Skip to content

test(cli): cover the cloud client 401-refresh-retry decorator#1202

Merged
jrusso1020 merged 1 commit into
heygen-com:mainfrom
calcarazgre646:test/cloud-401-retry-coverage
Jun 4, 2026
Merged

test(cli): cover the cloud client 401-refresh-retry decorator#1202
jrusso1020 merged 1 commit into
heygen-com:mainfrom
calcarazgre646:test/cloud-401-retry-coverage

Conversation

@calcarazgre646
Copy link
Copy Markdown
Contributor

What

Adds test coverage for the 401-retry decorator that createCloudClient() wraps around the generated cloud client (packages/cli/src/cloud/index.ts). Zero source changes; sibling of #1131, which covered reportApiError in the same module.

Why

The decorator is the auth recovery path for every cloud command: it catches HyperframesApiError(status=401), force-refreshes the OAuth token, and retries the call exactly once. None of that was tested. A regression here would only surface for users as cloud commands failing outright on server-side token revocation or clock-skew rejections, exactly the cases the decorator exists to absorb.

How

The tests go through createCloudClient() rather than the unexported decorator, so they also pin the factory wiring the cloud commands actually use. auth.js is module-mocked to control the token lifecycle, and global fetch is stubbed (the generated client falls back to it when no fetchImpl is injected, and createCloudClient doesn't expose that knob).

Six cases:

  • success passes through with no refresh
  • 401 refreshes once and retries; the retry carries the re-resolved token, asserted on the recorded fetch headers (a refresh that isn't picked up would 401 forever, so this is the assertion that matters)
  • refresh failure surfaces the original 401, not the refresh error (the 401 carries the API message/code that reportApiError needs)
  • exactly one retry: a second 401 propagates
  • non-401 API errors (500) pass through with no refresh
  • transport errors that aren't HyperframesApiError pass through with no refresh

Validation

  • bunx vitest run (cli package): 679 passed, including the 6 new
  • bunx oxlint / oxfmt --check: clean
  • bun run build: green

createCloudClient wraps the generated client in a Proxy that catches
HyperframesApiError(401), force-refreshes credentials, and retries
once. That auth recovery path had no tests; a regression would only
surface as cloud commands failing outright on server-side token
revocation or clock-skew rejections.

Covers: passthrough, refresh-and-retry with the new token actually
re-resolved (not a stale header replay), refresh failure surfacing
the original 401, single-retry on repeated 401, and no refresh on
non-401 or transport errors. Zero source changes.
Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 left a comment

Choose a reason for hiding this comment

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

Test-only PR (zero source changes) covering the 401-refresh-retry decorator in packages/cli/src/cloud/index.ts:39 — the auth-recovery path that silently failed every cloud command on token revocation / clock-skew if it regressed. Genuinely untested before this. Reviewed the full test file end-to-end and cross-checked each assertion against the decorator and the generated client.

Strengths

  • Goes through createCloudClient() rather than the unexported wrapWith401Retry, so the tests also pin the factory wiring the cloud commands actually use (index.test.ts:38). Right altitude.
  • The load-bearing assertion is the real one: index.test.ts:78-85 checks the retry carries Bearer tok-new, not a replay of the stale header. I verified this isn't vacuous — _gen/client.ts:96 re-invokes getAuthHeaders() inside request() per call, so a refresh that wasn't re-resolved would 401 forever. This test would actually catch that.
  • Refresh-failure case asserts the original 401 surfaces (index.test.ts:101-105), matching the decorator's catch { throw err } at index.ts:60 — and correctly notes why (the 401 carries the message/code reportApiError needs), not the refresh error.
  • "Exactly one retry" (index.test.ts:115) and the two no-refresh branches (non-401 at :127, transport error at :141) cover the remaining decorator paths.
  • fetch is stubbed in beforeEach before createCloudClient() runs in each test body, so the opts.fetchImpl ?? fetch capture at _gen/client.ts:91 resolves to the mock — the PR body's note about this is accurate.

Notes (non-blocking)

  • nit: headersOf returns unknown and feeds toMatchObject — fine, but the type narrowing only guards against missing headers, not its shape. toMatchObject carries the real assertion, so this is purely cosmetic.

Verdict: APPROVE
Reasoning: Targeted, correct coverage of a previously-untested auth-recovery path; every assertion maps to a real decorator branch and the key new-token assertion is verified load-bearing. CI green, no source risk.

— Claude (pr-review)

@jrusso1020 jrusso1020 merged commit 0870394 into heygen-com:main Jun 4, 2026
34 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.

2 participants