test(message-templates): review fixes left out of #140 squash (EVO-1716)#141
Conversation
…trim dead code (EVO-1716) Review follow-ups: - Add a real-user authorization happy-path: all prior green ran on the service token, which bypasses both gates (require_permissions + Pundit). Authenticate a user and stub check_user_permission per key to lock the new resource's contract (create -> 201, index -> 200, denied -> 403). - Restore sync coverage that regressed on the preserved sync_with_whatsapp_cloud action: 422 for a non-WhatsApp-Cloud channel and 404 for an unknown template id. - MessageTemplatePolicy: drop the dead Scope class (policy_scope is never called on MessageTemplate; the controller filters via base_scope). - Fix the misleading "Inbox not found" 404 when the caller passed a bare channel_id (now names the actual target).
Reviewer's GuideAdds real-user authorization tests for message template endpoints, restores sync_with_whatsapp_cloud coverage and behavior, removes unused MessageTemplatePolicy::Scope, and fixes a misleading 404 error message when channel_id is used instead of inbox_id. Sequence diagram for render_inbox_not_found 404 message selectionsequenceDiagram
participant Caller
participant MessageTemplatesController
Caller->>MessageTemplatesController: render_inbox_not_found
alt [params[:inbox_id].present?]
MessageTemplatesController->>MessageTemplatesController: set target = Inbox
else [no inbox_id param]
MessageTemplatesController->>MessageTemplatesController: set target = Channel
end
MessageTemplatesController->>Caller: error_response(ApiErrorCodes::RESOURCE_NOT_FOUND, "<target> not found")
Flow diagram for sync_with_whatsapp_cloud response codesflowchart TD
A["sync_with_whatsapp_cloud called"] --> B{"channel is WhatsApp Cloud?"}
B -- no --> C["respond 422 Unprocessable Entity"]
B -- yes --> D{"template id exists?"}
D -- no --> E["respond 404 Not Found"]
D -- yes --> F["perform sync and respond 200"]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The new authorization specs stub
EvoAuthService#check_user_permissioninline in each example; consider extracting a small helper or shared context to centralize the key-based stubbing and reduce duplication/maintenance overhead across these tests. - In
render_inbox_not_found, the selection ofInboxvsChannelbased solely on the presence ofinbox_idcould become misleading if both params are ever present or lookups change; it may be more robust to branch on which lookup actually failed or on a more explicit flag from the calling code.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new authorization specs stub `EvoAuthService#check_user_permission` inline in each example; consider extracting a small helper or shared context to centralize the key-based stubbing and reduce duplication/maintenance overhead across these tests.
- In `render_inbox_not_found`, the selection of `Inbox` vs `Channel` based solely on the presence of `inbox_id` could become misleading if both params are ever present or lookups change; it may be more robust to branch on which lookup actually failed or on a more explicit flag from the calling code.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
The channel_id filter on the flat endpoint had no real consumer: it never existed in the original inbox-nested code (it was added during the cutover), the FE always sends inbox_id, and evo-flow reads the table directly. Only a self-authored spec exercised it. Removing it also eliminates the latent bootstrap gap the review flagged: resolve_channel resolved a bare channel_id via find_by! over existing rows, so create/update/destroy against a channel with no templates yet 404'd instead of bootstrapping. With channel_id gone the write path is purely inbox-driven, the "Inbox not found" 404 is always accurate (the earlier target-branching is no longer needed), and channel_bound?/base_scope collapse to a single axis.
dpaes
left a comment
There was a problem hiding this comment.
Reviewed — approved ✅
Verified the follow-up delta against the real code on danilocarneiro/evo-1716-review-fixes. CRM-community runs no RSpec in CI, so I traced each spec's logic by hand rather than trusting the self-reported green.
- Real-user authz happy-path — traced the full chain:
require_permissions→check_<action>_permission!→check_permission!(key)→EvoAuthService#check_user_permission(user_id, key)(2-arg signature, matches.with(anything, 'message_templates.create')). Exactly one call per request with the action's key; Pundit goes throughUser#has_permission?(always true on community), not EvoAuthService, so the strict.withstub can't trip "unexpected arguments". 201 / 200 / 403 are all sound. - Sync coverage restored —
sync_template_with_whatsapp_clouddoes a globalMessageTemplate.find→ unknown uuid raisesRecordNotFound→ 404; theChannel::Whatsapp && provider == 'whatsapp_cloud'guard rejects the evolution channel → 422 (the cross-channel template is still found globally, hence 422 and not 404 — the fixtures line up). MessageTemplatePolicy::Scoperemoval — confirmed zero callers ofpolicy_scope(MessageTemplate)anywhere; the onlypolicy_scopereference is the genericPundit.policy_scope!inapplication_policy.rb. The controller authorizes instances + filters viabase_scope, so the Scope class was dead code. Safe to drop.channel_idsurface removal — gone from the controller entirely;base_scope/channel_bound?/resolve_channelcollapse to the singleinbox_idaxis. Confirmed no consumer ever sentchannel_id(FE global service sends neither filter, channel-bound sendsinbox_id; evo-flow reads the table directly), so the "Inbox not found" 404 is now always accurate.
Non-blocking nit: the PR description's "404 → 'Channel not found'" bullet describes commit 1's intermediate state — commit 2 dropped channel_id entirely, so the net diff only keeps the always-correct "Inbox not found". The code itself is coherent.
The one open item from the card (show / index global without channel scope) is an accepted by-design consequence of the account-less, instance-wide catalog — not a blocker.
Summary
Follow-up a #140. As correções da adversarial review foram commitadas depois que #140 já tinha sido mergeado (squash levou só o primeiro commit, head
f7b4440), então ficaram fora do develop. Este PR traz exatamente esse delta.require_permissions+ Pundit). Autentica um user e stubacheck_user_permissionpor key para travar o contrato do resource novo (create→ 201,index→ 200, negado → 403).sync_with_whatsapp_cloud(preservado, não movido): 422 para canal não-WhatsApp-Cloud e 404 para template id inexistente.MessageTemplatePolicy: removida a classeScope(dead code —policy_scopenunca é chamado sobreMessageTemplate; o controller filtra viabase_scope).channel_idpuro, a mensagem agora nomeia o alvo certo ("Channel not found") em vez de "Inbox not found".Test plan
message_templates_spec.rb+message_templates_service_token_spec.rb: 26 examples, 0 failures.Summary by Sourcery
Align message template authorization, sync behavior, and error responses with the intended contract and restore missing test coverage from a previous squash merge.
Bug Fixes:
Enhancements:
Tests: