Skip to content

feat(settings): add reviewed bulk cleanup flows#1754

Open
JSONbored wants to merge 10 commits into
we-promise:mainfrom
JSONbored:codex/feat-settings-bulk-cleanup-recreated
Open

feat(settings): add reviewed bulk cleanup flows#1754
JSONbored wants to merge 10 commits into
we-promise:mainfrom
JSONbored:codex/feat-settings-bulk-cleanup-recreated

Conversation

@JSONbored
Copy link
Copy Markdown
Contributor

@JSONbored JSONbored commented May 11, 2026

Supersedes #1630. The original PR head was on the old JSONbored/sure fork network; this replacement branch lives on JSONbored/sure-upstream, the current fork of we-promise/sure.


Summary

  • Adds reviewed bulk cleanup flows for merchants, categories, and account metadata.
  • Keeps suggestions human-reviewed before applying changes.
  • Adds safer validation around merge targets and reviewed website/domain input.

Feature objective

Large imported or synced datasets can create duplicate merchants/categories and incomplete account/provider metadata. This PR adds reviewed, family-scoped bulk cleanup flows so users can clean up common data issues faster without auto-applying destructive changes.

What changed

  • Adds family-scoped bulk merge/update flows.
  • Preserves provider merchant records while allowing local cleanup.
  • Supports bulk website/domain updates for better logo and institution metadata.
  • Adds merchant merge UI behavior so users choose either an existing target or create a new target, not both.
  • Rejects invalid reviewed website input instead of silently dropping it.

Screenshots

Hosted on a dedicated screenshot asset branch so the recreated PR renders the user-facing cleanup flows consistently.

Bulk update institution domains
Bulk update institution domains
Bulk update merchant websites
Bulk update merchant websites
Merchant merge mutual exclusion
Merchant merge mutual exclusion
Category merge
Category merge

Why

Large imported or synced datasets often create duplicate merchants, categories, and incomplete account/provider metadata. Reviewed bulk cleanup makes Sure easier to maintain while keeping changes scoped, transactional, and reviewable before applying.

Validation

Run through the packaged devcontainer stack for this worktree:

  • devcontainer up --workspace-folder . --mount-git-worktree-common-dir --include-configuration
  • devcontainer exec --workspace-folder . bash -lc 'cd /workspace && RAILS_ENV=test bin/rails db:prepare'
  • devcontainer exec --workspace-folder . bash -lc 'cd /workspace && RAILS_ENV=test bin/rails assets:precompile'
  • devcontainer exec --workspace-folder . bash -lc 'cd /workspace && bin/rails test test/controllers/accounts_controller_test.rb test/controllers/categories_controller_test.rb test/controllers/family_merchants_controller_test.rb test/models/account_test.rb test/models/merchant_test.rb'
    • 108 runs, 348 assertions, 0 failures, 0 errors, 0 skips
  • devcontainer exec --workspace-folder . bash -lc 'cd /workspace && npm run lint'
    • Checked 94 files in 89ms. No fixes applied.
  • scoped bin/rubocop
    • 14 files inspected, no offenses detected
  • git diff --check

Notes

  • Screenshots added for the new user-facing flows.

Summary by CodeRabbit

  • New Features

    • Bulk-update institution domains and merchant websites via modal dialogs.
    • Merge categories and merchants with option to use or create a target; dynamic target-mode selector for merchant merge.
  • Improvements

    • Canonical domain normalization and unified Brandfetch logo handling across merchants and accounts.
    • Deterministic default merchant color and improved modal-friendly UI actions and form accessibility/sync behavior.
  • Tests

    • Expanded controller and model tests covering bulk updates, merges, domain normalization, and logo behavior.

Review Change Stack

@superagent-security superagent-security Bot added contributor:verified Contributor passed trust analysis. pr:verified PR passed security analysis. labels May 11, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Centralizes domain extraction and Brandfetch logo generation; adds bulk account-domain and family-merchant-website management with modal UIs; introduces Category::Merger and merchant-merge refactors; delegates logo generation in models; adds Stimulus form sync, routes, locales, views, and comprehensive tests.

Changes

All Changes (single cohesive cohort)

Layer / File(s) Summary
Merchant domain & Brandfetch helpers
app/models/merchant.rb
Adds Merchant.extract_domain and Merchant.brandfetch_logo_url_for with normalization, validation, and sanitized fallback helpers.
Account normalization & logo delegation
app/models/account.rb
Normalizes institution_domain before validation via Account.normalize_institution_domain and delegates Brandfetch URL generation to Merchant.brandfetch_logo_url_for.
FamilyMerchant & ProviderMerchant refactor
app/models/family_merchant.rb, app/models/provider_merchant.rb
Deterministic FamilyMerchant.default_color; generate_logo_url_from_website delegates to Merchant.brandfetch_logo_url_for; removed duplicate extract_domain helpers.
Category::Merger service
app/models/category/merger.rb
New service implementing validation (family/hierarchy/reparent rules) and transactional merge: reassign transactions, merge budget_categories with summing, reparent children, destroy sources, track merged_count.
Merchant::Merger transactional extraction
app/models/merchant/merger.rb
Extracts core merge loop into merge_sources! and runs it inside a Merchant.transaction, returning boolean for empty/no-op cases.
Routes and index links
config/routes.rb, app/views/*/index.html.erb
Adds collection routes for categories/family_merchants/accounts bulk/merge endpoints and adds index action links for bulk domains, merge, and bulk websites.
Accounts bulk domains: controller, view, params
app/controllers/accounts_controller.rb, app/views/accounts/bulk_domains.html.erb, config/locales/views/accounts/en.yml, test/controllers/accounts_controller_test.rb
Adds bulk_domains (layoutless modal) and bulk_update_domains (normalizes domain, validates selection, transactional updates, RecordInvalid rescue), modal view, strong params/scope, locales, and controller tests for success/failure.
FamilyMerchants bulk websites & merge changes
app/controllers/family_merchants_controller.rb, app/views/family_merchants/*.html.erb, config/locales/views/merchants/en.yml, test/controllers/family_merchants_controller_test.rb
Adds bulk website modal/update, normalizes/validates website domain, regenerates provider logos within transaction; refactors perform_merge for conflict guards, new-target creation with normalized domain, transactional merge helper, expanded error handling; Stimulus-wired merge view and tests.
Categories merge controller, view, locales
app/controllers/categories_controller.rb, app/views/categories/merge.html.erb, config/locales/views/categories/en.yml, test/controllers/categories_controller_test.rb
Adds merge/perform_merge actions with custom exceptions, strong params, target selection/creation helpers, transactional merge execution raising MergeTargetNotFound/EmptyCategoryMerge, modal view, locales, and comprehensive tests.
Stimulus merge-target form controller
app/javascript/controllers/merchant_merge_target_controller.js, app/views/family_merchants/merge.html.erb
New Stimulus controller syncs existing vs new target sections, clears opposite fields, toggles ARIA/disabled states and CSS classes, and runs before-submit synchronization.
Tests: controllers & models
test/...
Extensive new/updated tests covering Merchant.extract_domain and brandfetch URL generation, Account normalization, FamilyMerchant/ProviderMerchant logo behavior, and controller integration tests for bulk updates and merge behaviors (success, rollback, validation, and failure cases).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant FamilyMerchantsController
  participant Merchant
  participant DB
  Client->>FamilyMerchantsController: POST bulk_update_websites(params: website_url, merchant_ids)
  FamilyMerchantsController->>Merchant: extract_domain(website_url)
  Merchant-->>FamilyMerchantsController: normalized_domain or nil
  FamilyMerchantsController->>DB: transaction begin
  FamilyMerchantsController->>DB: update website_url for selected merchants
  FamilyMerchantsController->>Merchant: brandfetch_logo_url_for(website_url) for ProviderMerchant
  Merchant-->>FamilyMerchantsController: brandfetch_url or nil
  FamilyMerchantsController->>DB: update logo_url
  FamilyMerchantsController->>DB: transaction commit
  FamilyMerchantsController-->>Client: redirect with success or alert
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • jjmata
  • sokie

🐰 I hopped through domains and merged the rest,
Stimulus nudged the form to do its best,
Bulk updates hummed across modal panes,
Categories and merchants stitched their chains,
A rabbit cheers: tidy code, fewer pains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(settings): add reviewed bulk cleanup flows' accurately summarizes the main change—introducing bulk cleanup operations (domain/website/category merges) for accounts, merchants, and categories with review requirements before application.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b611165bf1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread app/controllers/family_merchants_controller.rb Outdated
Comment thread app/controllers/categories_controller.rb Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
test/controllers/family_merchants_controller_test.rb (1)

287-315: ⚡ Quick win

Move the Brandfetch-clearing checks into merchant model tests.

These two cases never hit FamilyMerchantsController; they exercise ProviderMerchant / FamilyMerchant behavior directly. Keeping them here makes the controller suite own model behavior and obscures which layer actually regressed.

As per coding guidelines, "Never test the implementation details of one class in another class's test suite" and "test/**/*_test.rb: Tests must mirror app/ structure."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/controllers/family_merchants_controller_test.rb` around lines 287 - 315,
These two tests in family_merchants_controller_test.rb are exercising
ProviderMerchant and FamilyMerchant model behavior (e.g.,
ProviderMerchant.create!, FamilyMerchant.create!,
ProviderMerchant#generate_logo_url_from_website!, and Brandfetch setting stubs)
and should be moved into the respective model test files; move the
provider-merchant test into test/models/provider_merchant_test.rb and the
family-merchant test into test/models/family_merchant_test.rb (or the existing
model test files), keep the same setup/assertions and the
Setting.stubs(:brand_fetch_client_id).returns(nil) lines, and remove them from
family_merchants_controller_test.rb so controller tests only cover controller
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/controllers/family_merchants_controller.rb`:
- Around line 119-123: Update the validation logic in FamilyMerchantsController
around where you call Merchant.extract_domain(permitted_params[:website_url]) so
malformed domains are rejected with a specific error: first check if
permitted_params[:website_url].present? && website_url.blank? and in that case
redirect_to bulk_websites_family_merchants_path with alert:
t(".invalid_domain"); otherwise keep the original selection check and
redirect_to bulk_websites_family_merchants_path with alert:
t(".invalid_selection") when merchants.empty? or website_url.blank?; apply the
same change to the analogous block at lines 133-135. Ensure you reference
Merchant.extract_domain, permitted_params[:website_url],
bulk_websites_family_merchants_path, t(".invalid_domain") and
t(".invalid_selection") when making the edits.
- Around line 190-192: The method conflicting_merge_target? should treat any
populated new_target_* field as mutually exclusive with target_id; update it to
return true when permitted_params[:target_id].present? and any key in
permitted_params whose name starts with "new_target_" has a present/non-blank
value. In other words, replace the single-key check (new_target_name) with a
scan like permitted_params.any? { |k, v| k.to_s.start_with?("new_target_") &&
v.present? } so symbol or string keys and all new_target_* fields are
considered.

In `@app/models/merchant.rb`:
- Line 18: The host normalization currently uses domain =
URI.parse(normalized_url).host&.sub(/\Awww\./, "") which is case-sensitive and
fails to strip uppercase "WWW."; update that call to use a case-insensitive
regex (e.g. sub(/\Awww\./i, "")) so it matches "www" in any case and keep
behavior consistent with sanitized_domain_from; also add a unit test in
test/models/merchant_test.rb asserting an input like "https://WWW.example.com"
yields "example.com".

---

Nitpick comments:
In `@test/controllers/family_merchants_controller_test.rb`:
- Around line 287-315: These two tests in family_merchants_controller_test.rb
are exercising ProviderMerchant and FamilyMerchant model behavior (e.g.,
ProviderMerchant.create!, FamilyMerchant.create!,
ProviderMerchant#generate_logo_url_from_website!, and Brandfetch setting stubs)
and should be moved into the respective model test files; move the
provider-merchant test into test/models/provider_merchant_test.rb and the
family-merchant test into test/models/family_merchant_test.rb (or the existing
model test files), keep the same setup/assertions and the
Setting.stubs(:brand_fetch_client_id).returns(nil) lines, and remove them from
family_merchants_controller_test.rb so controller tests only cover controller
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a22a426f-db33-40be-8a17-e0b77cf86247

📥 Commits

Reviewing files that changed from the base of the PR and between 36960fe and b611165.

📒 Files selected for processing (26)
  • app/controllers/accounts_controller.rb
  • app/controllers/categories_controller.rb
  • app/controllers/family_merchants_controller.rb
  • app/javascript/controllers/merchant_merge_target_controller.js
  • app/models/account.rb
  • app/models/category/merger.rb
  • app/models/family_merchant.rb
  • app/models/merchant.rb
  • app/models/merchant/merger.rb
  • app/models/provider_merchant.rb
  • app/views/accounts/bulk_domains.html.erb
  • app/views/accounts/index.html.erb
  • app/views/categories/index.html.erb
  • app/views/categories/merge.html.erb
  • app/views/family_merchants/bulk_websites.html.erb
  • app/views/family_merchants/index.html.erb
  • app/views/family_merchants/merge.html.erb
  • config/locales/views/accounts/en.yml
  • config/locales/views/categories/en.yml
  • config/locales/views/merchants/en.yml
  • config/routes.rb
  • test/controllers/accounts_controller_test.rb
  • test/controllers/categories_controller_test.rb
  • test/controllers/family_merchants_controller_test.rb
  • test/models/account_test.rb
  • test/models/merchant_test.rb

Comment thread app/controllers/family_merchants_controller.rb
Comment thread app/controllers/family_merchants_controller.rb
Comment thread app/models/merchant.rb Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/controllers/accounts_controller_test.rb (1)

41-51: ⚡ Quick win

Consider verifying flash message for consistency.

This test asserts the redirect behavior when update! raises RecordInvalid, but unlike the test at lines 53-64, it doesn't verify that flash[:alert] or flash[:notice] is set. If the controller is expected to set a flash message for validation errors (as it does for domain validation errors), add an assertion here for completeness.

💡 Suggested assertion
   assert_redirected_to bulk_domains_accounts_path
+  assert_not_nil flash[:alert]
 end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/controllers/accounts_controller_test.rb` around lines 41 - 51, The test
"bulk domain update redirects when an account update fails validation" currently
only asserts the redirect; add an assertion to verify the controller sets the
expected flash message (e.g., assert_equal or assert_match on flash[:alert] or
flash[:notice]) when
Account.any_instance.stubs(:update!).raises(ActiveRecord::RecordInvalid.new(`@account`));
ensure the added assertion matches the same flash key and message style used in
the companion test that handles domain validation errors so behavior is
consistent with bulk_update_domains_accounts_path -> bulk_domains_accounts_path
flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/controllers/accounts_controller_test.rb`:
- Around line 41-51: The test "bulk domain update redirects when an account
update fails validation" currently only asserts the redirect; add an assertion
to verify the controller sets the expected flash message (e.g., assert_equal or
assert_match on flash[:alert] or flash[:notice]) when
Account.any_instance.stubs(:update!).raises(ActiveRecord::RecordInvalid.new(`@account`));
ensure the added assertion matches the same flash key and message style used in
the companion test that handles domain validation errors so behavior is
consistent with bulk_update_domains_accounts_path -> bulk_domains_accounts_path
flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cdda2638-e794-40e1-b56f-aa4832eac58e

📥 Commits

Reviewing files that changed from the base of the PR and between b611165 and bd0cf6c.

📒 Files selected for processing (12)
  • app/controllers/accounts_controller.rb
  • app/controllers/categories_controller.rb
  • app/controllers/family_merchants_controller.rb
  • app/models/merchant.rb
  • config/locales/views/accounts/en.yml
  • config/locales/views/merchants/en.yml
  • test/controllers/accounts_controller_test.rb
  • test/controllers/categories_controller_test.rb
  • test/controllers/family_merchants_controller_test.rb
  • test/models/family_merchant_test.rb
  • test/models/merchant_test.rb
  • test/models/provider_merchant_test.rb
✅ Files skipped from review due to trivial changes (1)
  • test/models/provider_merchant_test.rb
🚧 Files skipped from review as they are similar to previous changes (8)
  • config/locales/views/merchants/en.yml
  • app/models/merchant.rb
  • config/locales/views/accounts/en.yml
  • app/controllers/categories_controller.rb
  • test/controllers/categories_controller_test.rb
  • app/controllers/accounts_controller.rb
  • app/controllers/family_merchants_controller.rb
  • test/models/merchant_test.rb

Copy link
Copy Markdown
Collaborator

jjmata commented May 12, 2026

Good set of features — the reviewed-before-applying approach is the right call for destructive bulk ops. A couple of things to flag:

Gemfile / Gemfile.lock changes should be scoped to this PR's intent
The Gemfile changes platform strings from windows to mswin mswin64 mingw x64_mingw and the Gemfile.lock bumps the Ruby version from 3.4.7p58 to 3.4.9p82. These are unrelated to the bulk cleanup feature. The platform change may affect what gems get installed on contributors' machines, and the Ruby version bump can cause issues for anyone who hasn't upgraded yet. Unless there's a specific reason these are needed for this PR, they'd be cleaner in a dedicated chore commit or a separate PR.

category/merger.rb follows model conventions well
The new Category::Merger at 113 lines is a good Fat-Models placement — logic in the model, controller staying thin. Same for Merchant::Merger.

merchant_merge_target_controller.js mutual exclusion UX
The Stimulus controller for the merchant merge form looks like the right tool here. One thing to verify: when a user selects an existing target via the dropdown, does the "new target" input clear/disable reliably on Turbo morph navigation? Stimulus connect() vs initialize() timing can be tricky when the form is inside a Turbo Frame that morphs.

Account.normalize_institution_domain
The bulk_update_domains action calls Account.normalize_institution_domain(...) and then checks if permitted_params[:institution_domain].present? && domain.blank? to reject invalid domains. Make sure normalize_institution_domain handles all the edge cases you'd want to reject (leading/trailing spaces, bare IPs, localhost, etc.) — the test coverage in test/models/account_test.rb additions will be the primary signal here.


Generated by Claude Code

JSONbored added 2 commits May 11, 2026 21:36
Reject non-public domain inputs for account and merchant website cleanup, and make the merchant merge target controller clear the inactive target mode deterministically.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/models/merchant_test.rb (1)

38-57: ⚡ Quick win

Consider adding a test for nil domain handling.

The extract_domain tests demonstrate that the method can return nil (lines 16-32). Consider adding a test to verify brandfetch_logo_url_for handles this case gracefully—either by returning nil, a fallback URL, or raising a clear error.

📝 Suggested test addition
test "brandfetch_logo_url_for handles nil domain" do
  Setting.stubs(:brand_fetch_client_id).returns("test-client")
  Merchant.stubs(:extract_domain).returns(nil)
  
  logo_url = Merchant.brandfetch_logo_url_for("invalid-url")
  
  # Assert expected behavior: nil, fallback, or error
  assert_nil logo_url
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/models/merchant_test.rb` around lines 38 - 57, Add a test covering the
case where Merchant.extract_domain returns nil: stub
Setting.brand_fetch_client_id and Setting.brand_fetch_logo_size as needed, stub
Merchant.extract_domain to return nil, call Merchant.brandfetch_logo_url_for
with an invalid URL (e.g., "invalid-url") and assert the expected behavior (for
this repo assert_nil logo_url) to ensure brandfetch_logo_url_for handles nil
domains gracefully.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/models/merchant_test.rb`:
- Around line 38-57: Add a test covering the case where Merchant.extract_domain
returns nil: stub Setting.brand_fetch_client_id and
Setting.brand_fetch_logo_size as needed, stub Merchant.extract_domain to return
nil, call Merchant.brandfetch_logo_url_for with an invalid URL (e.g.,
"invalid-url") and assert the expected behavior (for this repo assert_nil
logo_url) to ensure brandfetch_logo_url_for handles nil domains gracefully.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c554dbc-901a-4f5d-bc14-67552a6d817f

📥 Commits

Reviewing files that changed from the base of the PR and between d7e970c and 8d0ce80.

📒 Files selected for processing (4)
  • app/javascript/controllers/merchant_merge_target_controller.js
  • app/models/merchant.rb
  • test/models/account_test.rb
  • test/models/merchant_test.rb
✅ Files skipped from review due to trivial changes (1)
  • test/models/account_test.rb
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/models/merchant.rb
  • app/javascript/controllers/merchant_merge_target_controller.js

Add regression coverage for Brandfetch logo generation when domain extraction fails.
@JSONbored
Copy link
Copy Markdown
Contributor Author

Good set of features — the reviewed-before-applying approach is the right call for destructive bulk ops. A couple of things to flag:

Gemfile / Gemfile.lock changes should be scoped to this PR's intent The Gemfile changes platform strings from windows to mswin mswin64 mingw x64_mingw and the Gemfile.lock bumps the Ruby version from 3.4.7p58 to 3.4.9p82. These are unrelated to the bulk cleanup feature. The platform change may affect what gems get installed on contributors' machines, and the Ruby version bump can cause issues for anyone who hasn't upgraded yet. Unless there's a specific reason these are needed for this PR, they'd be cleaner in a dedicated chore commit or a separate PR.

category/merger.rb follows model conventions well The new Category::Merger at 113 lines is a good Fat-Models placement — logic in the model, controller staying thin. Same for Merchant::Merger.

merchant_merge_target_controller.js mutual exclusion UX The Stimulus controller for the merchant merge form looks like the right tool here. One thing to verify: when a user selects an existing target via the dropdown, does the "new target" input clear/disable reliably on Turbo morph navigation? Stimulus connect() vs initialize() timing can be tricky when the form is inside a Turbo Frame that morphs.

Account.normalize_institution_domain The bulk_update_domains action calls Account.normalize_institution_domain(...) and then checks if permitted_params[:institution_domain].present? && domain.blank? to reject invalid domains. Make sure normalize_institution_domain handles all the edge cases you'd want to reject (leading/trailing spaces, bare IPs, localhost, etc.) — the test coverage in test/models/account_test.rb additions will be the primary signal here.

Generated by Claude Code

Thanks, should be addressed now.

  • Gemfile / Gemfile.lock are no longer in this PR diff.
  • Category::Merger / Merchant::Merger stayed as-is since that was just positive confirmation.
  • Merchant merge target UX now clears/disables the opposite mode deterministically on connect() and submit, so Turbo frame reconnects/morphs should keep the form state sane.
  • Account.normalize_institution_domain / Merchant.extract_domain now handle the edge cases called out: trimmed input, uppercase WWW., malformed labels, localhost, and bare IPv4/IPv6 inputs.
  • Added the missing Brandfetch nil-domain regression coverage too.

Validated locally in the devcontainer with focused Rails tests, Biome, scoped RuboCop, Brakeman/Codex Security, and CodeRabbit.

@superagent-security superagent-security Bot removed the pr:verified PR passed security analysis. label May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor:verified Contributor passed trust analysis.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants