internal/encoder/json/schema/schema_test.go

package schema_test

import (
	"errors"
	"strings"
	"testing"

	"mercemay.top/src/lambdalog/internal/encoder"
	"mercemay.top/src/lambdalog/internal/encoder/json/schema"
)

func newTestSchema(t *testing.T) *schema.Schema {
	t.Helper()
	s := schema.New()
	s.Register("user", schema.Spec{Type: schema.TypeString, Required: true})
	s.Register("count", schema.Spec{Type: schema.TypeInt})
	s.Register("ok", schema.Spec{Type: schema.TypeBool})
	return s
}

func TestValidate_Accepts(t *testing.T) {
	s := newTestSchema(t)
	rec := encoder.Record{
		Fields: []encoder.Field{
			{Key: "user", Value: "alice"},
			{Key: "count", Value: int64(3)},
			{Key: "ok", Value: true},
		},
	}
	if err := s.Validate(rec); err != nil {
		t.Fatalf("Validate: %v", err)
	}
}

func TestValidate_RejectsMissingRequired(t *testing.T) {
	s := newTestSchema(t)
	err := s.Validate(encoder.Record{})
	if err == nil {
		t.Fatal("expected error")
	}
	if !errors.Is(err, &schema.ValidationError{}) {
		t.Fatalf("not a ValidationError: %v", err)
	}
	if !strings.Contains(err.Error(), `missing required field "user"`) {
		t.Fatalf("unexpected message: %v", err)
	}
}

func TestValidate_RejectsTypeMismatch(t *testing.T) {
	s := newTestSchema(t)
	rec := encoder.Record{
		Fields: []encoder.Field{
			{Key: "user", Value: "x"},
			{Key: "count", Value: "not-an-int"},
		},
	}
	err := s.Validate(rec)
	if err == nil {
		t.Fatal("expected error")
	}
	if !strings.Contains(err.Error(), `field "count": expected int`) {
		t.Fatalf("unexpected message: %v", err)
	}
}

func TestValidate_RejectsUnknownField(t *testing.T) {
	s := newTestSchema(t)
	rec := encoder.Record{
		Fields: []encoder.Field{
			{Key: "user", Value: "x"},
			{Key: "mystery", Value: 1},
		},
	}
	err := s.Validate(rec)
	if err == nil || !strings.Contains(err.Error(), `unknown field "mystery"`) {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestValidationError_IsOpaqueSentinel(t *testing.T) {
	err := &schema.ValidationError{Problems: []string{"x"}}
	if !errors.Is(err, &schema.ValidationError{}) {
		t.Fatal("Is should match the zero value")
	}
}

func TestValidate_ProblemsAreSorted(t *testing.T) {
	s := schema.New()
	s.Register("a", schema.Spec{Type: schema.TypeInt, Required: true})
	s.Register("b", schema.Spec{Type: schema.TypeInt, Required: true})
	err := s.Validate(encoder.Record{})
	if err == nil {
		t.Fatal("expected error")
	}
	ve, ok := err.(*schema.ValidationError)
	if !ok {
		t.Fatalf("wrong type: %T", err)
	}
	if len(ve.Problems) != 2 {
		t.Fatalf("got %d problems, want 2", len(ve.Problems))
	}
	if ve.Problems[0] > ve.Problems[1] {
		t.Fatalf("not sorted: %v", ve.Problems)
	}
}