Skip to content

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

Merged
smarcet merged 2 commits into
mainfrom
feature/promo-code-apply-to-all-audience-restriction
May 13, 2026
Merged

fix(promo-codes): restrict implicit "apply to all" to Audience=All only#541
smarcet merged 2 commits into
mainfrom
feature/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 — 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 — pass
  • Unit suite: full tests/Unit/Services/ passes (53 tests, 77 assertions, no deprecations)
  • Integration: CI's Run Unit Tests On Push workflow ran tests/oauth2/ (OAuth2Tests shard) on the PR head — green. The previously-flagged OAuth2SummitTicketTypesApiTest::testGetAllowedTicketTypesApplyingRegularDiscountCodeToAllOfThem (:218) still passes: default_ticket_type is Audience=All per tests/InsertSummitTestData.php:355, so it remains discount-eligible after the empty branch is gated, and '+id' ordering keeps it at data[0] for the assertion.
  • Manual: against a deploy carrying this patch, 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. Deferred: dev/staging do not yet carry this change. Reviewer please run after first deploy.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed promo code application to properly enforce audience restrictions when applicable to all ticket types, ensuring correct eligibility validation.
  • Tests

    • Added comprehensive unit test coverage for promo code audience restriction behavior across multiple ticket type scenarios.
  • Documentation

    • Added specification document detailing promo code audience applicability rules and validation requirements.

Review Change Stack

@caseylocker caseylocker self-assigned this May 11, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b165a68d-0441-4063-96d1-ba19f3c44eaa

📥 Commits

Reviewing files that changed from the base of the PR and between e1c0a94 and 08c6036.

📒 Files selected for processing (3)
  • app/Models/Foundation/Summit/Registration/PromoCodes/SummitRegistrationPromoCode.php
  • doc/promo-code-apply-to-all-audience-restriction.md
  • tests/Unit/Services/PromoCodeCanBeAppliedToAudienceTest.php
✅ Files skipped from review due to trivial changes (1)
  • doc/promo-code-apply-to-all-audience-restriction.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/Unit/Services/PromoCodeCanBeAppliedToAudienceTest.php

📝 Walkthrough

Walkthrough

This PR enforces audience-aware applicability for promo codes. When allowed_ticket_types is empty, the code now restricts application to tickets with Audience_All instead of applying unconditionally. The change includes a comprehensive specification document, a targeted implementation edit, and extensive test coverage validating the behavior across ticket audiences and inheritance hierarchy.

Changes

Promo Code Audience Restriction

Layer / File(s) Summary
Specification and task breakdown
doc/promo-code-apply-to-all-audience-restriction.md
SDS document establishes requirements, scope, authoritative constraints, concrete tasks, test plan, acceptance criteria, and references for tightening empty allowed_ticket_types behavior to match only Audience_All tickets.
Audience-based applicability in canBeAppliedTo()
app/Models/Foundation/Summit/Registration/PromoCodes/SummitRegistrationPromoCode.php
canBeAppliedTo() now checks that the ticket audience equals SummitTicketType::Audience_All when allowed_ticket_types is empty; non-empty allowed_ticket_types path behavior is unchanged.
Test coverage for audience restrictions
tests/Unit/Services/PromoCodeCanBeAppliedToAudienceTest.php
PHPUnit test suite validates the audience × allowed_ticket_types matrix across Audience_All, With_Invitation, Without_Invitation, and With_Promo_Code audiences, plus subclass behavior for SummitRegistrationDiscountCode and DomainAuthorizedSummitRegistrationDiscountCode.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 A spec, some logic, and tests aligned—
The promo code now knows its mind!
All audiences dance, but only the All-blessed remain,
While others must ask for the ticket refrain.
Audience-aware at last! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% 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 clearly and specifically describes the main change: restricting the implicit 'apply to all' behavior of promo codes to only ticket types with Audience=All.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/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-541/

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

@caseylocker caseylocker marked this pull request as ready for review May 12, 2026 15:48
@caseylocker caseylocker requested a review from smarcet May 12, 2026 15:49
@smarcet smarcet requested review from Copilot and romanetar May 12, 2026 15:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR tightens promo-code applicability so the implicit “apply to all ticket types” behavior (empty allowed_ticket_types) only applies to ticket types whose audience is All, preventing unintended applicability to restricted audiences like WithPromoCode.

Changes:

  • Updated SummitRegistrationPromoCode::canBeAppliedTo() so an empty allowed_ticket_types collection returns true only for Audience_All.
  • Added a dedicated unit-test matrix covering all audiences across empty/non-empty allowed-ticket-type scenarios, plus subclass delegation smoke tests.
  • Added an SDS documenting the rationale, scope, and acceptance criteria; updated a related in-code comment to reflect the new audience-conditional empty-collection behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
app/Models/Foundation/Summit/Registration/PromoCodes/SummitRegistrationPromoCode.php Restricts empty allowed_ticket_types (“apply to all”) to Audience_All ticket types only.
tests/Unit/Services/PromoCodeCanBeAppliedToAudienceTest.php Adds unit coverage guarding the new audience restriction and verifying subclass delegation behavior.
doc/promo-code-apply-to-all-audience-restriction.md Documents the design decision, scope, and test plan for the audience restriction.
app/Models/Foundation/Summit/Registration/PromoCodes/DomainAuthorizedSummitRegistrationDiscountCode.php Updates a comment to align with the now audience-conditional empty-collection behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Collaborator

@romanetar romanetar left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Copy Markdown
Collaborator

@smarcet smarcet left a comment

Choose a reason for hiding this comment

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

LGTM

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.
@smarcet smarcet force-pushed the feature/promo-code-apply-to-all-audience-restriction branch from e1c0a94 to 08c6036 Compare May 13, 2026 14:24
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

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

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

Copy link
Copy Markdown
Collaborator

@smarcet smarcet left a comment

Choose a reason for hiding this comment

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

LGTM

@smarcet smarcet merged commit 7cf7fae into main May 13, 2026
19 checks passed
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.

4 participants