Files
fn_registry/registry/validate_test.go
egutierrez 08d141ea1b test: 15 tests para reglas de integridad
Cubre cada regla de integridad:
- Funciones validas pasan
- Pipeline puro rechazado, pipeline sin uses_functions rechazado
- Pura con returns_optional o error_type rechazada
- Impura sin error_type rechazada
- Coherencia tested/tests/test_file_path
- Referencias huerfanas detectadas (4 tipos de referencia)
- Component: framework, returns, has_state+purity
- file_path absoluta rechazada
- Types: algebraic invalido, auto-referencia, refs huerfanas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 02:13:41 +01:00

259 lines
7.3 KiB
Go

package registry
import (
"strings"
"testing"
)
func boolPtr(b bool) *bool { return &b }
func knownFns(ids ...string) map[string]bool {
m := make(map[string]bool)
for _, id := range ids {
m[id] = true
}
return m
}
func knownTps(ids ...string) map[string]bool {
return knownFns(ids...)
}
func TestValidateFunction_Valid(t *testing.T) {
f := &Function{
ID: "filter_slice_go_core", Name: "filter_slice", Kind: KindFunction,
Lang: "go", Domain: "core", Purity: PurityPure,
Description: "Filtra un slice", Version: "1.0.0",
}
if err := ValidateFunction(f, knownFns(), knownTps()); err != nil {
t.Errorf("expected valid, got: %v", err)
}
}
func TestValidateFunction_PipelineMustBeImpure(t *testing.T) {
f := &Function{
ID: "p_go_core", Name: "p", Kind: KindPipeline,
Lang: "go", Domain: "core", Purity: PurityPure,
Description: "bad pipeline", Version: "1.0.0",
UsesFunctions: []string{"filter_slice_go_core"},
}
err := ValidateFunction(f, knownFns("filter_slice_go_core"), knownTps())
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "pipeline must be impure") {
t.Errorf("unexpected error: %v", err)
}
}
func TestValidateFunction_PipelineNeedsUsesFunctions(t *testing.T) {
f := &Function{
ID: "p_go_core", Name: "p", Kind: KindPipeline,
Lang: "go", Domain: "core", Purity: PurityImpure,
Description: "bad pipeline", Version: "1.0.0",
ErrorType: "error_go_core",
}
err := ValidateFunction(f, knownFns(), knownTps("error_go_core"))
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "uses_functions cannot be empty") {
t.Errorf("unexpected error: %v", err)
}
}
func TestValidateFunction_PureNoReturnsOptional(t *testing.T) {
f := &Function{
ID: "f_go_core", Name: "f", Kind: KindFunction,
Lang: "go", Domain: "core", Purity: PurityPure,
Description: "bad", Version: "1.0.0",
ReturnsOptional: true,
}
err := ValidateFunction(f, knownFns(), knownTps())
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "returns_optional") {
t.Errorf("unexpected error: %v", err)
}
}
func TestValidateFunction_PureNoErrorType(t *testing.T) {
f := &Function{
ID: "f_go_core", Name: "f", Kind: KindFunction,
Lang: "go", Domain: "core", Purity: PurityPure,
Description: "bad", Version: "1.0.0",
ErrorType: "error_go_core",
}
err := ValidateFunction(f, knownFns(), knownTps("error_go_core"))
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "pure function cannot have error_type") {
t.Errorf("unexpected error: %v", err)
}
}
func TestValidateFunction_ImpureNeedsErrorType(t *testing.T) {
f := &Function{
ID: "f_go_io", Name: "f", Kind: KindFunction,
Lang: "go", Domain: "io", Purity: PurityImpure,
Description: "bad", Version: "1.0.0",
}
err := ValidateFunction(f, knownFns(), knownTps())
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "impure function must declare error_type") {
t.Errorf("unexpected error: %v", err)
}
}
func TestValidateFunction_TestedNeedsTestFile(t *testing.T) {
f := &Function{
ID: "f_go_core", Name: "f", Kind: KindFunction,
Lang: "go", Domain: "core", Purity: PurityPure,
Description: "test", Version: "1.0.0",
Tested: true,
}
err := ValidateFunction(f, knownFns(), knownTps())
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "test_file_path") {
t.Errorf("unexpected error: %v", err)
}
}
func TestValidateFunction_NotTestedNoTests(t *testing.T) {
f := &Function{
ID: "f_go_core", Name: "f", Kind: KindFunction,
Lang: "go", Domain: "core", Purity: PurityPure,
Description: "test", Version: "1.0.0",
Tested: false, Tests: []string{"ghost test"}, TestFilePath: "test.go",
}
err := ValidateFunction(f, knownFns(), knownTps())
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "tested: false but tests is not empty") {
t.Errorf("unexpected error: %v", err)
}
}
func TestValidateFunction_OrphanRefs(t *testing.T) {
f := &Function{
ID: "p_go_core", Name: "p", Kind: KindPipeline,
Lang: "go", Domain: "core", Purity: PurityImpure,
Description: "pipeline", Version: "1.0.0",
UsesFunctions: []string{"nonexistent_go_core"},
UsesTypes: []string{"ghost_go_core"},
Returns: []string{"phantom_go_core"},
ErrorType: "missing_go_core",
}
err := ValidateFunction(f, knownFns(), knownTps())
if err == nil {
t.Fatal("expected error")
}
if len(err.Errors) < 4 {
t.Errorf("expected at least 4 errors, got %d: %v", len(err.Errors), err)
}
}
func TestValidateFunction_ComponentRules(t *testing.T) {
f := &Function{
ID: "dt_typescript_core", Name: "DataTable", Kind: KindComponent,
Lang: "typescript", Domain: "core", Purity: PurityImpure,
Description: "table", Version: "1.0.0",
HasState: boolPtr(true), Framework: "react",
}
if err := ValidateFunction(f, knownFns(), knownTps()); err != nil {
t.Errorf("expected valid, got: %v", err)
}
// Missing framework
f2 := *f
f2.Framework = ""
if err := ValidateFunction(&f2, knownFns(), knownTps()); err == nil {
t.Error("expected error for missing framework")
}
// Returns should be empty
f3 := *f
f3.Returns = []string{"some_go_core"}
if err := ValidateFunction(&f3, knownFns(), knownTps("some_go_core")); err == nil {
t.Error("expected error for non-empty returns on component")
}
// has_state: true but pure
f4 := *f
f4.Purity = PurityPure
if err := ValidateFunction(&f4, knownFns(), knownTps()); err == nil {
t.Error("expected error for stateful pure component")
}
}
func TestValidateFunction_AbsoluteFilePath(t *testing.T) {
f := &Function{
ID: "f_go_core", Name: "f", Kind: KindFunction,
Lang: "go", Domain: "core", Purity: PurityPure,
Description: "test", Version: "1.0.0",
FilePath: "/absolute/path.go",
}
err := ValidateFunction(f, knownFns(), knownTps())
if err == nil {
t.Fatal("expected error for absolute file_path")
}
}
func TestValidateType_Valid(t *testing.T) {
typ := &Type{
ID: "ohlcv_go_finance", Name: "ohlcv", Lang: "go", Domain: "finance",
Algebraic: AlgebraicProduct, Description: "candle", Version: "1.0.0",
}
if err := ValidateType(typ, knownTps("ohlcv_go_finance")); err != nil {
t.Errorf("expected valid, got: %v", err)
}
}
func TestValidateType_BadAlgebraic(t *testing.T) {
typ := &Type{
ID: "t_go_core", Name: "t", Lang: "go", Domain: "core",
Algebraic: "wrong", Description: "bad", Version: "1.0.0",
}
err := ValidateType(typ, knownTps("t_go_core"))
if err == nil {
t.Fatal("expected error")
}
}
func TestValidateType_SelfReference(t *testing.T) {
typ := &Type{
ID: "t_go_core", Name: "t", Lang: "go", Domain: "core",
Algebraic: AlgebraicProduct, Description: "self ref", Version: "1.0.0",
UsesTypes: []string{"t_go_core"},
}
err := ValidateType(typ, knownTps("t_go_core"))
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "cannot reference itself") {
t.Errorf("unexpected error: %v", err)
}
}
func TestValidateType_OrphanRef(t *testing.T) {
typ := &Type{
ID: "t_go_core", Name: "t", Lang: "go", Domain: "core",
Algebraic: AlgebraicProduct, Description: "orphan ref", Version: "1.0.0",
UsesTypes: []string{"nonexistent_go_core"},
}
err := ValidateType(typ, knownTps("t_go_core"))
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "unknown type") {
t.Errorf("unexpected error: %v", err)
}
}