13
0
Fork 0
mirror of https://github.com/yannh/kubeconform.git synced 2026-06-28 16:00:44 +00:00
kubeconform/openapi2jsonschema-go/openapi2jsonschema_test.go
Yann Hamon b83bf792b2
Openapi2jsonschema-go (#357)
* Go implementation of openapi2jsonschema
* Add go version of openapi2jsonschema to container
2026-06-04 21:17:41 +02:00

127 lines
3.4 KiB
Go

package main
import (
"encoding/json"
"reflect"
"testing"
)
// roundTrip serialises and parses through encoding/json so the resulting
// generic structure can be compared with reflect.DeepEqual regardless of how
// the producer chose to nest *OrderedMap vs map[string]any.
func roundTrip(t *testing.T, v any) any {
t.Helper()
b, err := json.Marshal(v)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var out any
if err := json.Unmarshal(b, &out); err != nil {
t.Fatalf("unmarshal: %v", err)
}
return out
}
func mapOf(kv ...any) *OrderedMap {
m := NewOrderedMap()
for i := 0; i < len(kv); i += 2 {
m.Set(kv[i].(string), kv[i+1])
}
return m
}
func TestAdditionalProperties(t *testing.T) {
cases := []struct {
name string
input *OrderedMap
expect string
}{
{
name: "object with properties gets additionalProperties:false",
input: mapOf("something", mapOf("properties", NewOrderedMap())),
expect: `{"something":{"properties":{},"additionalProperties":false}}`,
},
{
name: "object without properties is left alone",
input: mapOf("something", mapOf("somethingelse", NewOrderedMap())),
expect: `{"something":{"somethingelse":{}}}`,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := additionalProperties(tc.input, false)
b, err := json.Marshal(got)
if err != nil {
t.Fatalf("marshal: %v", err)
}
if string(b) != tc.expect {
t.Fatalf("got %s want %s", b, tc.expect)
}
})
}
}
func TestReplaceIntOrString(t *testing.T) {
cases := []struct {
name string
input *OrderedMap
expect string
}{
{
name: "int-or-string is expanded to oneOf",
input: mapOf("something", mapOf("format", "int-or-string")),
expect: `{"something":{"oneOf":[{"type":"string"},{"type":"integer"}]}}`,
},
{
name: "other formats are left alone",
input: mapOf("something", mapOf("format", "string")),
expect: `{"something":{"format":"string"}}`,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := replaceIntOrString(tc.input)
b, err := json.Marshal(got)
if err != nil {
t.Fatalf("marshal: %v", err)
}
if string(b) != tc.expect {
t.Fatalf("got %s want %s", b, tc.expect)
}
})
}
}
func TestFormatFilename(t *testing.T) {
got := formatFilename("{kind}_{version}", "Prometheus", "monitoring", "monitoring.coreos.com", "v1")
if got != "prometheus_v1.json" {
t.Fatalf("got %s", got)
}
got = formatFilename("{kind}-{group}-{version}", "Prometheus", "monitoring", "monitoring.coreos.com", "v1")
if got != "prometheus-monitoring-v1.json" {
t.Fatalf("got %s", got)
}
}
// TestAdditionalProperties_PreservesUnrelatedKeys asserts the recursive walk
// does not lose sibling keys when injecting additionalProperties.
func TestAdditionalProperties_PreservesUnrelatedKeys(t *testing.T) {
input := mapOf(
"properties", mapOf("name", mapOf("type", "string")),
"required", []any{"name"},
"type", "object",
)
got := additionalProperties(input, false)
parsed := roundTrip(t, got)
// `additionalProperties: false` is only added at the level that has a
// "properties" key — the nested {type: string} does not.
want := map[string]any{
"properties": map[string]any{"name": map[string]any{"type": "string"}},
"required": []any{"name"},
"type": "object",
"additionalProperties": false,
}
if !reflect.DeepEqual(parsed, want) {
t.Fatalf("got %v want %v", parsed, want)
}
}