json2go/transpiler_test.go (view raw)
Oleksandr Smirnov
Oleksandr Smirnov
olexsmir@gmail.com refactor: use an actual parser instead of reflection..., 11 days ago
olexsmir@gmail.com refactor: use an actual parser instead of reflection..., 11 days ago
| 1 | package json2go |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | func TestTranspiler_Transpile_WithTags(t *testing.T) { |
| 9 | tests := map[string]struct { |
| 10 | v Value |
| 11 | name string |
| 12 | check func(t *testing.T, result string) |
| 13 | }{ |
| 14 | "simple object": { |
| 15 | name: "User", |
| 16 | v: Value{ |
| 17 | Kind: ObjectValue, |
| 18 | Object: []Field{ |
| 19 | {K: "name", V: Value{Kind: StringValue, Str: "John"}}, |
| 20 | {K: "age", V: Value{Kind: NumberValue, Int: 30}}, |
| 21 | }, |
| 22 | }, |
| 23 | check: func(t *testing.T, result string) { |
| 24 | if !strings.Contains(result, "type User struct") { |
| 25 | t.Errorf("missing User struct") |
| 26 | } |
| 27 | if !strings.Contains(result, "Name string `json:\"name\"`") { |
| 28 | t.Errorf("missing Name field") |
| 29 | } |
| 30 | if !strings.Contains(result, "Age int `json:\"age\"`") { |
| 31 | t.Errorf("missing Age field") |
| 32 | } |
| 33 | }, |
| 34 | }, |
| 35 | "nested object": { |
| 36 | name: "Response", |
| 37 | v: Value{ |
| 38 | Kind: ObjectValue, |
| 39 | Object: []Field{{ |
| 40 | K: "user", |
| 41 | V: Value{ |
| 42 | Kind: ObjectValue, |
| 43 | Object: []Field{ |
| 44 | {K: "name", V: Value{Kind: StringValue, Str: "Alice"}}, |
| 45 | {K: "active", V: Value{Kind: BoolValue, Bool: true}}, |
| 46 | }, |
| 47 | }, |
| 48 | }}, |
| 49 | }, |
| 50 | check: func(t *testing.T, result string) { |
| 51 | if !strings.Contains(result, "type Response struct") { |
| 52 | t.Errorf("missing Response struct") |
| 53 | } |
| 54 | if !strings.Contains(result, "type ResponseUser struct") { |
| 55 | t.Errorf("missing ResponseUser struct") |
| 56 | } |
| 57 | if !strings.Contains(result, "User ResponseUser `json:\"user\"`") { |
| 58 | t.Errorf("missing User field") |
| 59 | } |
| 60 | }, |
| 61 | }, |
| 62 | "array of scalars": { |
| 63 | name: "Tags", |
| 64 | v: Value{ |
| 65 | Kind: ArrayValue, |
| 66 | Array: []Value{{Kind: StringValue, Str: "go"}}, |
| 67 | }, |
| 68 | check: func(t *testing.T, result string) { |
| 69 | if result != "type Tags []string" { |
| 70 | t.Errorf("expected scalar array type, got: %s", result) |
| 71 | } |
| 72 | }, |
| 73 | }, |
| 74 | "array of objects": { |
| 75 | name: "Users", |
| 76 | v: Value{ |
| 77 | Kind: ArrayValue, |
| 78 | Array: []Value{{ |
| 79 | Kind: ObjectValue, |
| 80 | Object: []Field{ |
| 81 | {K: "id", V: Value{Kind: NumberValue, Int: 1}}, |
| 82 | {K: "name", V: Value{Kind: StringValue, Str: "Bob"}}, |
| 83 | }, |
| 84 | }}, |
| 85 | }, |
| 86 | check: func(t *testing.T, result string) { |
| 87 | if !strings.Contains(result, "type UsersItem struct") { |
| 88 | t.Errorf("missing UsersItem struct in: %s", result) |
| 89 | } |
| 90 | if !strings.Contains(result, "Id int `json:\"id\"`") { |
| 91 | t.Errorf("missing Id field") |
| 92 | } |
| 93 | }, |
| 94 | }, |
| 95 | "snake_case fields": { |
| 96 | name: "Data", |
| 97 | v: Value{ |
| 98 | Kind: ObjectValue, |
| 99 | Object: []Field{ |
| 100 | {K: "first_name", V: Value{Kind: StringValue, Str: "Jane"}}, |
| 101 | {K: "last_name", V: Value{Kind: StringValue, Str: "Doe"}}, |
| 102 | }, |
| 103 | }, |
| 104 | check: func(t *testing.T, result string) { |
| 105 | if !strings.Contains(result, "FirstName string `json:\"first_name\"`") { |
| 106 | t.Errorf("missing FirstName field") |
| 107 | } |
| 108 | if !strings.Contains(result, "LastName string `json:\"last_name\"`") { |
| 109 | t.Errorf("missing LastName field") |
| 110 | } |
| 111 | }, |
| 112 | }, |
| 113 | } |
| 114 | for tname, tt := range tests { |
| 115 | t.Run(tname, func(t *testing.T) { |
| 116 | result, err := NewTranspiler().Transpile(tt.name, tt.v, true) |
| 117 | if err != nil { |
| 118 | t.Fatalf("unexpected error: %v", err) |
| 119 | } |
| 120 | tt.check(t, result) |
| 121 | }) |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | func TestTranspiler_Transpile_WithoutTags(t *testing.T) { |
| 126 | tests := map[string]struct { |
| 127 | v Value |
| 128 | name string |
| 129 | check func(t *testing.T, result string) |
| 130 | }{ |
| 131 | "simple object": { |
| 132 | name: "User", |
| 133 | v: Value{ |
| 134 | Kind: ObjectValue, |
| 135 | Object: []Field{ |
| 136 | {K: "name", V: Value{Kind: StringValue, Str: "John"}}, |
| 137 | {K: "age", V: Value{Kind: NumberValue, Int: 30}}, |
| 138 | }, |
| 139 | }, |
| 140 | check: func(t *testing.T, result string) { |
| 141 | if !strings.Contains(result, "type User struct") { |
| 142 | t.Errorf("missing User struct") |
| 143 | } |
| 144 | if !strings.Contains(result, "Name string\n") { |
| 145 | t.Errorf("missing Name field without tag") |
| 146 | } |
| 147 | if !strings.Contains(result, "Age int\n") { |
| 148 | t.Errorf("missing Age field without tag") |
| 149 | } |
| 150 | if strings.Contains(result, "`json:") { |
| 151 | t.Errorf("should not have json tags") |
| 152 | } |
| 153 | }, |
| 154 | }, |
| 155 | "nested object": { |
| 156 | name: "Response", |
| 157 | v: Value{ |
| 158 | Kind: ObjectValue, |
| 159 | Object: []Field{{ |
| 160 | K: "user", |
| 161 | V: Value{ |
| 162 | Kind: ObjectValue, |
| 163 | Object: []Field{ |
| 164 | {K: "name", V: Value{Kind: StringValue, Str: "Alice"}}, |
| 165 | {K: "active", V: Value{Kind: BoolValue, Bool: true}}, |
| 166 | }, |
| 167 | }, |
| 168 | }}, |
| 169 | }, |
| 170 | check: func(t *testing.T, result string) { |
| 171 | if !strings.Contains(result, "type Response struct") { |
| 172 | t.Errorf("missing Response struct") |
| 173 | } |
| 174 | if !strings.Contains(result, "type ResponseUser struct") { |
| 175 | t.Errorf("missing ResponseUser struct") |
| 176 | } |
| 177 | if strings.Contains(result, "`json:") { |
| 178 | t.Errorf("should not have json tags") |
| 179 | } |
| 180 | }, |
| 181 | }, |
| 182 | "snake_case fields preserved": { |
| 183 | name: "Data", |
| 184 | v: Value{ |
| 185 | Kind: ObjectValue, |
| 186 | Object: []Field{ |
| 187 | {K: "first_name", V: Value{Kind: StringValue, Str: "Jane"}}, |
| 188 | {K: "last_name", V: Value{Kind: StringValue, Str: "Doe"}}, |
| 189 | }, |
| 190 | }, |
| 191 | check: func(t *testing.T, result string) { |
| 192 | if !strings.Contains(result, "FirstName string\n") { |
| 193 | t.Errorf("missing FirstName field") |
| 194 | } |
| 195 | if !strings.Contains(result, "LastName string\n") { |
| 196 | t.Errorf("missing LastName field") |
| 197 | } |
| 198 | }, |
| 199 | }, |
| 200 | } |
| 201 | for tname, tt := range tests { |
| 202 | t.Run(tname, func(t *testing.T) { |
| 203 | result, err := NewTranspiler().Transpile(tt.name, tt.v, false) |
| 204 | if err != nil { |
| 205 | t.Fatalf("unexpected error: %v", err) |
| 206 | } |
| 207 | tt.check(t, result) |
| 208 | }) |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | func TestTranspiler_ParserIntegration(t *testing.T) { |
| 213 | tests := map[string]struct { |
| 214 | input string |
| 215 | structName string |
| 216 | includeTags bool |
| 217 | expectTags bool |
| 218 | }{ |
| 219 | "parse and transpile with tags": { |
| 220 | input: `{"user_name": "alice", "user_age": 25}`, |
| 221 | structName: "Profile", |
| 222 | includeTags: true, |
| 223 | expectTags: true, |
| 224 | }, |
| 225 | "parse and transpile without tags": { |
| 226 | input: `{"user_name": "bob", "user_age": 30}`, |
| 227 | structName: "Account", |
| 228 | includeTags: false, |
| 229 | expectTags: false, |
| 230 | }, |
| 231 | } |
| 232 | for tname, tt := range tests { |
| 233 | t.Run(tname, func(t *testing.T) { |
| 234 | lexer := NewLexer([]byte(tt.input)) |
| 235 | parser := NewParser(lexer) |
| 236 | v, err := parser.Parse() |
| 237 | if err != nil { |
| 238 | t.Fatalf("parse error: %v", err) |
| 239 | } |
| 240 | |
| 241 | result, err := NewTranspiler().Transpile(tt.structName, v, tt.includeTags) |
| 242 | if err != nil { |
| 243 | t.Fatalf("transpile error: %v", err) |
| 244 | } |
| 245 | |
| 246 | hasTag := strings.Contains(result, "`json:") |
| 247 | if tt.expectTags != hasTag { |
| 248 | t.Errorf("expected tags=%v, got=%v\n%s", tt.expectTags, hasTag, result) |
| 249 | } |
| 250 | |
| 251 | if !strings.Contains(result, "type "+tt.structName) { |
| 252 | t.Errorf("struct name %q not found in output", tt.structName) |
| 253 | } |
| 254 | }) |
| 255 | } |
| 256 | } |