Skip to content

Commit 3192305

Browse files
iHiDclaude
authored andcommitted
Update GlossaryEntryProposal commands to manage status and add Search tests
- Update GlossaryEntryProposal::Approve to set glossary_entry status to :checked - Update GlossaryEntryProposal::Reject to set status to :unchecked when no pending proposals remain - Add locale parameter support to GlossaryEntry::Search - Create comprehensive test suite for GlossaryEntry::Search following Original::SearchTest pattern - Update assembler to pass filter_locale parameter to Search command 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d9b4058 commit 3192305

7 files changed

Lines changed: 284 additions & 8 deletions

File tree

app/assemblers/assemble_localization_glossary_entries.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ def glossary_entries
2424
user,
2525
criteria: params[:criteria],
2626
status: params[:status],
27-
page: params[:page]
27+
page: params[:page],
28+
locale: params[:filter_locale]
2829
)
2930
end
3031
end

app/commands/localization/glossary_entry/search.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ def self.default_per
88
DEFAULT_PER
99
end
1010

11-
def initialize(user, page: nil, per: nil, criteria: nil, status: nil)
11+
def initialize(user, page: nil, per: nil, criteria: nil, status: nil, locale: nil)
1212
@user = user
1313

1414
@criteria = criteria
1515
@status = status
16+
@locale = locale
1617
@page = page.present? && page.to_i.positive? ? page.to_i : DEFAULT_PAGE
1718
@per = per.present? && per.to_i.positive? ? per.to_i : self.class.default_per
1819
end
@@ -22,6 +23,7 @@ def call
2223

2324
filter_criteria!
2425
filter_status!
26+
filter_locale!
2527

2628
paginated_glossary_entries = @glossary_entries.
2729
order(:locale, :term).
@@ -37,7 +39,7 @@ def call
3739
def locales = user.translator_locales - [:en]
3840

3941
private
40-
attr_reader :user, :per, :page, :criteria, :status
42+
attr_reader :user, :per, :page, :criteria, :status, :locale
4143

4244
def filter_criteria!
4345
return if criteria.blank?
@@ -53,4 +55,10 @@ def filter_status!
5355

5456
@glossary_entries = @glossary_entries.where("localization_glossary_entries.status": status)
5557
end
58+
59+
def filter_locale!
60+
return if locale.blank?
61+
62+
@glossary_entries = @glossary_entries.where("localization_glossary_entries.locale": locale)
63+
end
5664
end

app/commands/localization/glossary_entry_proposal/approve.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def handle_addition!
2525
locale: proposal.locale,
2626
term: proposal.term,
2727
translation: proposal.translation,
28-
llm_instructions: proposal.llm_instructions
28+
llm_instructions: proposal.llm_instructions,
29+
status: :checked
2930
)
3031
end
3132

@@ -34,7 +35,8 @@ def handle_modification!
3435

3536
proposal.glossary_entry.update!(
3637
translation: proposal.translation,
37-
llm_instructions: proposal.llm_instructions
38+
llm_instructions: proposal.llm_instructions,
39+
status: :checked
3840
)
3941
end
4042

app/commands/localization/glossary_entry_proposal/reject.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ def call
99
status: :rejected,
1010
reviewer: user
1111
)
12+
13+
# Set glossary_entry status to unchecked if no pending proposals remain (for modifications only)
14+
if proposal.type == :modification && proposal.glossary_entry
15+
glossary_entry = proposal.glossary_entry
16+
glossary_entry.update!(status: :unchecked) unless glossary_entry.proposals.pending.exists?
17+
end
1218
end
1319
end
1420
end
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
require 'test_helper'
2+
3+
class Localization::GlossaryEntry::SearchTest < ActiveSupport::TestCase
4+
test "searches for user's locales" do
5+
user = create :user
6+
user.stubs(translator_locales: %i[hu nl])
7+
8+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "term1", status: :checked
9+
glossary_entry_2 = create :localization_glossary_entry, locale: "nl", term: "term2", status: :checked
10+
11+
# Different locale (not in user's translator_locales)
12+
create :localization_glossary_entry, locale: "fr", term: "term3", status: :checked
13+
14+
# English locale (should be excluded)
15+
create :localization_glossary_entry, locale: "en", term: "term4", status: :checked
16+
17+
actual = Localization::GlossaryEntry::Search.(user)
18+
19+
expected = [glossary_entry_1, glossary_entry_2]
20+
assert_equal expected, actual.to_a
21+
end
22+
23+
test "searches for unchecked glossary entries" do
24+
user = create :user
25+
user.stubs(translator_locales: %i[hu nl])
26+
27+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "term1", status: :unchecked
28+
glossary_entry_2 = create :localization_glossary_entry, locale: "nl", term: "term2", status: :unchecked
29+
30+
# Proposed
31+
create :localization_glossary_entry, locale: "hu", term: "term3", status: :proposed
32+
33+
# Checked
34+
create :localization_glossary_entry, locale: "hu", term: "term4", status: :checked
35+
36+
actual = Localization::GlossaryEntry::Search.(user, status: :unchecked)
37+
38+
expected = [glossary_entry_1, glossary_entry_2]
39+
assert_equal expected, actual.to_a
40+
end
41+
42+
test "searches for checked glossary entries" do
43+
user = create :user
44+
user.stubs(translator_locales: %i[hu nl])
45+
46+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "term1", status: :checked
47+
glossary_entry_2 = create :localization_glossary_entry, locale: "nl", term: "term2", status: :checked
48+
49+
# Unchecked
50+
create :localization_glossary_entry, locale: "hu", term: "term3", status: :unchecked
51+
52+
# Proposed
53+
create :localization_glossary_entry, locale: "hu", term: "term4", status: :proposed
54+
55+
actual = Localization::GlossaryEntry::Search.(user, status: :checked)
56+
57+
expected = [glossary_entry_1, glossary_entry_2]
58+
assert_equal expected, actual.to_a
59+
end
60+
61+
test "honours criteria for term" do
62+
user = create :user
63+
user.stubs(translator_locales: %i[hu nl])
64+
65+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "foobar", translation: "something", status: :checked
66+
create :localization_glossary_entry, locale: "nl", term: "bazqux", translation: "other", status: :checked
67+
68+
# Different locale (should not be included)
69+
create :localization_glossary_entry, locale: "fr", term: "foobar", translation: "something", status: :checked
70+
71+
actual = Localization::GlossaryEntry::Search.(user, criteria: "foo")
72+
73+
expected = [glossary_entry_1]
74+
assert_equal expected, actual.to_a
75+
end
76+
77+
test "honours criteria for translation" do
78+
user = create :user
79+
user.stubs(translator_locales: %i[hu nl])
80+
81+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "term1", translation: "contains foo here",
82+
status: :checked
83+
create :localization_glossary_entry, locale: "nl", term: "term2", translation: "contains bar here",
84+
status: :checked
85+
86+
# Different locale (should not be included)
87+
create :localization_glossary_entry, locale: "fr", term: "term3", translation: "contains foo here",
88+
status: :checked
89+
90+
actual = Localization::GlossaryEntry::Search.(user, criteria: "foo")
91+
92+
expected = [glossary_entry_1]
93+
assert_equal expected, actual.to_a
94+
end
95+
96+
test "filters by locale" do
97+
user = create :user
98+
user.stubs(translator_locales: %i[hu nl fr])
99+
100+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "term1", status: :checked
101+
glossary_entry_2 = create :localization_glossary_entry, locale: "hu", term: "term2", status: :checked
102+
create :localization_glossary_entry, locale: "nl", term: "term3", status: :checked
103+
create :localization_glossary_entry, locale: "fr", term: "term4", status: :checked
104+
105+
actual = Localization::GlossaryEntry::Search.(user, locale: "hu")
106+
107+
expected = [glossary_entry_1, glossary_entry_2]
108+
assert_equal expected, actual.to_a
109+
end
110+
111+
test "combines filters" do
112+
user = create :user
113+
user.stubs(translator_locales: %i[hu nl])
114+
115+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "foobar", status: :unchecked
116+
create :localization_glossary_entry, locale: "hu", term: "bazqux", status: :unchecked
117+
create :localization_glossary_entry, locale: "hu", term: "foobar", status: :checked
118+
create :localization_glossary_entry, locale: "nl", term: "foobar", status: :unchecked
119+
120+
actual = Localization::GlossaryEntry::Search.(user, criteria: "foo", status: :unchecked, locale: "hu")
121+
122+
expected = [glossary_entry_1]
123+
assert_equal expected, actual.to_a
124+
end
125+
126+
test "orders by locale then term" do
127+
user = create :user
128+
user.stubs(translator_locales: %i[hu nl])
129+
130+
glossary_entry_1 = create :localization_glossary_entry, locale: "nl", term: "zebra", status: :checked
131+
glossary_entry_2 = create :localization_glossary_entry, locale: "hu", term: "apple", status: :checked
132+
glossary_entry_3 = create :localization_glossary_entry, locale: "hu", term: "banana", status: :checked
133+
glossary_entry_4 = create :localization_glossary_entry, locale: "nl", term: "apple", status: :checked
134+
135+
actual = Localization::GlossaryEntry::Search.(user)
136+
137+
expected = [glossary_entry_2, glossary_entry_3, glossary_entry_4, glossary_entry_1]
138+
assert_equal expected, actual.to_a
139+
end
140+
141+
test "paginates" do
142+
Localization::GlossaryEntry::Search.stubs(:default_per).returns(1)
143+
144+
user = create :user
145+
user.stubs(translator_locales: %i[hu nl])
146+
147+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "term1", status: :checked
148+
glossary_entry_2 = create :localization_glossary_entry, locale: "nl", term: "term2", status: :checked
149+
150+
actual = Localization::GlossaryEntry::Search.(user, page: 1)
151+
assert_equal 2, actual.total_count
152+
assert_equal 1, actual.size
153+
assert_includes [glossary_entry_1.id, glossary_entry_2.id], actual.first.id
154+
assert_equal 1, actual.current_page
155+
assert_equal 2, actual.total_pages
156+
157+
actual = Localization::GlossaryEntry::Search.(user, page: 2)
158+
assert_equal 2, actual.total_count
159+
assert_equal 1, actual.size
160+
assert_includes [glossary_entry_1.id, glossary_entry_2.id], actual.first.id
161+
assert_equal 2, actual.current_page
162+
assert_equal 2, actual.total_pages
163+
164+
# Ensure we got different items on each page
165+
page1_ids = Localization::GlossaryEntry::Search.(user, page: 1).map(&:id)
166+
page2_ids = Localization::GlossaryEntry::Search.(user, page: 2).map(&:id)
167+
assert_equal [glossary_entry_1.id, glossary_entry_2.id].sort, (page1_ids + page2_ids).sort
168+
end
169+
170+
test "handles custom per page" do
171+
user = create :user
172+
user.stubs(translator_locales: [:hu])
173+
174+
create :localization_glossary_entry, locale: "hu", term: "term1", status: :checked
175+
create :localization_glossary_entry, locale: "hu", term: "term2", status: :checked
176+
create :localization_glossary_entry, locale: "hu", term: "term3", status: :checked
177+
178+
actual = Localization::GlossaryEntry::Search.(user, per: 2, page: 1)
179+
assert_equal 3, actual.total_count
180+
assert_equal 2, actual.size
181+
assert_equal 1, actual.current_page
182+
assert_equal 2, actual.total_pages
183+
184+
actual = Localization::GlossaryEntry::Search.(user, per: 2, page: 2)
185+
assert_equal 3, actual.total_count
186+
assert_equal 1, actual.size
187+
assert_equal 2, actual.current_page
188+
assert_equal 2, actual.total_pages
189+
end
190+
191+
test "excludes english locale" do
192+
user = create :user
193+
user.stubs(translator_locales: %i[en hu nl])
194+
195+
glossary_entry_1 = create :localization_glossary_entry, locale: "hu", term: "term1", status: :checked
196+
glossary_entry_2 = create :localization_glossary_entry, locale: "nl", term: "term2", status: :checked
197+
create :localization_glossary_entry, locale: "en", term: "term3", status: :checked
198+
199+
actual = Localization::GlossaryEntry::Search.(user)
200+
201+
expected = [glossary_entry_1, glossary_entry_2]
202+
assert_equal expected, actual.to_a
203+
end
204+
205+
test "returns empty array when user has no translator locales" do
206+
user = create :user
207+
user.stubs(translator_locales: [])
208+
209+
create :localization_glossary_entry, locale: "hu", term: "term1", status: :checked
210+
211+
actual = Localization::GlossaryEntry::Search.(user)
212+
213+
assert_empty actual.to_a
214+
end
215+
216+
test "returns empty array when user only has english locale" do
217+
user = create :user
218+
user.stubs(translator_locales: [:en])
219+
220+
create :localization_glossary_entry, locale: "en", term: "term1", status: :checked
221+
222+
actual = Localization::GlossaryEntry::Search.(user)
223+
224+
assert_empty actual.to_a
225+
end
226+
end

test/commands/localization/glossary_entry_proposal/approve_test.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class Localization::GlossaryEntryProposal::ApproveTest < ActiveSupport::TestCase
2424
assert_equal term, glossary_entry.term
2525
assert_equal translation, glossary_entry.translation
2626
assert_equal llm_instructions, glossary_entry.llm_instructions
27+
assert_equal :checked, glossary_entry.status
2728
end
2829

2930
test "modification" do
@@ -38,8 +39,9 @@ class Localization::GlossaryEntryProposal::ApproveTest < ActiveSupport::TestCase
3839

3940
assert_equal :approved, proposal.reload.status
4041
assert_equal user, proposal.reviewer
41-
assert_equal translation, glossary_entry.translation
42+
assert_equal translation, glossary_entry.reload.translation
4243
assert_equal llm_instructions, glossary_entry.llm_instructions
44+
assert_equal :checked, glossary_entry.status
4345
end
4446

4547
test "deletion" do

test/commands/localization/glossary_entry_proposal/reject_test.rb

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
class Localization::GlossaryEntryProposal::RejectTest < ActiveSupport::TestCase
44
test "reject proposal with no other ones" do
55
user = create :user
6-
glossary_entry = create :localization_glossary_entry, term: "wild_term", locale: "en"
6+
glossary_entry = create :localization_glossary_entry, term: "wild_term", locale: "en", status: :checked
77
proposal = create :localization_glossary_entry_proposal, :modification, glossary_entry: glossary_entry, proposer: user,
88
translation: "Something wiiiiiild"
99

@@ -13,11 +13,12 @@ class Localization::GlossaryEntryProposal::RejectTest < ActiveSupport::TestCase
1313
assert_equal user, proposal.reviewer
1414

1515
assert_equal 0, glossary_entry.proposals.pending.count
16+
assert_equal :unchecked, glossary_entry.reload.status
1617
end
1718

1819
test "reject proposals with other pending ones" do
1920
user = create :user
20-
glossary_entry = create :localization_glossary_entry, term: "wild_term", locale: "hu"
21+
glossary_entry = create :localization_glossary_entry, term: "wild_term", locale: "hu", status: :checked
2122
proposal_1 = create :localization_glossary_entry_proposal, :modification, glossary_entry: glossary_entry, proposer: user,
2223
translation: "Something wiiiiiild"
2324
create :localization_glossary_entry_proposal, :modification, glossary_entry: glossary_entry, proposer: user,
@@ -29,5 +30,35 @@ class Localization::GlossaryEntryProposal::RejectTest < ActiveSupport::TestCase
2930
assert_equal user, proposal_1.reviewer
3031

3132
assert_equal 1, glossary_entry.proposals.pending.count
33+
assert_equal :checked, glossary_entry.reload.status # Should remain checked since there's still a pending proposal
34+
end
35+
36+
test "reject addition proposal" do
37+
user = create :user
38+
proposal = create :localization_glossary_entry_proposal, :addition, proposer: user,
39+
term: "new_term", locale: "en", translation: "A new term"
40+
41+
Localization::GlossaryEntryProposal::Reject.(proposal, user)
42+
43+
assert_equal :rejected, proposal.reload.status
44+
assert_equal user, proposal.reviewer
45+
46+
# No glossary entry should be created for rejected additions
47+
assert_equal 0, Localization::GlossaryEntry.where(term: "new_term", locale: "en").count
48+
end
49+
50+
test "reject deletion proposal" do
51+
user = create :user
52+
glossary_entry = create :localization_glossary_entry, term: "term_to_delete", locale: "en", status: :checked
53+
proposal = create :localization_glossary_entry_proposal, :deletion, glossary_entry: glossary_entry, proposer: user
54+
55+
Localization::GlossaryEntryProposal::Reject.(proposal, user)
56+
57+
assert_equal :rejected, proposal.reload.status
58+
assert_equal user, proposal.reviewer
59+
60+
# Glossary entry should still exist and status should remain unchanged
61+
assert glossary_entry.reload
62+
assert_equal :checked, glossary_entry.status
3263
end
3364
end

0 commit comments

Comments
 (0)