Skip to content

Commit ea009ca

Browse files
authored
Merge pull request NetrunnerDB#382 from plural/convert-to-spec-tests
Convert to spec tests
2 parents 3ec15b4 + 6f82426 commit ea009ca

16 files changed

+1781
-1831
lines changed

.devcontainer/devcontainer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@
8383
"mtxr.sqltools-driver-pg",
8484
"rubocop.vscode-rubocop",
8585
"vscode-icons-team.vscode-icons",
86-
"waderyan.gitblame"
86+
"waderyan.gitblame",
87+
"github.vscode-github-actions"
8788
]
8889
}
8990
},

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565
- name: Upload coverage reports to Codecov
6666
uses: codecov/codecov-action@v4
6767
with:
68-
files: ./coverage/spec/coverage.xml,./coverage/unit/coverage.xml
68+
files: ./coverage/spec/coverage.xml
6969
env:
7070
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
7171

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
require 'parslet/convenience'
5+
6+
RSpec.describe CardSearchQueryBuilder do
7+
describe '#initialize' do
8+
context 'with simple successful query' do
9+
let(:builder) { described_class.new('x:trash') }
10+
11+
it 'parses without error' do
12+
expect(builder.parse_error).to be_nil
13+
end
14+
15+
it 'builds correct where clause' do
16+
expect(builder.where).to eq('lower(unified_cards.stripped_text) LIKE ?')
17+
expect(builder.where_values).to eq(['%trash%'])
18+
expect(builder.left_joins).to eq([])
19+
end
20+
end
21+
22+
context 'with multiple terms' do
23+
let(:builder) { described_class.new('x:trash cost:3') }
24+
25+
it 'parses without error' do
26+
expect(builder.parse_error).to be_nil
27+
end
28+
29+
it 'builds correct where clause' do
30+
expect(builder.where).to eq('lower(unified_cards.stripped_text) LIKE ? AND unified_cards.cost = ?')
31+
expect(builder.where_values).to eq(['%trash%', '3'])
32+
expect(builder.left_joins).to eq([])
33+
end
34+
end
35+
36+
context 'with numeric field not equal' do
37+
let(:builder) { described_class.new('trash_cost!3') }
38+
39+
it 'builds correct where clause' do
40+
expect(builder.where).to eq('unified_cards.trash_cost != ?')
41+
expect(builder.where_values).to eq(['3'])
42+
expect(builder.left_joins).to eq([])
43+
end
44+
end
45+
46+
context 'with numeric field less than' do
47+
let(:builder) { described_class.new('trash_cost<3') }
48+
49+
it 'builds correct where clause' do
50+
expect(builder.where).to eq('unified_cards.trash_cost < ?')
51+
expect(builder.where_values).to eq(['3'])
52+
expect(builder.left_joins).to eq([])
53+
end
54+
end
55+
56+
context 'with numeric field less than or equal to' do
57+
let(:builder) { described_class.new('trash_cost<=3') }
58+
59+
it 'builds correct where clause' do
60+
expect(builder.where).to eq('unified_cards.trash_cost <= ?')
61+
expect(builder.where_values).to eq(['3'])
62+
expect(builder.left_joins).to eq([])
63+
end
64+
end
65+
66+
context 'with numeric field greater than' do
67+
let(:builder) { described_class.new('trash_cost>3') }
68+
69+
it 'builds correct where clause' do
70+
expect(builder.where).to eq('unified_cards.trash_cost > ?')
71+
expect(builder.where_values).to eq(['3'])
72+
expect(builder.left_joins).to eq([])
73+
end
74+
end
75+
76+
context 'with numeric field greater than or equal to' do
77+
let(:builder) { described_class.new('trash_cost>=3') }
78+
79+
it 'builds correct where clause' do
80+
expect(builder.where).to eq('unified_cards.trash_cost >= ?')
81+
expect(builder.where_values).to eq(['3'])
82+
expect(builder.left_joins).to eq([])
83+
end
84+
end
85+
86+
context 'with string field not like' do
87+
let(:builder) { described_class.new('title!sure') }
88+
89+
it 'builds correct where clause' do
90+
expect(builder.where).to eq('lower(unified_cards.stripped_title) NOT LIKE ?')
91+
expect(builder.where_values).to eq(['%sure%'])
92+
expect(builder.left_joins).to eq([])
93+
end
94+
end
95+
96+
context 'with bad boolean operators' do
97+
['<', '<=', '>', '>='].each do |op|
98+
it "raises error for operator #{op}" do
99+
expect do
100+
described_class.new("is_unique#{op}true")
101+
end.to raise_error(RuntimeError, "Invalid boolean operator \"#{op}\"")
102+
end
103+
end
104+
end
105+
106+
context 'with bad string operators' do
107+
['<', '<=', '>', '>='].each do |op|
108+
it "raises error for operator #{op}" do
109+
expect do
110+
described_class.new("title#{op}sure")
111+
end.to raise_error(RuntimeError, "Invalid string operator \"#{op}\"")
112+
end
113+
end
114+
end
115+
116+
context 'with bare word' do
117+
let(:builder) { described_class.new('diversion') }
118+
119+
it 'builds correct where clause' do
120+
expect(builder.where).to eq('lower(unified_cards.stripped_title) LIKE ?')
121+
expect(builder.where_values).to eq(['%diversion%'])
122+
expect(builder.left_joins).to eq([])
123+
end
124+
end
125+
126+
context 'with bare word negated' do
127+
let(:builder) { described_class.new('!diversion') }
128+
129+
it 'builds correct where clause' do
130+
expect(builder.where).to eq('NOT lower(unified_cards.stripped_title) LIKE ?')
131+
expect(builder.where_values).to eq(['%diversion%'])
132+
expect(builder.left_joins).to eq([])
133+
end
134+
end
135+
136+
context 'with quoted string negated' do
137+
let(:builder) { described_class.new('!"diversion of funds"') }
138+
139+
it 'builds correct where clause' do
140+
expect(builder.where).to eq('NOT lower(unified_cards.stripped_title) LIKE ?')
141+
expect(builder.where_values).to eq(['%diversion of funds%'])
142+
expect(builder.left_joins).to eq([])
143+
end
144+
end
145+
146+
context 'with unicode in query' do
147+
let(:builder) { described_class.new('"Chaos Theory: Wünderkind"') }
148+
149+
it 'strips unicode for query' do
150+
expect(builder.where).to eq('lower(unified_cards.stripped_title) LIKE ?')
151+
expect(builder.where_values).to eq(['%chaos theory: wunderkind%'])
152+
expect(builder.left_joins).to eq([])
153+
end
154+
end
155+
156+
context 'with bad query operator' do
157+
it 'raises error for unknown keyword' do
158+
expect do
159+
described_class.new('asdfasdf:bleargh')
160+
end.to raise_error(RuntimeError, 'Unknown keyword asdfasdf')
161+
end
162+
end
163+
164+
context 'with is_banned and no restriction specified' do
165+
let(:builder) { described_class.new('is_banned:true') }
166+
167+
it 'builds correct where clause' do
168+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.restrictions_banned))')
169+
expect(builder.where_values).to eq(['true'])
170+
expect(builder.left_joins).to eq([])
171+
end
172+
end
173+
174+
context 'with is_restricted and no restriction specified' do
175+
let(:builder) { described_class.new('is_restricted:true') }
176+
177+
it 'builds correct where clause' do
178+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.restrictions_restricted))')
179+
expect(builder.where_values).to eq(['true'])
180+
expect(builder.left_joins).to eq([])
181+
end
182+
end
183+
184+
context 'with has_global_penalty and no restriction specified' do
185+
let(:builder) { described_class.new('has_global_penalty:true') }
186+
187+
it 'builds correct where clause' do
188+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.restrictions_global_penalty))')
189+
expect(builder.where_values).to eq(['true'])
190+
expect(builder.left_joins).to eq([])
191+
end
192+
end
193+
194+
context 'with is_banned and restriction specified' do
195+
let(:builder) { described_class.new('is_banned:true restriction_id:ban_list_foo') }
196+
197+
it 'builds correct where clause' do
198+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.restrictions_banned)) AND (? = ANY(unified_cards.restriction_ids))') # rubocop:disable Layout/LineLength
199+
expect(builder.where_values).to eq(%w[true ban_list_foo])
200+
expect(builder.left_joins).to eq([])
201+
end
202+
end
203+
204+
context 'with is_restricted and restriction specified' do
205+
let(:builder) { described_class.new('is_restricted:true restriction_id:ban_list_foo') }
206+
207+
it 'builds correct where clause' do
208+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.restrictions_restricted)) AND (? = ANY(unified_cards.restriction_ids))') # rubocop:disable Layout/LineLength
209+
expect(builder.where_values).to eq(%w[true ban_list_foo])
210+
expect(builder.left_joins).to eq([])
211+
end
212+
end
213+
214+
context 'with has_global_penalty and restriction specified' do
215+
let(:builder) { described_class.new('has_global_penalty:true restriction_id:ban_list_foo') }
216+
217+
it 'builds correct where clause' do
218+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.restrictions_global_penalty)) AND (? = ANY(unified_cards.restriction_ids))') # rubocop:disable Layout/LineLength
219+
expect(builder.where_values).to eq(%w[true ban_list_foo])
220+
expect(builder.left_joins).to eq([])
221+
end
222+
end
223+
224+
context 'with eternal_points' do
225+
let(:builder) { described_class.new('eternal_points:eternal_restriction_id-3') }
226+
227+
it 'builds correct where clause' do
228+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.restrictions_points))')
229+
expect(builder.where_values).to eq(['eternal_restriction_id=3'])
230+
expect(builder.left_joins).to eq([])
231+
end
232+
end
233+
234+
context 'with universal_faction_cost' do
235+
let(:builder) { described_class.new('universal_faction_cost:3') }
236+
237+
it 'builds correct where clause' do
238+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.restrictions_universal_faction_cost))')
239+
expect(builder.where_values).to eq(['3'])
240+
expect(builder.left_joins).to eq([])
241+
end
242+
end
243+
244+
context 'with card_pool' do
245+
let(:builder) { described_class.new('card_pool:best_pool') }
246+
247+
it 'builds correct where clause' do
248+
expect(builder.where.strip).to eq('(? = ANY(unified_cards.card_pool_ids))')
249+
expect(builder.where_values).to eq(['best_pool'])
250+
expect(builder.left_joins).to eq([])
251+
end
252+
end
253+
254+
context 'with bad boolean value' do
255+
it 'raises error for invalid boolean value' do
256+
expect do
257+
described_class.new('additional_cost:nah')
258+
end.to raise_error(RuntimeError, 'Invalid value "nah" for boolean field "additional_cost"')
259+
end
260+
end
261+
262+
context 'with bad numeric value' do
263+
it 'raises error for invalid integer value' do
264+
expect do
265+
described_class.new('trash_cost:"too damn high"')
266+
end.to raise_error(RuntimeError, 'Invalid value "too damn high" for integer field "trash_cost"')
267+
end
268+
end
269+
end
270+
end
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
RSpec.describe DeckValidation do
6+
let(:empty_validation) { { 'label' => 'empty' } }
7+
let(:snapshot_only) { { 'label' => 'expand snapshot', 'snapshot_id' => 'standard_02' } }
8+
let(:format_only) { { 'label' => 'expand format', 'format_id' => 'standard' } }
9+
let(:card_pool_only) { { 'label' => 'expand card_pool', 'card_pool_id' => 'startup_02' } }
10+
let(:restriction_only) { { 'label' => 'expand restriction', 'restriction_id' => 'standard_banlist' } }
11+
12+
describe '#initialize' do
13+
context 'with empty validation' do
14+
subject(:v) { described_class.new(empty_validation) }
15+
16+
it 'has expected fields' do
17+
expect(v.label).to eq('empty')
18+
expect(v.format_id).to be_nil
19+
expect(v.card_pool_id).to be_nil
20+
expect(v.restriction_id).to be_nil
21+
expect(v.snapshot_id).to be_nil
22+
expect(v).to be_valid
23+
end
24+
end
25+
26+
context 'with snapshot only' do
27+
subject(:v) { described_class.new(snapshot_only) }
28+
29+
it 'expands fields' do
30+
expect(v.label).to eq('expand snapshot')
31+
expect(v.format_id).to eq('standard')
32+
expect(v.card_pool_id).to eq('standard_02')
33+
expect(v.restriction_id).to eq('standard_banlist')
34+
expect(v.snapshot_id).to eq('standard_02')
35+
expect(v).to be_valid
36+
end
37+
end
38+
39+
context 'with format only' do
40+
subject(:v) { described_class.new(format_only) }
41+
42+
it 'expands fields' do
43+
expect(v.label).to eq('expand format')
44+
expect(v.format_id).to eq('standard')
45+
expect(v.card_pool_id).to eq('standard_02')
46+
expect(v.restriction_id).to eq('standard_banlist')
47+
expect(v.snapshot_id).to eq('standard_02')
48+
expect(v).to be_valid
49+
end
50+
end
51+
52+
context 'with card pool only' do
53+
subject(:v) { described_class.new(card_pool_only) }
54+
55+
it 'expands fields' do
56+
expect(v.label).to eq('expand card_pool')
57+
expect(v.format_id).to eq('startup')
58+
expect(v.card_pool_id).to eq('startup_02')
59+
expect(v.restriction_id).to be_nil
60+
expect(v.snapshot_id).to be_nil
61+
expect(v).to be_valid
62+
end
63+
end
64+
65+
context 'with restriction only' do
66+
subject(:v) { described_class.new(restriction_only) }
67+
68+
it 'expands fields' do
69+
expect(v.label).to eq('expand restriction')
70+
expect(v.format_id).to eq('standard')
71+
expect(v.restriction_id).to eq('standard_banlist')
72+
expect(v.card_pool_id).to be_nil
73+
expect(v.snapshot_id).to be_nil
74+
expect(v).to be_valid
75+
end
76+
end
77+
end
78+
end

0 commit comments

Comments
 (0)