all repos

json2go @ fc9c7dff8393608d5d61f0e8f5274ad0904c93c4

convert json to go type annotations

json2go/json2go_test.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
refactor: use an actual parser instead of reflection..., 11 days ago
1
package json2go
2
3
import (
4
	"errors"
5
	"fmt"
6
	"reflect"
7
	"strings"
8
	"testing"
9
)
10
11
func field(indentLvl int, name, type_ string, json_ ...string) string {
12
	indent := strings.Repeat("\t", indentLvl)
13
	if strings.Contains(type_, "struct") {
14
		return fmt.Sprintf("\n%s%s %s", indent, name, type_)
15
	}
16
17
	tag := strings.ToLower(name)
18
	if len(json_) == 1 {
19
		tag = json_[0]
20
	}
21
	return fmt.Sprintf("\n%s%s %s `json:\"%s\"`", indent, name, type_, tag)
22
}
23
24
func TestTransform(t *testing.T) {
25
	tests := map[string]struct {
26
		input      string
27
		check      func(t *testing.T, result string)
28
		structName string
29
		err        error
30
	}{
31
		"simple object": {
32
			input: `{"name": "Olex", "active": true, "age": 420}`,
33
			check: func(t *testing.T, result string) {
34
				if !strings.Contains(result, "type Out struct") {
35
					t.Errorf("missing Out struct")
36
				}
37
				if !strings.Contains(result, "Name string `json:\"name\"`") {
38
					t.Errorf("missing Name field")
39
				}
40
				if !strings.Contains(result, "Active bool `json:\"active\"`") {
41
					t.Errorf("missing Active field")
42
				}
43
				if !strings.Contains(result, "Age int `json:\"age\"`") {
44
					t.Errorf("missing Age field")
45
				}
46
			},
47
		},
48
		"invalid json": {
49
			err:   ErrInvalidJSON,
50
			input: `{"invalid":json}`,
51
		},
52
		"invalid struct name, starts with number": {
53
			err:        ErrInvalidStructName,
54
			structName: "1Name",
55
		},
56
		"invalid struct name, has space": {
57
			err:        ErrInvalidStructName,
58
			structName: "Name Name2",
59
		},
60
		"invalid struct name, has non letter/number": {
61
			err:        ErrInvalidStructName,
62
			structName: "Name$",
63
		},
64
		"snake_case to CamelCase": {
65
			input: `{"first_name": "Bob", "last_name": "Bobberson"}`,
66
			check: func(t *testing.T, result string) {
67
				if !strings.Contains(result, "FirstName string `json:\"first_name\"`") {
68
					t.Errorf("missing FirstName field")
69
				}
70
				if !strings.Contains(result, "LastName string `json:\"last_name\"`") {
71
					t.Errorf("missing LastName field")
72
				}
73
			},
74
		},
75
		"nested object and array": {
76
			input: `{"user": {"name": "Alice", "score": 95.5}, "tags": ["go", "json"]}`,
77
			check: func(t *testing.T, result string) {
78
				if !strings.Contains(result, "type Out struct") {
79
					t.Errorf("missing Out struct")
80
				}
81
				if !strings.Contains(result, "Tags []string") {
82
					t.Errorf("missing Tags field")
83
				}
84
				if !strings.Contains(result, "User OutUser") {
85
					t.Errorf("missing User field")
86
				}
87
				if !strings.Contains(result, "type OutUser struct") {
88
					t.Errorf("missing OutUser struct")
89
				}
90
			},
91
		},
92
		"empty nested object": {
93
			input: `{"user": {}}`,
94
			check: func(t *testing.T, result string) {
95
				if !strings.Contains(result, "type Out struct") {
96
					t.Errorf("missing Out struct")
97
				}
98
				if !strings.Contains(result, "type OutUser struct") {
99
					t.Errorf("missing OutUser struct")
100
				}
101
			},
102
		},
103
		"array of object": {
104
			input: `[{"name": "John"}, {"name": "Jane"}]`,
105
			check: func(t *testing.T, result string) {
106
				if !strings.Contains(result, "type Out []OutItem") {
107
					t.Errorf("missing Out array type")
108
				}
109
				if !strings.Contains(result, "type OutItem struct") {
110
					t.Errorf("missing OutItem struct")
111
				}
112
			},
113
		},
114
		"empty array": {
115
			input: `{"items": []}`,
116
			check: func(t *testing.T, result string) {
117
				if !strings.Contains(result, "Items []any `json:\"items\"`") {
118
					t.Errorf("missing Items field")
119
				}
120
			},
121
		},
122
		"null": {
123
			input: `{"item": null}`,
124
			check: func(t *testing.T, result string) {
125
				if !strings.Contains(result, "Item any `json:\"item\"`") {
126
					t.Errorf("missing Item field")
127
				}
128
			},
129
		},
130
		"numbers": {
131
			input: `{"pos": 123, "neg": -321, "float": 420.69}`,
132
			check: func(t *testing.T, result string) {
133
				if !strings.Contains(result, "Pos int `json:\"pos\"`") {
134
					t.Errorf("missing Pos field")
135
				}
136
				if !strings.Contains(result, "Neg int `json:\"neg\"`") {
137
					t.Errorf("missing Neg field")
138
				}
139
				if !strings.Contains(result, "Float float64 `json:\"float\"`") {
140
					t.Errorf("missing Float field")
141
				}
142
			},
143
		},
144
	}
145
146
	for tname, tt := range tests {
147
		t.Run(tname, func(t *testing.T) {
148
			sn := "Out"
149
			if tt.structName != "" {
150
				sn = tt.structName
151
			}
152
153
			result, err := Transform(sn, tt.input, true)
154
			assertEqualErr(t, tt.err, err)
155
			if tt.check != nil {
156
				tt.check(t, result)
157
			}
158
		})
159
	}
160
}
161
162
func assertEqualErr(t *testing.T, expected, actual error) {
163
	t.Helper()
164
	if expected == nil && actual == nil {
165
		return
166
	}
167
168
	if expected == nil || actual == nil {
169
		t.Errorf("expected: %v, got: %v", expected, actual)
170
		return
171
	}
172
173
	if !errors.Is(actual, expected) {
174
		t.Errorf("expected error: %v, got: %v", expected, actual)
175
	}
176
}
177
178
func assertEqual[T any](t *testing.T, expected, actual T) {
179
	t.Helper()
180
	if !reflect.DeepEqual(expected, actual) {
181
		t.Errorf("expected: %v, got: %v\n", expected, actual)
182
	}
183
}