package api // schemagen_test.go generates and golden-compares JSON Schema files for every // /api/X response type. // // Normal run (CI): go test ./internal/web/api/... — asserts schemas match committed files. // Regenerate: go test -run TestGenerateSchemas -update ./internal/web/api/... import ( "encoding/json" "flag" "os" "path/filepath" "testing" "github.com/invopop/jsonschema" ) var updateFlag = flag.Bool("update", false, "overwrite api-schema fixture files with freshly generated schemas") // JSONSchema makes Expected self-describing for the reflector at test time. // The method is in a test file and is not compiled into production binaries. // It emits oneOf [integer, "?"] to match the custom MarshalJSON behaviour. func (Expected) JSONSchema() *jsonschema.Schema { return &jsonschema.Schema{ OneOf: []*jsonschema.Schema{ {Type: "integer"}, {Enum: []any{"?"}}, }, } } func TestGenerateSchemas(t *testing.T) { r := &jsonschema.Reflector{ AllowAdditionalProperties: false, } cases := []struct { name string val any }{ {"adults", &AdultsResponse{}}, {"juniors", &JuniorsResponse{}}, {"payments", &PaymentsResponse{}}, {"version", &VersionResponse{}}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { schema := r.Reflect(tc.val) got, err := json.MarshalIndent(schema, "", " ") if err != nil { t.Fatalf("marshal schema: %v", err) } got = append(got, '\n') // Path: go/internal/web/api/ → ../../.. → go/ → tests/fixtures/api-schema/ path := filepath.Join("..", "..", "..", "tests", "fixtures", "api-schema", tc.name+".schema.json") if *updateFlag { if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { t.Fatalf("mkdir: %v", err) } if err := os.WriteFile(path, got, 0o644); err != nil { t.Fatalf("write schema: %v", err) } t.Logf("wrote %s", path) return } want, err := os.ReadFile(path) if err != nil { t.Fatalf("read fixture %s: %v (re-run with -update to generate)", path, err) } if string(got) != string(want) { t.Errorf("schema mismatch for %s; re-run with -update to regenerate", tc.name) } }) } }