-
-
Notifications
You must be signed in to change notification settings - Fork 289
Expand file tree
/
Copy pathregistry_test.go
More file actions
156 lines (128 loc) · 4.7 KB
/
Copy pathregistry_test.go
File metadata and controls
156 lines (128 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package huma
import (
"net/url"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
type Output[T any] struct{}
type Embedded[P any] struct{}
type EmbeddedTwo[P, V any] struct{}
type S struct{}
type ü struct{}
type MP4 struct{}
func TestDefaultSchemaNamer(t *testing.T) {
type Renamed Output[*[]Embedded[time.Time]]
for _, example := range []struct {
typ any
name string
hint string
}{
{int(0), "Int", ""},
{int64(0), "Int64", ""},
{S{}, "S", ""},
{time.Time{}, "Time", ""},
{Output[int]{}, "OutputInt", ""},
{Output[*int]{}, "OutputInt", ""},
{Output[[]int]{}, "OutputListInt", ""},
{Output[[]*int]{}, "OutputListInt", ""},
{Output[[][]int]{}, "OutputListListInt", ""},
{Output[map[string]int]{}, "OutputMapStringInt", ""},
{Output[map[string][]*int]{}, "OutputMapStringListInt", ""},
{Output[S]{}, "OutputS", ""},
{Output[ü]{}, "OutputÜ", ""},
{Output[MP4]{}, "OutputMP4", ""},
{Output[Embedded[*time.Time]]{}, "OutputEmbeddedTime", ""},
{Output[*[]Embedded[time.Time]]{}, "OutputListEmbeddedTime", ""},
{Output[EmbeddedTwo[[]time.Time, **url.URL]]{}, "OutputEmbeddedTwoListTimeURL", ""},
{Renamed{}, "Renamed", ""},
{struct{}{}, "SomeGenericThing", "Some[pkg.Generic]Thing"},
{struct{}{}, "Type1Type2Type3", "pkg1.Type1[path/to/pkg2.Type2]pkg3.Type3"},
} {
t.Run(example.name, func(t *testing.T) {
name := DefaultSchemaNamer(reflect.TypeOf(example.typ), example.hint)
assert.Equal(t, example.name, name)
})
}
}
func TestSchemaAlias(t *testing.T) {
type StringContainer struct {
Value string
}
type StructWithStringContainer struct {
Name StringContainer `json:"name,omitzero"`
}
type StructWithString struct {
Name string `json:"name,omitempty"`
}
registry := NewMapRegistry("#/components/schemas", DefaultSchemaNamer)
registry.RegisterTypeAlias(reflect.TypeFor[StringContainer](), reflect.TypeFor[string]())
schemaWithContainer := registry.Schema(reflect.TypeFor[StructWithStringContainer](), false, "")
schemaWithString := registry.Schema(reflect.TypeFor[StructWithString](), false, "")
assert.Equal(t, schemaWithString, schemaWithContainer)
}
func TestAllowAdditionalPropertiesByDefault(t *testing.T) {
type MyStruct struct {
Name string `json:"name"`
}
t.Run("DefaultIsFalse", func(t *testing.T) {
r := NewMapRegistry("/schemas", DefaultSchemaNamer)
// Confirm default is false.
assert.False(t, getConfig[registryConfig](r).AllowAdditionalPropertiesByDefault)
s := r.Schema(reflect.TypeFor[MyStruct](), false, "")
assert.Equal(t, false, s.AdditionalProperties)
})
t.Run("OverrideViaRegistryConfig", func(t *testing.T) {
r := NewMapRegistry("/schemas", DefaultSchemaNamer).(*mapRegistry)
r.config.AllowAdditionalPropertiesByDefault = true
s := r.Schema(reflect.TypeFor[MyStruct](), false, "")
assert.Equal(t, true, s.AdditionalProperties)
})
}
func TestInlineTypeDisambiguation(t *testing.T) {
// Distinct inline (unnamed) structs share the same hint-derived name and
// must be disambiguated rather than panic. See issue reproduced in #893.
r := NewMapRegistry("#/components/schemas/", DefaultSchemaNamer)
first := r.Schema(reflect.TypeOf(struct {
Field1 string `json:"field1"`
}{}), true, "Request")
second := r.Schema(reflect.TypeOf(struct {
Field2 string `json:"field2"`
}{}), true, "Request")
assert.Equal(t, "#/components/schemas/Request", first.Ref)
assert.Equal(t, "#/components/schemas/Request1", second.Ref)
assert.Len(t, r.Map(), 2)
}
func TestInlineTypeDeduplication(t *testing.T) {
// Identical inline structs are the same reflect.Type and must still
// deduplicate to a single schema.
r := NewMapRegistry("#/components/schemas/", DefaultSchemaNamer)
first := r.Schema(reflect.TypeOf(struct {
Field string `json:"field"`
}{}), true, "Request")
second := r.Schema(reflect.TypeOf(struct {
Field string `json:"field"`
}{}), true, "Request")
assert.Equal(t, "#/components/schemas/Request", first.Ref)
assert.Equal(t, first.Ref, second.Ref)
assert.Len(t, r.Map(), 1)
}
func TestDuplicateNamedTypePanics(t *testing.T) {
// Genuinely different named types that collide on a name remain a hard
// error, directing the caller to a custom namer.
r := NewMapRegistry("#/components/schemas/", func(reflect.Type, string) string {
return "Collision"
})
r.Schema(reflect.TypeFor[S](), true, "")
assert.Panics(t, func() {
r.Schema(reflect.TypeFor[Output[int]](), true, "")
})
}
func TestRegistryConfigValue(t *testing.T) {
r := NewMapRegistry("/schemas", DefaultSchemaNamer)
cfg := getConfig[registryConfig](r)
cfg.AllowAdditionalPropertiesByDefault = true
// Config() returns a copy, so modifying it shouldn't affect the registry.
assert.False(t, getConfig[registryConfig](r).AllowAdditionalPropertiesByDefault)
}