feat: funciones puras assert_json_equal y assert_contains_all con tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 02:00:14 +02:00
parent a38ac74b86
commit 9d6b3551a7
6 changed files with 288 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
package core
// AssertContainsAll verifica que haystack contiene todos los elementos de needles.
// Retorna (true, nil) si todos estan presentes, o (false, []string con los faltantes).
func AssertContainsAll(haystack, needles []string) (bool, []string) {
set := make(map[string]struct{}, len(haystack))
for _, s := range haystack {
set[s] = struct{}{}
}
var missing []string
for _, n := range needles {
if _, ok := set[n]; !ok {
missing = append(missing, n)
}
}
if len(missing) == 0 {
return true, nil
}
return false, missing
}
+53
View File
@@ -0,0 +1,53 @@
---
name: assert_contains_all
kind: function
lang: go
domain: core
version: "1.0.0"
purity: pure
signature: "func AssertContainsAll(haystack, needles []string) (bool, []string)"
description: "Verifica que el slice haystack contiene todos los elementos de needles. Retorna (true, nil) si todos estan presentes, o (false, missing) con los elementos faltantes."
tags: [testing, assert, slice, contains, pure]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
- name: haystack
desc: "slice de strings donde se busca; es el conjunto completo a inspeccionar"
- name: needles
desc: "slice de strings que deben estar todos presentes en haystack"
output: "(ok bool, missing []string) — ok=true y missing=nil si todos los needles estan en haystack; ok=false y missing con los elementos ausentes si falta alguno"
tested: true
tests:
- "todos presentes retorna true y nil"
- "un elemento faltante retorna false con el faltante"
- "multiples faltantes retornan todos en missing"
- "needles vacio retorna true"
- "haystack vacio con needles no vacios retorna todos como faltantes"
- "duplicados en haystack no afectan el resultado"
test_file_path: "functions/core/assert_contains_all_test.go"
file_path: "functions/core/assert_contains_all.go"
---
## Ejemplo
```go
ok, missing := AssertContainsAll(
[]string{"a", "b", "c", "d"},
[]string{"a", "c"},
)
// ok = true, missing = nil
ok2, missing2 := AssertContainsAll(
[]string{"a", "b"},
[]string{"a", "c", "d"},
)
// ok2 = false, missing2 = ["c", "d"]
```
## Notas
Funcion pura. Usa un mapa set para lookup O(1). El orden de los elementos en missing sigue el orden de needles. No elimina duplicados de needles.
@@ -0,0 +1,58 @@
package core
import (
"testing"
)
func TestAssertContainsAll(t *testing.T) {
t.Run("todos presentes retorna true y nil", func(t *testing.T) {
ok, missing := AssertContainsAll([]string{"a", "b", "c"}, []string{"a", "b"})
if !ok || missing != nil {
t.Errorf("got ok=%v missing=%v, want ok=true missing=nil", ok, missing)
}
})
t.Run("un elemento faltante retorna false con el faltante", func(t *testing.T) {
ok, missing := AssertContainsAll([]string{"a", "b"}, []string{"a", "c"})
if ok {
t.Error("expected ok=false")
}
if len(missing) != 1 || missing[0] != "c" {
t.Errorf("got missing=%v, want [c]", missing)
}
})
t.Run("multiples faltantes retornan todos en missing", func(t *testing.T) {
ok, missing := AssertContainsAll([]string{"a"}, []string{"b", "c", "d"})
if ok {
t.Error("expected ok=false")
}
if len(missing) != 3 {
t.Errorf("got missing=%v, want [b c d]", missing)
}
})
t.Run("needles vacio retorna true", func(t *testing.T) {
ok, missing := AssertContainsAll([]string{"a", "b"}, []string{})
if !ok || missing != nil {
t.Errorf("got ok=%v missing=%v, want ok=true missing=nil", ok, missing)
}
})
t.Run("haystack vacio con needles no vacios retorna todos como faltantes", func(t *testing.T) {
ok, missing := AssertContainsAll([]string{}, []string{"x", "y"})
if ok {
t.Error("expected ok=false")
}
if len(missing) != 2 {
t.Errorf("got missing=%v, want [x y]", missing)
}
})
t.Run("duplicados en haystack no afectan el resultado", func(t *testing.T) {
ok, missing := AssertContainsAll([]string{"a", "a", "b", "b"}, []string{"a", "b"})
if !ok || missing != nil {
t.Errorf("got ok=%v missing=%v, want ok=true missing=nil", ok, missing)
}
})
}
+30
View File
@@ -0,0 +1,30 @@
package core
import (
"encoding/json"
"fmt"
"reflect"
)
// AssertJSONEqual compara dos payloads JSON por igualdad semantica.
// Retorna (true, "") si son equivalentes, o (false, diff) con una descripcion
// legible de la diferencia. No es sensible al orden de las claves en objetos.
func AssertJSONEqual(expected, actual []byte) (bool, string) {
var expVal, actVal any
if err := json.Unmarshal(expected, &expVal); err != nil {
return false, fmt.Sprintf("expected JSON invalido: %v", err)
}
if err := json.Unmarshal(actual, &actVal); err != nil {
return false, fmt.Sprintf("actual JSON invalido: %v", err)
}
if reflect.DeepEqual(expVal, actVal) {
return true, ""
}
expPretty, _ := json.MarshalIndent(expVal, "", " ")
actPretty, _ := json.MarshalIndent(actVal, "", " ")
diff := fmt.Sprintf("expected:\n%s\n\nactual:\n%s", string(expPretty), string(actPretty))
return false, diff
}
+54
View File
@@ -0,0 +1,54 @@
---
name: assert_json_equal
kind: function
lang: go
domain: core
version: "1.0.0"
purity: pure
signature: "func AssertJSONEqual(expected, actual []byte) (bool, string)"
description: "Compara dos payloads JSON por igualdad semantica. Insensible al orden de claves. Retorna (true, \"\") si son equivalentes o (false, diff) con descripcion legible de la diferencia."
tags: [testing, json, assert, diff, pure]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: ["encoding/json", "fmt", "reflect"]
params:
- name: expected
desc: "payload JSON esperado como slice de bytes"
- name: actual
desc: "payload JSON real a comparar contra el esperado"
output: "(ok bool, diff string) — ok=true si son semanticamente iguales, diff con formato legible si difieren"
tested: true
tests:
- "json identico retorna true y diff vacio"
- "objetos con claves en distinto orden son iguales"
- "valores distintos retorna false con diff"
- "json invalido en expected retorna false con mensaje de error"
- "json invalido en actual retorna false con mensaje de error"
- "arrays con mismo contenido son iguales"
- "arrays con distinto orden son distintos"
test_file_path: "functions/core/assert_json_equal_test.go"
file_path: "functions/core/assert_json_equal.go"
---
## Ejemplo
```go
ok, diff := AssertJSONEqual(
[]byte(`{"a":1,"b":2}`),
[]byte(`{"b":2,"a":1}`),
)
// ok = true, diff = ""
ok2, diff2 := AssertJSONEqual(
[]byte(`{"a":1}`),
[]byte(`{"a":2}`),
)
// ok2 = false, diff2 = "expected:\n{...}\n\nactual:\n{...}"
```
## Notas
Funcion pura. Usa json.Unmarshal a `any` seguido de reflect.DeepEqual para comparacion semantica — no textual. El diff incluye ambos JSONs formateados con indentacion para facilitar la inspeccion manual.
+71
View File
@@ -0,0 +1,71 @@
package core
import (
"testing"
)
func TestAssertJSONEqual(t *testing.T) {
t.Run("json identico retorna true y diff vacio", func(t *testing.T) {
ok, diff := AssertJSONEqual([]byte(`{"a":1}`), []byte(`{"a":1}`))
if !ok || diff != "" {
t.Errorf("got ok=%v diff=%q, want ok=true diff=\"\"", ok, diff)
}
})
t.Run("objetos con claves en distinto orden son iguales", func(t *testing.T) {
ok, diff := AssertJSONEqual(
[]byte(`{"a":1,"b":2}`),
[]byte(`{"b":2,"a":1}`),
)
if !ok || diff != "" {
t.Errorf("got ok=%v diff=%q, want ok=true diff=\"\"", ok, diff)
}
})
t.Run("valores distintos retorna false con diff", func(t *testing.T) {
ok, diff := AssertJSONEqual([]byte(`{"a":1}`), []byte(`{"a":2}`))
if ok {
t.Error("expected ok=false for different values")
}
if diff == "" {
t.Error("expected non-empty diff")
}
})
t.Run("json invalido en expected retorna false con mensaje de error", func(t *testing.T) {
ok, diff := AssertJSONEqual([]byte(`{not json}`), []byte(`{"a":1}`))
if ok {
t.Error("expected ok=false for invalid expected JSON")
}
if diff == "" {
t.Error("expected error message in diff")
}
})
t.Run("json invalido en actual retorna false con mensaje de error", func(t *testing.T) {
ok, diff := AssertJSONEqual([]byte(`{"a":1}`), []byte(`{not json}`))
if ok {
t.Error("expected ok=false for invalid actual JSON")
}
if diff == "" {
t.Error("expected error message in diff")
}
})
t.Run("arrays con mismo contenido son iguales", func(t *testing.T) {
ok, diff := AssertJSONEqual([]byte(`[1,2,3]`), []byte(`[1,2,3]`))
if !ok || diff != "" {
t.Errorf("got ok=%v diff=%q, want ok=true diff=\"\"", ok, diff)
}
})
t.Run("arrays con distinto orden son distintos", func(t *testing.T) {
ok, diff := AssertJSONEqual([]byte(`[1,2,3]`), []byte(`[3,2,1]`))
if ok {
t.Error("expected ok=false for different array order")
}
if diff == "" {
t.Error("expected non-empty diff for different arrays")
}
})
}