|
1204 | 1204 | end |
1205 | 1205 | end |
1206 | 1206 |
|
| 1207 | + context 'when fast-path for unquoted keys' do |
| 1208 | + [ |
| 1209 | + { |
| 1210 | + input: '{ simple: "val" }', |
| 1211 | + expected_output: JSON.dump({ 'simple' => 'val' }), |
| 1212 | + desc: 'simple alphabetic unquoted key' |
| 1213 | + }, |
| 1214 | + { |
| 1215 | + input: '{ my_var_name: "val" }', |
| 1216 | + expected_output: JSON.dump({ 'my_var_name' => 'val' }), |
| 1217 | + desc: 'underscored identifier' |
| 1218 | + }, |
| 1219 | + { |
| 1220 | + input: '{ $special-var_1: "val" }', |
| 1221 | + expected_output: JSON.dump({ '$special-var_1' => 'val' }), |
| 1222 | + desc: 'special characters ($, -) allowed in fast-path regex' |
| 1223 | + }, |
| 1224 | + { |
| 1225 | + input: '{ key1: "v1", key2: "v2", key3: "v3" }', |
| 1226 | + expected_output: JSON.dump({ 'key1' => 'v1', 'key2' => 'v2', 'key3' => 'v3' }), |
| 1227 | + desc: 'sequence of fast-path keys' |
| 1228 | + }, |
| 1229 | + { |
| 1230 | + input: '{veryLongVariableNameThatShouldBeScannedInOneGo: true}', |
| 1231 | + expected_output: JSON.dump({ 'veryLongVariableNameThatShouldBeScannedInOneGo' => true }), |
| 1232 | + desc: 'long key triggering chunk scan' |
| 1233 | + }, |
| 1234 | + { |
| 1235 | + input: '{ key:val }', |
| 1236 | + expected_output: JSON.dump({ 'key' => 'val' }), |
| 1237 | + desc: 'unquoted key and unquoted value' |
| 1238 | + } |
| 1239 | + ].each do |tc| |
| 1240 | + it "correctly parses #{tc[:desc]}" do |
| 1241 | + expect(described_class.repair(tc[:input])).to eq(tc[:expected_output]) |
| 1242 | + end |
| 1243 | + end |
| 1244 | + end |
| 1245 | + |
| 1246 | + context 'when parse_number optimization and rewind logic' do |
| 1247 | + [ |
| 1248 | + { |
| 1249 | + input: '{"a":1,"b":2}', |
| 1250 | + expected_output: JSON.dump({ 'a' => 1, 'b' => 2 }), |
| 1251 | + desc: 'compact JSON with comma immediately following number' |
| 1252 | + }, |
| 1253 | + { |
| 1254 | + input: '{"a":123,"b":456}', |
| 1255 | + expected_output: JSON.dump({ 'a' => 123, 'b' => 456 }), |
| 1256 | + desc: 'compact JSON with multi-digit numbers' |
| 1257 | + }, |
| 1258 | + { |
| 1259 | + input: '{"float":1.5,"int":1}', |
| 1260 | + expected_output: JSON.dump({ 'float' => 1.5, 'int' => 1 }), |
| 1261 | + desc: 'compact JSON with mixed number types' |
| 1262 | + }, |
| 1263 | + { |
| 1264 | + input: '[1,2,3,4]', |
| 1265 | + expected_output: JSON.dump([1, 2, 3, 4]), |
| 1266 | + desc: 'compact array with numbers' |
| 1267 | + }, |
| 1268 | + { |
| 1269 | + input: '{"a": 1, "b": 2}', |
| 1270 | + expected_output: JSON.dump({ 'a' => 1, 'b' => 2 }), |
| 1271 | + desc: 'standard spacing (boundary check)' |
| 1272 | + }, |
| 1273 | + { |
| 1274 | + input: '{"key": 1e5,}', |
| 1275 | + expected_output: JSON.dump({ 'key' => 100_000.0 }), |
| 1276 | + desc: 'scientific notation followed by comma' |
| 1277 | + }, |
| 1278 | + { |
| 1279 | + input: '{"key": 123-}', |
| 1280 | + expected_output: JSON.dump({ 'key' => 123 }), |
| 1281 | + desc: 'number with invalid trailer needing strip and rewind' |
| 1282 | + } |
| 1283 | + ].each do |tc| |
| 1284 | + it "correctly handles #{tc[:desc]}" do |
| 1285 | + expect(described_class.repair(tc[:input])).to eq(tc[:expected_output]) |
| 1286 | + end |
| 1287 | + end |
| 1288 | + end |
| 1289 | + |
| 1290 | + context 'when peek_char unicode stability' do |
| 1291 | + [ |
| 1292 | + { |
| 1293 | + input: '{"ascii": 1, "uni\u00f6": 2}', |
| 1294 | + expected_output: JSON.dump({ 'ascii' => 1, 'uniö' => 2 }), |
| 1295 | + desc: 'mixed ASCII and Unicode escape' |
| 1296 | + }, |
| 1297 | + { |
| 1298 | + input: '{"👍": "thumbs_up"}', |
| 1299 | + expected_output: JSON.dump({ '👍' => 'thumbs_up' }), |
| 1300 | + desc: 'multibyte emoji as key' |
| 1301 | + }, |
| 1302 | + { |
| 1303 | + input: '{"“smart”": "quotes"}', |
| 1304 | + expected_output: JSON.dump({ '“smart”' => 'quotes' }), |
| 1305 | + desc: 'multibyte smart quotes as key' |
| 1306 | + }, |
| 1307 | + { |
| 1308 | + input: '{"key": "value with — dash"}', |
| 1309 | + expected_output: JSON.dump({ 'key' => 'value with — dash' }), |
| 1310 | + desc: 'multibyte char in value' |
| 1311 | + }, |
| 1312 | + { |
| 1313 | + input: '{"Український": "text"}', |
| 1314 | + expected_output: JSON.dump({ 'Український' => 'text' }), |
| 1315 | + desc: 'Cyrillic characters' |
| 1316 | + } |
| 1317 | + ].each do |tc| |
| 1318 | + it "correctly parses #{tc[:desc]}" do |
| 1319 | + expect(described_class.repair(tc[:input])).to eq(tc[:expected_output]) |
| 1320 | + end |
| 1321 | + end |
| 1322 | + end |
| 1323 | + |
1207 | 1324 | context 'with valid JSON (direct parser usage)' do |
1208 | 1325 | [ |
1209 | 1326 | { |
|
0 commit comments