all repos

json2go @ 34a739e

convert json to go type annotations

json2go/json2go_test.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
feat: indentation, 6 months 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 TestTransformer_Transform(t *testing.T) {
25
	tests := map[string]struct {
26
		input      string
27
		output     string
28
		structName string
29
		err        error
30
	}{
31
		"simple object": {
32
			input: `{"name": "Olex", "active": true, "age": 420}`,
33
			output: "type Out struct {" +
34
				field(1, "Active", "bool") +
35
				field(1, "Age", "int") +
36
				field(1, "Name", "string") +
37
				"\n}",
38
		},
39
		"invalid json": {
40
			err:   ErrInvalidJSON,
41
			input: `{"invalid":json}`,
42
		},
43
		"invalid struct name, starts with number": {
44
			err:        ErrInvalidStructName,
45
			structName: "1Name",
46
		},
47
		"invalid struct name, has space": {
48
			err:        ErrInvalidStructName,
49
			structName: "Name Name2",
50
		},
51
		"invalid struct name, has non letter/number": {
52
			err:        ErrInvalidStructName,
53
			structName: "Name$",
54
		},
55
		"snake_case to CamelCase": {
56
			input: `{"first_name": "Bob", "last_name": "Bobberson"}`,
57
			output: "type Out struct {" +
58
				field(1, "FirstName", "string", "first_name") +
59
				field(1, "LastName", "string", "last_name") +
60
				"\n}",
61
		},
62
		"nested object and array": {
63
			input: `{"user": {"name": "Alice", "score": 95.5}, "tags": ["go", "json"]}`,
64
			output: "type Out struct {" +
65
				field(1, "Tags", "[]string") +
66
				field(1, "User", "struct {") +
67
				field(2, "Name", "string") +
68
				field(2, "Score", "float64") +
69
				"\n\t} `json:\"user\"`" +
70
				"\n}",
71
		},
72
		"empty nested object": {
73
			input: `{"user": {}}`,
74
			output: "type Out struct {" +
75
				field(1, "User", "struct {") +
76
				"\n\t} `json:\"user\"`" +
77
				"\n}",
78
		},
79
		"array of object": {
80
			input: `[{"name": "John"}, {"name": "Jane"}]`,
81
			output: "type Out []struct {" +
82
				field(1, "Name", "string") +
83
				"\n}",
84
		},
85
		"empty array": {
86
			input: `{"items": []}`,
87
			output: "type Out struct {" +
88
				field(1, "Items", "[]any") +
89
				"\n}",
90
		},
91
		"null": {
92
			input: `{"item": null}`,
93
			output: `type Out struct {` +
94
				field(1, "Item", "any") +
95
				"\n}",
96
		},
97
		"numbers": {
98
			input: `{"pos": 123, "neg": -321, "float": 420.69}`,
99
			output: "type Out struct {" +
100
				field(1, "Float", "float64") +
101
				field(1, "Neg", "int") +
102
				field(1, "Pos", "int") +
103
				"\n}",
104
		},
105
	}
106
107
	trans := NewTransformer()
108
	for tname, tt := range tests {
109
		t.Run(tname, func(t *testing.T) {
110
			sn := "Out"
111
			if tt.structName != "" {
112
				sn = tt.structName
113
			}
114
115
			result, err := trans.Transform(sn, tt.input)
116
			assertEqualErr(t, tt.err, err)
117
			assertEqual(t, tt.output, result)
118
		})
119
	}
120
}
121
122
func assertEqualErr(t *testing.T, expected, actual error) {
123
	t.Helper()
124
	if expected == nil && actual == nil {
125
		return
126
	}
127
128
	if expected == nil || actual == nil {
129
		t.Errorf("expected: %v, got: %v", expected, actual)
130
		return
131
	}
132
133
	if !errors.Is(actual, expected) {
134
		t.Errorf("expected error: %v, got: %v", expected, actual)
135
	}
136
}
137
138
func assertEqual[T any](t *testing.T, expected, actual T) {
139
	t.Helper()
140
	if !reflect.DeepEqual(expected, actual) {
141
		t.Errorf("expected: %v, got: %v\n", expected, actual)
142
	}
143
}