test: await completed ChatGPT sign-in and consent saves

Replace arbitrary event-loop budgets in two release-gate tests with fresh per-attempt sign-in notifications and the actual consent control reply, bounded by a clear watchdog. Retain all final-state assertions and explicitly verify successful connected/error-free completion.

Controlled token/save gates demonstrate both old 100-turn waits return before completion; the corrected waits remain pending and retain the final persistence/UI assertions after release. Add small watchdog result/error/timeout tests. No production, workflow or dependency changes.

Isolated checks: focused 87/87, full suite once 2517/2517, full ESLint and typecheck. The full run preceded three runtime-erased non-null array-index annotations; focused tests/typecheck/full ESLint passed again on final source.
This commit is contained in:
amosblomqvist
2026-10-27 23:44:48 +02:00
parent b577ec5453
commit 4f4b5643d4
4 changed files with 109 additions and 15 deletions
+39 -8
View File
@@ -6,6 +6,8 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { exportJWK, generateKeyPair, SignJWT } from "jose";
import type { ChatGPTStatus } from "../src/core/contracts/chatgpt.ts";
import { completedWithin } from "./completion.ts";
const require = createRequire(import.meta.url);
const { createChatGPT, fileStore, allowedAuthEndpoint, SCOPES } = require("../electron/chatgpt.cjs");
const { privateKey, publicKey } = await generateKeyPair("RS256");
@@ -611,10 +613,17 @@ test("disconnect persistence failure is not reported as local forgetting and lea
} finally { f.service.close(); }
});
test("one app-wide background preference defaults ON over legacy OFF, persists with zero accounts, and never changes on auth/account operations", async () => {
for (const holdToken of [false, true]) test(`one app-wide background preference defaults ON over legacy OFF, persists with zero accounts, and never changes on auth/account operations${holdToken ? " (completion held beyond old wait budget)" : ""}`, async () => {
const first = await loggedIn(); first.service.close();
const legacy = structuredClone(first.saved); legacy.registrations[0].background = false;
const f = fixture({ state: legacy });
const tokenEntered = deferred(), releaseToken = deferred();
let completion = deferred<ChatGPTStatus>();
const change = ({ status }: { status: ChatGPTStatus }) => { if (!status.pending) completion.resolve(status); };
const f = fixture({ state: legacy, change, token: async (form, url) => {
tokenEntered.resolve();
if (holdToken) await releaseToken.promise;
return Response.json({ access_token: "scratch-oauth-token", refresh_token: "scratch-refresh", id_token: await signed(form.get("client_id")!, url.searchParams.get("nonce")!), token_type: "Bearer", expires_in: 3600, scope: SCOPES.join(" ") });
} });
try {
assert.equal((await f.service.status()).allowBackgroundUse, true, "absent new setting ignores legacy per-account OFF");
await f.service.lease(true);
@@ -624,21 +633,43 @@ test("one app-wide background preference defaults ON over legacy OFF, persists w
await f.service.lease(false);
const id = f.saved.activeId;
await f.service.select(id); assert.equal((await f.service.status()).allowBackgroundUse, false);
await f.service.login(id); await f.callback();
for (let i = 0; i < 100 && (await f.service.status()).pending; i++) await new Promise((r) => setImmediate(r));
// A fresh signal per attempt: the fixture's original changed promise
// was already resolved by earlier account/consent notifications.
completion = deferred<ChatGPTStatus>();
await f.service.login(id);
const response = await f.callback(); assert.equal(response.status, 200); await response.text();
const waiting = completedWithin(completion.promise, "returning verified sign-in");
let settled = false; void waiting.then(() => { settled = true; }, () => { settled = true; });
if (holdToken) {
await completedWithin(tokenEntered.promise, "held token request");
// Regression only: exhaust the OLD wait while real completion is
// explicitly held, rather than relying on machine speed or sleeps.
for (let i = 0; i < 100 && (await f.service.status()).pending; i++) await new Promise((r) => setImmediate(r));
assert.equal((await f.service.status()).pending, true);
assert.equal(f.saved.registrations[0].background, false, "the old assertion would run before replacement was saved");
assert.equal(settled, false, "the corrected wait must still await completion");
releaseToken.resolve();
}
const status = await waiting;
assert.equal(status.pending, false); assert.equal(status.error, null, "reconnecting must actually succeed");
assert.equal(status.activeId, id); assert.equal(status.accounts[0]!.connected, true);
assert.equal(f.saved.allowBackgroundUse, false, "reconnecting cannot overwrite app preference");
assert.equal(f.saved.registrations[0].background, undefined, "new verified registrations omit obsolete per-account preference");
await f.service.disconnect(f.saved.activeId);
assert.deepEqual(f.saved.registrations, []); assert.equal(f.saved.allowBackgroundUse, false);
const restarted = fixture({ state: f.saved });
const restarted = fixture({ state: f.saved, change });
try {
assert.equal((await restarted.service.status()).allowBackgroundUse, false, "zero-account restart preserves global OFF");
await restarted.service.login(); await restarted.callback();
for (let i = 0; i < 100 && (await restarted.service.status()).pending; i++) await new Promise((r) => setImmediate(r));
completion = deferred<ChatGPTStatus>();
await restarted.service.login();
const response = await restarted.callback(); assert.equal(response.status, 200); await response.text();
const status = await completedWithin(completion.promise, "verified sign-in after zero-account restart");
assert.equal(status.pending, false); assert.equal(status.error, null);
assert.notEqual(status.activeId, null); assert.equal(status.accounts[0]!.connected, true);
assert.equal(restarted.saved.allowBackgroundUse, false);
await restarted.service.consent(true); assert.equal(restarted.saved.allowBackgroundUse, true);
} finally { restarted.service.close(); }
} finally { f.service.close(); }
} finally { releaseToken.resolve(); f.service.close(); }
});
test("private leases bind active account, authority revision and raw visible model membership", async () => {