axonhub-chat / internal /pkg /xjson /schema_test.go
llzai's picture
Upload 1793 files
9853396 verified
package xjson
import (
"encoding/json"
"strings"
"testing"
"github.com/google/jsonschema-go/jsonschema"
"github.com/stretchr/testify/require"
)
func TestCleanSchema_SimpleFields(t *testing.T) {
schema := `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schema",
"title": "Test Schema",
"description": "A test schema",
"type": "object",
"additionalProperties": {"type": "string"},
"properties": {
"name": {"type": "string"}
}
}`
result, err := CleanSchema([]byte(schema), "$schema", "$id", "additionalProperties")
require.NoError(t, err)
var cleaned map[string]any
require.NoError(t, json.Unmarshal(result, &cleaned))
require.NotContains(t, cleaned, "$schema")
require.NotContains(t, cleaned, "$id")
require.NotContains(t, cleaned, "additionalProperties")
require.Equal(t, "Test Schema", cleaned["title"])
require.Equal(t, "A test schema", cleaned["description"])
require.Contains(t, cleaned, "properties")
require.Contains(t, cleaned, "type")
}
func TestCleanSchema_RecursiveSubSchemas(t *testing.T) {
schema := `{
"type": "object",
"properties": {
"user": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "string"},
"properties": {
"name": {
"type": "string",
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
}
}
},
"items": {
"type": "array",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "number"}
}
}`
result, err := CleanSchema([]byte(schema), "$schema", "additionalProperties")
require.NoError(t, err)
var cleaned map[string]any
require.NoError(t, json.Unmarshal(result, &cleaned))
// Check main schema doesn't have these fields
require.NotContains(t, cleaned, "$schema")
require.NotContains(t, cleaned, "additionalProperties")
// Check nested user schema
if userProp, ok := cleaned["properties"].(map[string]any)["user"].(map[string]any); ok {
require.NotContains(t, userProp, "$schema")
require.NotContains(t, userProp, "additionalProperties")
// Check deeply nested name schema
if nameProp, ok := userProp["properties"].(map[string]any)["name"].(map[string]any); ok {
require.NotContains(t, nameProp, "$schema")
}
}
// Check items schema
if items, ok := cleaned["items"].(map[string]any); ok {
require.NotContains(t, items, "$schema")
require.NotContains(t, items, "additionalProperties")
}
}
func TestCleanSchema_ArraySchemas(t *testing.T) {
schema := `{
"type": "object",
"allOf": [
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"additionalProperties": {"type": "string"}
},
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "number",
"additionalProperties": {"type": "number"}
}
],
"anyOf": [
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "boolean"}
}
],
"oneOf": [
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "null"}
}
]
}`
result, err := CleanSchema([]byte(schema), "$schema", "additionalProperties")
require.NoError(t, err)
var cleaned map[string]any
require.NoError(t, json.Unmarshal(result, &cleaned))
// Check allOf schemas
if allOf, ok := cleaned["allOf"].([]any); ok {
for _, schema := range allOf {
if schemaMap, ok := schema.(map[string]any); ok {
require.NotContains(t, schemaMap, "$schema")
require.NotContains(t, schemaMap, "additionalProperties")
}
}
}
// Check anyOf schemas
if anyOf, ok := cleaned["anyOf"].([]any); ok {
for _, schema := range anyOf {
if schemaMap, ok := schema.(map[string]any); ok {
require.NotContains(t, schemaMap, "$schema")
require.NotContains(t, schemaMap, "additionalProperties")
}
}
}
// Check oneOf schemas
if oneOf, ok := cleaned["oneOf"].([]any); ok {
for _, schema := range oneOf {
if schemaMap, ok := schema.(map[string]any); ok {
require.NotContains(t, schemaMap, "$schema")
require.NotContains(t, schemaMap, "additionalProperties")
}
}
}
}
func TestCleanSchema_MapSchemas(t *testing.T) {
schema := `{
"type": "object",
"definitions": {
"user": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": {"type": "string"}
},
"address": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"street": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
}
}
}
},
"properties": {
"data": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "object"}
}
}
}`
result, err := CleanSchema([]byte(schema), "$schema", "additionalProperties")
require.NoError(t, err)
var cleaned map[string]any
require.NoError(t, json.Unmarshal(result, &cleaned))
// Check definitions
if definitions, ok := cleaned["definitions"].(map[string]any); ok {
for _, def := range definitions {
if defMap, ok := def.(map[string]any); ok {
require.NotContains(t, defMap, "$schema")
require.NotContains(t, defMap, "additionalProperties")
// Check nested properties in address definition
if props, ok := defMap["properties"].(map[string]any); ok {
for _, prop := range props {
if propMap, ok := prop.(map[string]any); ok {
require.NotContains(t, propMap, "$schema")
}
}
}
}
}
}
// Check properties
if properties, ok := cleaned["properties"].(map[string]any); ok {
for _, prop := range properties {
if propMap, ok := prop.(map[string]any); ok {
require.NotContains(t, propMap, "$schema")
require.NotContains(t, propMap, "additionalProperties")
}
}
}
}
func TestCleanSchema_ConditionalSchemas(t *testing.T) {
schema := `{
"type": "object",
"if": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "string"},
"properties": {
"type": {"type": "string"}
}
},
"then": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "number"},
"properties": {
"value": {"type": "number"}
}
},
"else": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "boolean"},
"properties": {
"flag": {"type": "boolean"}
}
},
"not": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": {"type": "null"},
"properties": {
"null_field": {"type": "null"}
}
}
}`
result, err := CleanSchema([]byte(schema), "$schema", "additionalProperties")
require.NoError(t, err)
var cleaned map[string]any
require.NoError(t, json.Unmarshal(result, &cleaned))
// Check conditional schemas
for _, key := range []string{"if", "then", "else", "not"} {
if schema, ok := cleaned[key].(map[string]any); ok {
require.NotContains(t, schema, "$schema")
require.NotContains(t, schema, "additionalProperties")
// Check nested properties
if props, ok := schema["properties"].(map[string]any); ok {
for _, prop := range props {
if propMap, ok := prop.(map[string]any); ok {
// These nested properties shouldn't have $schema or additionalProperties
// since they weren't in the original schema
require.NotContains(t, propMap, "$schema")
require.NotContains(t, propMap, "additionalProperties")
}
}
}
}
}
}
func TestCleanSchema_ArrayItemSchemas(t *testing.T) {
schema := `{
"type": "object",
"prefixItems": [
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"additionalProperties": {"type": "string"}
},
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "number",
"additionalProperties": {"type": "number"}
}
],
"items": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "boolean",
"additionalProperties": {"type": "boolean"}
},
"contains": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "null",
"additionalProperties": {"type": "null"}
}
}`
result, err := CleanSchema([]byte(schema), "$schema", "additionalProperties")
require.NoError(t, err)
var cleaned map[string]any
require.NoError(t, json.Unmarshal(result, &cleaned))
// Check prefixItems
if prefixItems, ok := cleaned["prefixItems"].([]any); ok {
for _, item := range prefixItems {
if itemMap, ok := item.(map[string]any); ok {
require.NotContains(t, itemMap, "$schema")
require.NotContains(t, itemMap, "additionalProperties")
}
}
}
// Check items
if items, ok := cleaned["items"].(map[string]any); ok {
require.NotContains(t, items, "$schema")
require.NotContains(t, items, "additionalProperties")
}
// Check contains
if contains, ok := cleaned["contains"].(map[string]any); ok {
require.NotContains(t, contains, "$schema")
require.NotContains(t, contains, "additionalProperties")
}
}
func TestCleanSchema_SpecialFields(t *testing.T) {
schema := `{
"$id": "https://example.com/schema",
"$ref": "#/definitions/user",
"$comment": "A comment",
"$anchor": "user",
"$dynamicAnchor": "dynamic-user",
"$dynamicRef": "#/dynamic-user",
"$vocabulary": {"https://json-schema.org/draft/2020-12/vocab/core": true},
"definitions": {
"user": {
"$id": "https://example.com/user",
"$ref": "#/definitions/name",
"$comment": "User definition"
}
}
}`
result, err := CleanSchema([]byte(schema), "$id", "$ref", "$comment", "$anchor", "$dynamicAnchor", "$dynamicRef", "$vocabulary")
require.NoError(t, err)
var cleaned map[string]any
require.NoError(t, json.Unmarshal(result, &cleaned))
require.NotContains(t, cleaned, "$id")
require.NotContains(t, cleaned, "$ref")
require.NotContains(t, cleaned, "$comment")
require.NotContains(t, cleaned, "$anchor")
require.NotContains(t, cleaned, "$dynamicAnchor")
require.NotContains(t, cleaned, "$dynamicRef")
require.NotContains(t, cleaned, "$vocabulary")
// Check nested definitions
if definitions, ok := cleaned["definitions"].(map[string]any); ok {
if userDef, ok := definitions["user"].(map[string]any); ok {
require.NotContains(t, userDef, "$id")
require.NotContains(t, userDef, "$ref")
require.NotContains(t, userDef, "$comment")
}
}
}
func TestCleanSchema_InvalidJSON(t *testing.T) {
invalidSchema := `{"invalid": json}`
_, err := CleanSchema([]byte(invalidSchema), "$schema")
require.Error(t, err)
require.Contains(t, err.Error(), "invalid character")
}
func TestCleanSchema_NoFieldsToClear(t *testing.T) {
schema := `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": {"type": "string"}
}`
result, err := CleanSchema([]byte(schema))
require.NoError(t, err)
var cleaned map[string]any
require.NoError(t, json.Unmarshal(result, &cleaned))
// Should contain all original fields
require.Contains(t, cleaned, "$schema")
require.Contains(t, cleaned, "additionalProperties")
require.Equal(t, "https://json-schema.org/draft/2020-12/schema", cleaned["$schema"])
}
func TestCleanSchema_NilSchemaInput(t *testing.T) {
_, err := CleanSchema(nil, "$schema")
require.Error(t, err)
}
func TestTransform_TypeToLowercase(t *testing.T) {
schema := `{
"type": "OBJECT",
"properties": {
"name": {"type": "STRING"},
"age": {"type": "INTEGER"},
"active": {"type": "BOOLEAN"}
},
"items": {
"type": "ARRAY",
"items": {"type": "NUMBER"}
},
"allOf": [
{"type": "STRING"},
{"type": "NULL"}
],
"definitions": {
"user": {"type": "OBJECT"}
}
}`
result, err := Transform([]byte(schema), func(s *jsonschema.Schema) {
s.Type = strings.ToLower(s.Type)
})
require.NoError(t, err)
var transformed map[string]any
require.NoError(t, json.Unmarshal(result, &transformed))
// Check main schema type
require.Equal(t, "object", transformed["type"])
// Check properties
props := transformed["properties"].(map[string]any)
require.Equal(t, "string", props["name"].(map[string]any)["type"])
require.Equal(t, "integer", props["age"].(map[string]any)["type"])
require.Equal(t, "boolean", props["active"].(map[string]any)["type"])
// Check items
items := transformed["items"].(map[string]any)
require.Equal(t, "array", items["type"])
require.Equal(t, "number", items["items"].(map[string]any)["type"])
// Check allOf
allOf := transformed["allOf"].([]any)
require.Equal(t, "string", allOf[0].(map[string]any)["type"])
require.Equal(t, "null", allOf[1].(map[string]any)["type"])
// Check definitions
defs := transformed["definitions"].(map[string]any)
require.Equal(t, "object", defs["user"].(map[string]any)["type"])
}
func TestTransform_InvalidJSON(t *testing.T) {
invalidSchema := `{"invalid": json}`
_, err := Transform([]byte(invalidSchema), func(s *jsonschema.Schema) {})
require.Error(t, err)
require.Contains(t, err.Error(), "invalid character")
}
func TestTransform_NilInput(t *testing.T) {
_, err := Transform(nil, func(s *jsonschema.Schema) {})
require.Error(t, err)
}
func TestTransform_AddDescription(t *testing.T) {
schema := `{
"type": "object",
"properties": {
"name": {"type": "string"},
"address": {
"type": "object",
"properties": {
"city": {"type": "string"}
}
}
}
}`
result, err := Transform([]byte(schema), func(s *jsonschema.Schema) {
if s.Description == "" {
s.Description = "Auto-generated description"
}
})
require.NoError(t, err)
var transformed map[string]any
require.NoError(t, json.Unmarshal(result, &transformed))
// Check main schema
require.Equal(t, "Auto-generated description", transformed["description"])
// Check nested properties
props := transformed["properties"].(map[string]any)
require.Equal(t, "Auto-generated description", props["name"].(map[string]any)["description"])
require.Equal(t, "Auto-generated description", props["address"].(map[string]any)["description"])
// Check deeply nested
addressProps := props["address"].(map[string]any)["properties"].(map[string]any)
require.Equal(t, "Auto-generated description", addressProps["city"].(map[string]any)["description"])
}