Skip to content

release: promote OAuth consent error handling and automated test teardown to main - #27

Merged
JOY (JOY) merged 5 commits into
mainfrom
dev
Sep 1, 2026
Merged

release: promote OAuth consent error handling and automated test teardown to main#27
JOY (JOY) merged 5 commits into
mainfrom
dev

Conversation

@JOY

@JOY JOY (JOY) commented Sep 1, 2026

Copy link
Copy Markdown

What kind of change does this PR introduce?

Release & Reliability Improvements

Why was this change needed?

Promotes verified dev branch fixes and tooling improvements to main:

  1. OAuth Consent Error Handling: Preserves logout state and provides clean UI feedback on failed, superseded, or rejected first-party consent requests.
  2. Integration Test Lifecycle Automation: Added scripts/test-integration.ps1 with disposable container lifecycle management (starts isolated PostgreSQL & Redis test fixtures, executes Jest bootstrap suite, and guarantees automatic teardown via finally block).

Technical Details & Scope

  • apps/frontend/src/components/layout/: Handled unauthorized consent states and error messaging.
  • tests/: Added integration tests for consent error scenarios.
  • scripts/test-integration.ps1: Automated isolated test runner with auto-teardown.

Verification & Testing

  • scripts/validate-beta-compose.mjs passed.
  • Branding Guard passed 100%.
  • Extension build verified.
  • Live health checks on Production and Beta returning HTTP 200 OK.

QA

  1. Checkout main and run pnpm install and pnpm run build.
  2. Verify GET https://post.crove.com/api/health and GET https://beta-post.crove.com/api/health return HTTP 200 OK.
  3. Run .\scripts\test-integration.ps1 and verify all tests pass and test containers are cleanly removed.

Checklist:

  • My code follows the project's code style and architectural conventions.
  • Local build passes (pnpm run build).
  • Branding guard validation passes (pnpm dlx tsx scripts/branding-guard.ts).
  • Tests and typecheck have been verified without errors.
  • Documentation has been updated (if applicable).
  • No secrets or sensitive credentials are included in this PR.
  • I have filled in the QA / Verification section above with real steps to verify this change.

Note

Medium Risk
Changes global fetch response handling for 401 and logout headers; scope is narrow (OAuth authorize POST) but touches session cookie clearing and redirects.

Overview
OAuth consent flows no longer get swept into the app-wide 401/logout redirect when a POST to /oauth/authorize returns 401. New helpers in oauth-consent-unauthorized.ts drive an early exit in the layout fetch afterRequest hook and gate logout-header handling so the consent UI can show errors instead of clearing cookies and sending users to / or launches.

Tests add unit coverage for those URL/method/status rules alongside existing consent error UI tests.

Dev tooling adds scripts/test-integration.ps1, which spins up disposable Postgres/Redis containers, runs Prisma db push and the Jest bootstrap suite, and tears containers down in a finally block (optional -KeepContainers).

Reviewed by Cursor Bugbot for commit 5a0b5bc. Configure here.

@cursor

cursor Bot commented Sep 1, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_442163c1-0135-485d-a4e0-3bafa97b1fc2)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces helper functions to manage OAuth consent unauthorized responses and global logout logic, integrating them into the layout context, along with a new PowerShell script for automated integration testing using Docker. The review feedback highlights potential TypeError crashes in the layout context due to unsafe access to options.method when options is undefined, suggesting the use of optional chaining. Additionally, it recommends checking $LASTEXITCODE after starting Docker containers in the PowerShell script to ensure fast failures if container initialization fails.

Comment on lines +49 to +57
if (
shouldPreserveOAuthConsentUnauthorized(
url,
options.method,
response.status
)
) {
return true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The options parameter in afterRequest can be undefined or null if the fetch wrapper is called without options (e.g., for simple GET requests). Accessing options.method directly will throw a TypeError: Cannot read properties of undefined (reading 'method') and crash the application layout. Use optional chaining (options?.method) to safely access the method.

Suggested change
if (
shouldPreserveOAuthConsentUnauthorized(
url,
options.method,
response.status
)
) {
return true;
}
if (
shouldPreserveOAuthConsentUnauthorized(
url,
options?.method,
response.status
)
) {
return true;
}

Comment on lines +67 to +75
if (
shouldHandleGlobalLogout(
url,
options.method,
response.status,
Boolean(logout)
) &&
!isSecured
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Accessing options.method directly can throw a TypeError if options is undefined. Use optional chaining (options?.method) to ensure safe property access.

Suggested change
if (
shouldHandleGlobalLogout(
url,
options.method,
response.status,
Boolean(logout)
) &&
!isSecured
) {
if (
shouldHandleGlobalLogout(
url,
options?.method,
response.status,
Boolean(logout)
) &&
!isSecured
) {

Comment on lines +104 to +112
if (
response.status === 401 ||
shouldHandleGlobalLogout(
url,
options.method,
response.status,
Boolean(logout)
)
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Accessing options.method directly can throw a TypeError if options is undefined. Use optional chaining (options?.method) to ensure safe property access.

Suggested change
if (
response.status === 401 ||
shouldHandleGlobalLogout(
url,
options.method,
response.status,
Boolean(logout)
)
) {
if (
response.status === 401 ||
shouldHandleGlobalLogout(
url,
options?.method,
response.status,
Boolean(logout)
)
) {

Comment on lines +36 to +37
docker run -d --name $PgContainer -p "127.0.0.1:${PgPort}:5432" -e POSTGRES_PASSWORD=postiz-password -e POSTGRES_USER=postiz-user -e POSTGRES_DB=postiz-db-local postgres:17-alpine | Out-Null
docker run -d --name $RedisContainer -p "127.0.0.1:${RedisPort}:6379" redis:7.2 | Out-Null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

In PowerShell, external commands (like docker run) do not throw script-terminating errors when they fail, even with $ErrorActionPreference = "Stop". If docker run fails (e.g., due to port conflicts or Docker daemon not running), the script will continue and wait 30 seconds for PostgreSQL to become ready, leading to a slow and confusing failure. Check $LASTEXITCODE immediately after each docker run command to fail fast.

    docker run -d --name $PgContainer -p "127.0.0.1:${PgPort}:5432" -e POSTGRES_PASSWORD=postiz-password -e POSTGRES_USER=postiz-user -e POSTGRES_DB=postiz-db-local postgres:17-alpine | Out-Null
    if ($LASTEXITCODE -ne 0) { throw "Failed to start PostgreSQL container." }
    docker run -d --name $RedisContainer -p "127.0.0.1:${RedisPort}:6379" redis:7.2 | Out-Null
    if ($LASTEXITCODE -ne 0) { throw "Failed to start Redis container." }

@JOY
JOY (JOY) merged commit faa19bc into main Sep 1, 2026
15 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.

1 participant