Skip to content

fix(promo-codes): restrict implicit "apply to all" to Audience=All only#540

Closed
caseylocker wants to merge 2 commits into
mainfrom
feat/promo-code-apply-to-all-audience-restriction
Closed

fix(promo-codes): restrict implicit "apply to all" to Audience=All only#540
caseylocker wants to merge 2 commits into
mainfrom
feat/promo-code-apply-to-all-audience-restriction

Conversation

@caseylocker
Copy link
Copy Markdown
Contributor

@caseylocker caseylocker commented May 11, 2026

ref: https://app.clickup.com/t/86b9vrpxp

Summary

Tightens SummitRegistrationPromoCode::canBeAppliedTo() so the implicit "apply to all" branch (empty allowed_ticket_types) only matches ticket types with Audience = All. Other audiences (WithInvitation, WithoutInvitation, WithPromoCode) must be opted in via explicit allowed_ticket_types membership.

What changed

  • app/Models/Foundation/Summit/Registration/PromoCodes/SummitRegistrationPromoCode.php:483-495 — empty-collection branch returns $ticketType->getAudience() === SummitTicketType::Audience_All. Subclass overrides (SummitRegistrationDiscountCode::canBeAppliedTo, DomainAuthorizedSummitRegistrationDiscountCode::canBeAppliedTo) delegate to the patched base — no override changes needed.
  • tests/Unit/Services/PromoCodeCanBeAppliedToAudienceTest.php — 11 new unit tests, dataProvider over all 4 audiences × {empty, present, absent} + a smoke test for the domain-authorized override.
  • doc/promo-code-apply-to-all-audience-restriction.md — dedicated SDS.
  • app/Models/Foundation/Summit/Registration/PromoCodes/DomainAuthorizedSummitRegistrationDiscountCode.php:73 — tightened a stale comment now that the empty branch is audience-conditional.

Caller audit

canBeAppliedTo() is the single gate for every production path:

  • RegularPromoCodeTicketTypesStrategy::applyPromo2TicketType() line 82
  • SummitOrderService.php:812 and :1093 (order/checkout pipeline)
  • RegularTicketTypePromoCodeValidationStrategy.php:91

No code path independently treats empty allowed_ticket_types as "applies to all audiences" for promo codes. The out-of-scope sibling SummitAttendee.php:1367 is a different domain (invitations).

Behavior change (intentional)

Existing discount codes with empty allowed_ticket_types that today implicitly cover WithInvitation / WithoutInvitation tickets will stop covering them after deploy. This is called out in the ticket: "Existing discount codes with 'Apply to all' checked continue to work correctly for Audience = All ticket types — no regression."

AC5 wording note

The ClickUp acceptance criterion #5 says "submitting apply_to_all_ticket_types = true resolves to Audience = All only, regardless of frontend." No such payload field exists in this API — it's a conceptual name. The actual enforcement is canBeAppliedTo(), the single gate every consumer flows through. A hostile client cannot bypass it because there's no apply_to_all_* field to honor or ignore.

Test plan

  • Unit: ./vendor/bin/phpunit tests/Unit/Services/PromoCodeCanBeAppliedToAudienceTest.php — 11 tests, 11 assertions pass
  • Unit suite: full tests/Unit/Services/ passes (53 tests, 77 assertions, no deprecations)
  • Integration: docker test DB on this workspace is broken for unrelated reasons (migrations table missing columns) — not run here. Reviewer with a working test env: please run tests/oauth2/OAuth2SummitOrdersApiTest.php, OAuth2SummitTicketTypesApiTest.php, OAuth2PromoCodesApiTest.php. The existing testGetAllowedTicketTypesApplyingRegularDiscountCodeToAllOfThem in OAuth2SummitTicketTypesApiTest.php:218 may need updating since it removes types 1/2/3 from allowed_ticket_types and asserts the discount applies — that's exactly the path we tightened. Confirm the assertion is still meaningful after the fix and update if needed.
  • Manual: against staging, create a SUMMIT_DISCOUNT_CODE with empty rules, verify it does NOT apply to a WithPromoCode ticket type; add the WithPromoCode type to allowed_ticket_types, verify it then applies.

When SummitRegistrationPromoCode::allowed_ticket_types is empty, canBeAppliedTo()
previously returned true for any ticket type — including WithPromoCode-audience
tickets that are deliberately hidden from the public. This change tightens the
implicit-sweep branch to require Audience = All; non-All audiences must be
opted in via explicit allowed_ticket_types membership.

Adds 11 unit tests (4 audience values × empty/explicit) and a smoke test
confirming DomainAuthorizedSummitRegistrationDiscountCode's override still
applies to its explicitly-allowed WithPromoCode tickets.

Refs: ClickUp 86b9vrpxp; parent feature SDS doc/promo-codes-for-early-registration-access.md
…case

testNonEmptyAllowedTicketTypesNotContainingTicketReturnsFalse previously
only ran for Audience=All. Convert it to a DataProvider so a regression
that accidentally returns true on the absent-from-list path for
WithInvitation / WithoutInvitation / WithPromoCode also gets caught.

Also tighten the stale "returns true when allowed_ticket_types is empty"
comment in DomainAuthorizedSummitRegistrationDiscountCode::addTicketTypeRule
to reflect the post-fix conditional, and refresh the SDS line-number cites
to match the current method body.

Refs: ClickUp 86b9vrpxp; Codex review of 8defe88.
@caseylocker caseylocker self-assigned this May 11, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b4f663d8-c68e-46cc-9670-a508470715b2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/promo-code-apply-to-all-audience-restriction

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-540/

This page is automatically updated on each push to this PR.

@caseylocker caseylocker deleted the feat/promo-code-apply-to-all-audience-restriction branch May 11, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant