-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathroundtrip_test.go
More file actions
82 lines (76 loc) · 2.48 KB
/
Copy pathroundtrip_test.go
File metadata and controls
82 lines (76 loc) · 2.48 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
package flashduty
import (
"encoding/json"
"os"
"strings"
"testing"
)
// TestSpecExamplesRoundTrip decodes every endpoint's canonical response example
// (vendored in the OpenAPI spec) into its generated Go data type via the same
// envelope→data path do() uses. A decode error means the generated type does not
// match the documented payload shape (e.g. a scalar typed wrong). This validates
// the whole generated type layer against real-shaped data without a live API.
func TestSpecExamplesRoundTrip(t *testing.T) {
raw, err := os.ReadFile("openapi/openapi.en.json")
if err != nil {
t.Fatalf("read spec: %v", err)
}
// Parse leniently: path-item values are method->operation, but a path item
// may also carry a sibling "parameters" array — so read operations as raw.
var spec struct {
Paths map[string]map[string]json.RawMessage `json:"paths"`
}
if err := json.Unmarshal(raw, &spec); err != nil {
t.Fatalf("parse spec: %v", err)
}
tested := 0
for path, methods := range spec.Paths {
for method, opRaw := range methods {
m := strings.ToUpper(method)
if m != "GET" && m != "POST" {
continue // skip "parameters" and other non-method keys
}
dec, ok := exampleDataDecoders[m+" "+path]
if !ok {
continue // endpoint returns no typed data
}
var op struct {
Responses map[string]struct {
Content map[string]struct {
Example json.RawMessage `json:"example"`
} `json:"content"`
} `json:"responses"`
}
if err := json.Unmarshal(opRaw, &op); err != nil {
t.Errorf("%s %s: parse operation: %v", m, path, err)
continue
}
example := op.Responses["200"].Content["application/json"].Example
if len(example) == 0 {
continue
}
// Examples are full envelopes; decode data like do() does.
var env struct {
Data json.RawMessage `json:"data"`
}
if err := json.Unmarshal(example, &env); err != nil {
t.Errorf("%s %s: malformed example envelope: %v", m, path, err)
continue
}
if len(env.Data) == 0 {
continue
}
if err := dec(env.Data); err != nil {
t.Errorf("%s %s: example data does not fit generated type: %v", m, path, err)
continue
}
tested++
}
}
// Guard against the test silently exercising nothing (e.g. spec path change).
// ~135 endpoints return typed data with an example; require a healthy floor.
if tested < 120 {
t.Fatalf("expected to round-trip most endpoints, only exercised %d", tested)
}
t.Logf("round-tripped %d spec examples into generated types", tested)
}