Skip to content

Commit f3ed011

Browse files
committed
add test cases
1 parent 9818e37 commit f3ed011

File tree

5 files changed

+99
-61
lines changed

5 files changed

+99
-61
lines changed

.rubocop.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,22 @@ AllCops:
77
SuggestExtensions: false
88

99
Metrics/AbcSize:
10-
Max: 30
10+
Max: 60
1111

1212
Metrics/ClassLength:
13-
Max: 130
13+
Max: 800
1414

1515
Metrics/CyclomaticComplexity:
16-
Max: 10
16+
Max: 30
1717

1818
Metrics/MethodLength:
19-
Max: 30
19+
Max: 70
2020

2121
Metrics/PerceivedComplexity:
22-
Max: 10
22+
Max: 32
23+
24+
Metrics/BlockNesting:
25+
Max: 5
2326

2427
Naming/PredicateMethod:
2528
Enabled: false

Rakefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,4 @@ require 'rubocop/rake_task'
99

1010
RuboCop::RakeTask.new
1111

12-
task default: %i[spec]
13-
# task default: %i[rubocop spec]
12+
task default: %i[rubocop spec]

lib/json_mend/parser.rb

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -667,9 +667,9 @@ def determine_complex_delimiter_action(lstring_delimiter, rstring_delimiter)
667667
return [false, false] if (1..i).all? { |j| peek_char(j).to_s.match(/\s/) }
668668

669669
if current_context?(:object_value)
670-
return check_unmatched_in_object_value(i, lstring_delimiter, rstring_delimiter)
670+
return check_unmatched_in_object_value(index: i, lstring_delimiter:, rstring_delimiter:)
671671
elsif current_context?(:array)
672-
return check_unmatched_in_array(i, rstring_delimiter)
672+
return check_unmatched_in_array(rstring_delimiter:)
673673
elsif current_context?(:object_key)
674674
return [true, false]
675675
end
@@ -678,34 +678,34 @@ def determine_complex_delimiter_action(lstring_delimiter, rstring_delimiter)
678678
[false, false]
679679
end
680680

681-
def check_unmatched_in_object_value(i, lstring_delimiter, rstring_delimiter)
682-
i = skip_whitespaces_at(start_idx: i + 1)
683-
if peek_char(i) == ','
681+
def check_unmatched_in_object_value(index:, lstring_delimiter:, rstring_delimiter:)
682+
index = skip_whitespaces_at(start_idx: index + 1)
683+
if peek_char(index) == ','
684684
# So we found a comma, this could be a case of a single quote like "va"lue",
685685
# Search if it's followed by another key, starting with the first delimeter
686-
i = skip_to_character(lstring_delimiter, start_idx: i + 1)
687-
i += 1
688-
i = skip_to_character(rstring_delimiter, start_idx: i + 1)
689-
i += 1
690-
i = skip_whitespaces_at(start_idx: i)
691-
next_c = peek_char(i)
686+
index = skip_to_character(lstring_delimiter, start_idx: index + 1)
687+
index += 1
688+
index = skip_to_character(rstring_delimiter, start_idx: index + 1)
689+
index += 1
690+
index = skip_whitespaces_at(start_idx: index)
691+
next_c = peek_char(index)
692692
return [true, false] if next_c == ':'
693693
end
694694
# We found a delimiter and we need to check if this is a key
695695
# so find a rstring_delimiter and a colon after
696-
i = skip_to_character(rstring_delimiter, start_idx: i + 1)
697-
i += 1
698-
next_c = peek_char(i)
696+
index = skip_to_character(rstring_delimiter, start_idx: index + 1)
697+
index += 1
698+
next_c = peek_char(index)
699699
while next_c && next_c != ':'
700700
if TERMINATORS_VALUE.include?(next_c) || (
701701
next_c == rstring_delimiter &&
702-
peek_char(i - 1) != '\\'
702+
peek_char(index - 1) != '\\'
703703
)
704704
break
705705
end
706706

707-
i += 1
708-
next_c = peek_char(i)
707+
index += 1
708+
next_c = peek_char(index)
709709
end
710710

711711
# Only if we fail to find a ':' then we know this is misplaced quote
@@ -714,7 +714,7 @@ def check_unmatched_in_object_value(i, lstring_delimiter, rstring_delimiter)
714714
[false, false]
715715
end
716716

717-
def check_unmatched_in_array(_i, rstring_delimiter)
717+
def check_unmatched_in_array(rstring_delimiter:)
718718
# Heuristic: Check if this quote is a closer or internal.
719719
# 1. Find the NEXT delimiter (quote) index `j`.
720720
j = 1

spec/json_mend_spec.rb

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
# frozen_string_literal: true
22

3-
require 'timeout'
4-
5-
# Helper to enforce timeout for these specific tests
6-
def with_timeout(seconds = 1, &)
7-
Timeout.timeout(seconds, &)
8-
end
9-
103
RSpec.describe JsonMend do
114
it 'has a version number' do
125
expect(described_class::VERSION).not_to be_nil
@@ -947,6 +940,31 @@ def with_timeout(seconds = 1, &)
947940

948941
context 'with edge cases' do
949942
[
943+
{
944+
input: '1 ""',
945+
expected_output: '1',
946+
desc: 'multi-json with empty string skipped'
947+
},
948+
{
949+
input: "[1, # comment inside array \n 2]",
950+
expected_output: JSON.dump([1, 2]),
951+
desc: 'array with hash-style line comment'
952+
},
953+
{
954+
input: '[ "a", "b\"c" ]',
955+
expected_output: JSON.dump(['a', 'b"c']),
956+
desc: 'array with internal escaped quote logic'
957+
},
958+
{
959+
input: '{"a": "val\'ue"}',
960+
expected_output: JSON.dump({ 'a' => "val'ue" }),
961+
desc: 'string with escaped single quote'
962+
},
963+
{
964+
input: '{"key:colons": "value"}',
965+
expected_output: JSON.dump({ 'key:colons' => 'value' }),
966+
desc: 'key containing colon'
967+
},
950968
{
951969
input: '[ "key": "value", "key2": "value2" ]',
952970
expected_output: JSON.dump([{ 'key' => 'value', 'key2' => 'value2' }]),
@@ -1050,35 +1068,6 @@ def with_timeout(seconds = 1, &)
10501068
end
10511069
end
10521070

1053-
context 'when provided valid complex standard json' do
1054-
[
1055-
{
1056-
input: '{"simple": "string", "number": 123, "bool": true, "nil": null}',
1057-
expected_output: JSON.dump({ simple: 'string', number: 123, bool: true, nil: nil })
1058-
},
1059-
{
1060-
input: '{"nested": {"array": [1, 2, {"deep": "obj"}]}}',
1061-
expected_output: JSON.dump({ nested: { array: [1, 2, { deep: 'obj' }] } })
1062-
},
1063-
{
1064-
input: '{"unicode": "こんにちは", "emoji": "👍"}',
1065-
expected_output: JSON.dump({ unicode: 'こんにちは', emoji: '👍' })
1066-
},
1067-
{
1068-
input: '{"escapes":"\" \\\\ / \\b \\f \\n \\r \\t"}',
1069-
expected_output: JSON.dump({ escapes: "\" \\ / \b \f \n \r \t" })
1070-
},
1071-
{
1072-
input: '{"empty_obj": {}, "empty_arr": []}',
1073-
expected_output: JSON.dump({ empty_obj: {}, empty_arr: [] })
1074-
}
1075-
].each do |test_case|
1076-
it "preserves valid json: #{test_case[:input]}" do
1077-
expect(described_class.repair(test_case[:input])).to eq(test_case[:expected_output])
1078-
end
1079-
end
1080-
end
1081-
10821071
context 'when provided ambiguous number formats' do
10831072
[
10841073
{
@@ -1173,5 +1162,46 @@ def with_timeout(seconds = 1, &)
11731162
end
11741163
end
11751164
end
1165+
1166+
context 'with valid JSON (direct parser usage)' do
1167+
[
1168+
{
1169+
input: '{"a": 1}',
1170+
expected_output: JSON.dump({ 'a' => 1 })
1171+
},
1172+
{
1173+
input: '[1, 2, 3]',
1174+
expected_output: JSON.dump([1, 2, 3])
1175+
},
1176+
{
1177+
input: '{"a": [1, 2], "b": {"c": 3}}',
1178+
expected_output: JSON.dump({ 'a' => [1, 2], 'b' => { 'c' => 3 } })
1179+
},
1180+
{
1181+
input: '{"simple": "string", "number": 123, "bool": true, "nil": null}',
1182+
expected_output: JSON.dump({ simple: 'string', number: 123, bool: true, nil: nil })
1183+
},
1184+
{
1185+
input: '{"nested": {"array": [1, 2, {"deep": "obj"}]}}',
1186+
expected_output: JSON.dump({ nested: { array: [1, 2, { deep: 'obj' }] } })
1187+
},
1188+
{
1189+
input: '{"unicode": "こんにちは", "emoji": "👍"}',
1190+
expected_output: JSON.dump({ unicode: 'こんにちは', emoji: '👍' })
1191+
},
1192+
{
1193+
input: '{"escapes":"\" \\\\ / \\b \\f \\n \\r \\t"}',
1194+
expected_output: JSON.dump({ escapes: "\" \\ / \b \f \n \r \t" })
1195+
},
1196+
{
1197+
input: '{"empty_obj": {}, "empty_arr": []}',
1198+
expected_output: JSON.dump({ empty_obj: {}, empty_arr: [] })
1199+
}
1200+
].each do |test_case|
1201+
it "parses valid JSON #{test_case[:input]}" do
1202+
expect(described_class.repair(test_case[:input])).to eq(test_case[:expected_output])
1203+
end
1204+
end
1205+
end
11761206
end
11771207
end

spec/spec_helper.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# frozen_string_literal: true
22

33
require 'json_mend'
4+
require 'timeout'
5+
6+
# Helper to enforce timeout for these specific tests
7+
def with_timeout(seconds = 1, &)
8+
Timeout.timeout(seconds, &)
9+
end
410

511
RSpec.configure do |config|
612
# Enable flags like --only-failures and --next-failure

0 commit comments

Comments
 (0)