85453da49f
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import { startLoopback, type LoopbackHandle } from "./loopback.js";
|
|
|
|
describe("auth/loopback", () => {
|
|
let active: LoopbackHandle | null = null;
|
|
|
|
afterEach(async () => {
|
|
if (active) {
|
|
await active.close().catch(() => {});
|
|
active = null;
|
|
}
|
|
});
|
|
|
|
it("captures `code` when state matches", async () => {
|
|
const handle = await startLoopback({ state: "expected_state", timeoutMs: 5_000 });
|
|
active = handle;
|
|
const redirect = new URL(handle.redirectUri);
|
|
const callback = new URL(`${handle.redirectUri}?code=abc123&state=expected_state`);
|
|
|
|
const fetchPromise = fetch(callback.toString());
|
|
const result = await handle.result;
|
|
const res = await fetchPromise;
|
|
|
|
expect(result.code).toBe("abc123");
|
|
expect(result.redirectUri).toContain(redirect.host);
|
|
expect(res.status).toBe(200);
|
|
const body = await res.text();
|
|
expect(body).toContain("Signed in");
|
|
});
|
|
|
|
async function expectRejection(args: {
|
|
expectedState: string;
|
|
query: string;
|
|
pattern: RegExp;
|
|
}): Promise<void> {
|
|
const handle = await startLoopback({ state: args.expectedState, timeoutMs: 5_000 });
|
|
active = handle;
|
|
await fetch(`${handle.redirectUri}?${args.query}`).catch(() => {});
|
|
await expect(handle.result).rejects.toThrow(args.pattern);
|
|
}
|
|
|
|
it("rejects when state does not match", async () => {
|
|
await expectRejection({
|
|
expectedState: "expected",
|
|
query: "code=abc&state=wrong",
|
|
pattern: /state mismatch/i,
|
|
});
|
|
});
|
|
|
|
it("rejects when the IdP returns an error", async () => {
|
|
await expectRejection({
|
|
expectedState: "s",
|
|
query: "error=access_denied&error_description=user+denied&state=s",
|
|
pattern: /access_denied/,
|
|
});
|
|
});
|
|
|
|
it("rejects when code is missing from the callback", async () => {
|
|
await expectRejection({ expectedState: "s", query: "state=s", pattern: /code/ });
|
|
});
|
|
|
|
it("times out when no callback arrives", async () => {
|
|
const handle = await startLoopback({ state: "s", timeoutMs: 200 });
|
|
active = handle;
|
|
await expect(handle.result).rejects.toThrow(/timed out/i);
|
|
});
|
|
|
|
it("404s non-callback paths and does not resolve the flow", async () => {
|
|
const handle = await startLoopback({ state: "s", timeoutMs: 1_000 });
|
|
active = handle;
|
|
const res = await fetch(`${handle.redirectUri.replace("/oauth/callback", "/other")}`);
|
|
expect(res.status).toBe(404);
|
|
// Flow is still waiting — kill it via timeout.
|
|
await expect(handle.result).rejects.toThrow(/timed out/i);
|
|
});
|
|
});
|