Skip to content

Address review feedback on anonymous checkout - #3735

Open
ChristopherChudzicki wants to merge 4 commits into
cg/anonymous-checkoutfrom
cc/anon-checkout-review-fixes
Open

Address review feedback on anonymous checkout#3735
ChristopherChudzicki wants to merge 4 commits into
cg/anonymous-checkoutfrom
cc/anon-checkout-review-fixes

Conversation

@ChristopherChudzicki

@ChristopherChudzicki ChristopherChudzicki commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review follow-ups for #3709, split out so that PR's history stays readable.

Three changes: the basket checkout query is invalidated unconditionally again, the anonymous_basket_id handoff finally has a test that runs the real hook, and some comments and test overrides that restated the code are gone. The reasoning is in the commit messages.

How can this be tested?

  • yarn test frontends/main/src/app-pages frontends/main/src/common/mitxonline
  • The new coverage is the "unauthenticated paid click" test in useCourseEnrollment.test.tsx. To confirm it isn't vacuous, delete the url.searchParams.set("anonymous_basket_id", ...) line in useReplaceBasketItem.ts — the test fails.
  • Manually, with the anonymous-checkout flag on and logged out: click a paid CTA on a course page and confirm the redirect to mitxonline /cart/ carries ?anonymous_basket_id=<uuid> alongside ecom-service=true.

Copilot AI balanced review requested due to automatic review settings August 6, 2026 21:32
@ChristopherChudzicki ChristopherChudzicki changed the title cc/anon checkout review fixes Address review feedback on anonymous checkout Aug 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Updates anonymous/cc checkout test coverage and MITx Online basket utilities to support anonymous basket handoff and simplify basket-state invalidation behavior.

Changes:

  • Simplifies several frontend tests by removing Learn-auth mocking that is no longer needed.
  • Adds a MITx Online baskets test factory and updates tests to use real UUIDs for anonymous baskets and consistent basket shapes.
  • Changes MITx Online basket hooks to always invalidate basketState after adding/clearing basket items.

Reviewed changes

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

Show a summary per file
File Description
frontends/main/src/page-components/EnrollmentDialogs/CourseEnrollmentDialog.test.tsx Removes Learn-auth pre-mocks and narrows api/test-utils imports.
frontends/main/src/common/mitxonline/useReplaceBasketItem.test.tsx Uses shared basket factory + real UUIDs for anonymous basket redirect assertions.
frontends/main/src/app-pages/ProductPages/useProgramEnrollment.ts Removes outdated/duplicated inline comments about anonymous vs free enrollment.
frontends/main/src/app-pages/ProductPages/useCourseEnrollment.ts Removes outdated/duplicated inline comments about anonymous vs free enrollment.
frontends/main/src/app-pages/ProductPages/useCourseEnrollment.test.tsx Uses basket factory; adds assertion that anonymous basket id is propagated via redirect URL.
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/UnenrolledCourseCard.test.tsx Removes Learn-auth pre-mocks and narrows api/test-utils imports.
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/ProgramEnrollmentDisplay.test.tsx Removes Learn-auth pre-mocks and narrows api/test-utils imports.
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/ProgramAsCourseCard.test.tsx Removes Learn-auth pre-mocks and narrows api/test-utils imports.
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/OrganizationCards.test.tsx Adjusts org fixture shape (removes sso_organization_id: null).
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/HomeEnrollmentsDisplay.test.tsx Removes Learn-auth pre-mocks/imports no longer needed.
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/EnrolledCourseCard.test.tsx Removes Learn-auth pre-mocks and narrows api/test-utils imports.
frontends/api/src/mitxonline/test-utils/factories/index.ts Exposes new baskets factory module.
frontends/api/src/mitxonline/test-utils/factories/baskets.ts Adds basket factory supporting user-owned and anonymous baskets.
frontends/api/src/mitxonline/hooks/baskets/index.ts Always invalidates basketState after add/clear (removes auth gating).

Comment thread frontends/api/src/mitxonline/hooks/baskets/index.ts
Comment thread frontends/api/src/mitxonline/hooks/baskets/index.ts
Comment thread frontends/api/src/mitxonline/test-utils/factories/baskets.ts Outdated
Comment thread frontends/api/src/mitxonline/test-utils/factories/baskets.ts Outdated
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

OpenAPI Changes

No changes detected

View full changelog

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@ChristopherChudzicki
ChristopherChudzicki force-pushed the cc/anon-checkout-review-fixes branch from 3a9a656 to 73db6eb Compare August 7, 2026 13:19
ChristopherChudzicki and others added 4 commits August 7, 2026 09:40
useAddToBasket/useClearBasket gated invalidateQueries on Learn's
useUserIsAuthenticated. Two problems:

- It reads the wrong session. The gated operation targets MITx Online
  through a separate APISIX route with its own OIDC cookie, so "logged
  out on Learn, logged in on MITx Online" would skip an invalidation
  that should fire.
- Gating belongs on the consumer, not the invalidator. invalidateQueries
  defaults to refetchType: "active", and a query whose observers all set
  enabled: false is not active, so an anonymous user never refetches
  anyway. basketState also sets gcTime: 0, so with no observers there is
  nothing cached to invalidate.

The guard could not prevent the 403 it was written for either: a
consumer that forgot `enabled` takes one on mount regardless, and
suppressing only the refetch turns a loud failure into a quiet stale one.

The stated rationale was also wrong -- the checkout-payload endpoint
kept IsAuthenticated, so an anonymous call 403s rather than creating an
order against a purchaser-less basket.

Drops the six beforeEach blocks that existed only to feed the guard a
Learn user.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing asserted the feature's actual output. useReplaceBasketItem.test
mocks api/mitxonline-hooks/baskets wholesale so the real hook never ran,
and the one test that did run it fed the hook a response with no
anonymous_id -- so the seam between "read the field off the API
response" and "put it on the redirect URL" was untested.

The fixture it used, { id: 1, items: [] }, was not a BasketWithProduct
either: `items` is not a field (it is basket_items) and four required
ones were missing. setMockResponse.post takes unknown, so TS did not
catch it.

Adds baskets factories alongside the other mitxonline ones: basket() for
a signed-in user, anonymousBasket() for the other case. The two are
mutually exclusive -- MITx Online enforces that with a check constraint,
basket_user_xor_anonymous_id -- so a caller who has to remember both
`user: null` and an `anonymous_id` can get it half right. Two named
constructors plus an XOR invariant cannot: setting both, or neither,
throws where the fixture is built rather than surfacing as a confusing
assertion failure later. The invariant tests for null/undefined rather
than truthiness, so a user id of 0 still counts as a user.

Tests read the generated id back off the factory instead of threading a
uuid through, which also retires the "abc-123" fixture: MITx Online's
handoff middleware parses the value with uuid.UUID() and logs-and-ignores
anything that does not, so the old fixture encoded a value that would
fail in production.

The anonymous case now runs through the real hook from mocked HTTP
response to asserted redirect URL. Verified it fails if cartUrl stops
appending the param.

Drops "does not append anonymous_basket_id when the basket belongs to a
real user". The factory default is anonymous_id: null, so the two
redirect tests above it already feed that exact basket and assert exact
URL equality, which is strictly stronger than asserting the param is
absent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three comment blocks were added to each enrollment hook. The one above
the auth guard earns its place -- it explains why the guard has that
shape, which is not obvious from the condition. The other two restate
the code, and the second restates the guard ten lines above it.

Also drops sso_organization_id: null from an OrganizationCards test
about logo: undefined, where it is an unrelated override.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The { id: 1, items: [] } stand-in the previous commit replaced appears at
eleven more mock sites across seven enrollment and dashboard test files.
None of them read the response, so nothing was failing -- but nothing
would start failing either if the real shape drifted, since
setMockResponse.post takes unknown and TS never checks these.

Mechanical: no assertions change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChristopherChudzicki
ChristopherChudzicki force-pushed the cc/anon-checkout-review-fixes branch from 73db6eb to ca35b99 Compare August 7, 2026 14:05
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.

2 participants