




















@@ -582,6 +582,60 @@ async function withAgentWorkspace<T>(
582582}
583583}
584584585+function requireRecord(value: unknown, label: string): Record<string, unknown> {
586+if (value === null || typeof value !== "object" || Array.isArray(value)) {
587+throw new Error(`expected ${label} to be a record`);
588+}
589+return value as Record<string, unknown>;
590+}
591+592+function requireLogRecord(
593+records: ReadonlyArray<unknown>,
594+message: string,
595+): Record<string, unknown> {
596+const record = records.find(
597+(candidate) => requireRecord(candidate, "log record").message === message,
598+);
599+if (!record) {
600+throw new Error(`expected log record: ${message}`);
601+}
602+return requireRecord(record, message);
603+}
604+605+async function expectFailoverError(
606+promise: Promise<unknown>,
607+expected: {
608+name?: string;
609+profileId?: string;
610+reason?: string;
611+provider?: string;
612+model?: string;
613+},
614+) {
615+let thrown: unknown;
616+try {
617+await promise;
618+} catch (error) {
619+thrown = error;
620+}
621+expect(thrown).toBeInstanceOf(Error);
622+const errorRecord = requireRecord(thrown, "failover error");
623+expect(errorRecord.name).toBe(expected.name ?? "FailoverError");
624+if (expected.profileId !== undefined) {
625+expect(errorRecord.profileId).toBe(expected.profileId);
626+}
627+if (expected.reason !== undefined) {
628+expect(errorRecord.reason).toBe(expected.reason);
629+}
630+if (expected.provider !== undefined) {
631+expect(errorRecord.provider).toBe(expected.provider);
632+}
633+if (expected.model !== undefined) {
634+expect(errorRecord.model).toBe(expected.model);
635+}
636+return errorRecord;
637+}
638+585639async function runTurnWithCooldownSeed(params: {
586640sessionKey: string;
587641runId: string;
@@ -863,37 +917,33 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
863917await logCapture.flush();
864918865919const safeProfileId = redactIdentifier("openai:p1", { len: 12 });
866-expect(logCapture.records).toEqual(
867-expect.arrayContaining([
868-expect.objectContaining({
869-message: "embedded run failover decision",
870-attributes: expect.objectContaining({
871-event: "embedded_run_failover_decision",
872-runId: "run:overloaded-logging",
873-decision: "rotate_profile",
874-failoverReason: "overloaded",
875-profileId: safeProfileId,
876-sourceProvider: "openai",
877-sourceModel: "mock-1",
878-providerErrorType: "overloaded_error",
879-rawErrorPreview: expect.stringContaining('"request_id":"sha256:'),
880-}),
881-}),
882-]),
920+const failoverDecision = requireLogRecord(logCapture.records, "embedded run failover decision");
921+const failoverAttributes = requireRecord(
922+failoverDecision.attributes,
923+"failover decision attributes",
883924);
884-expect(logCapture.records).toEqual(
885-expect.arrayContaining([
886-expect.objectContaining({
887-message: "auth profile failure state updated",
888-attributes: expect.objectContaining({
889-event: "auth_profile_failure_state_updated",
890-runId: "run:overloaded-logging",
891-profileId: safeProfileId,
892-reason: "overloaded",
893-}),
894-}),
895-]),
925+expect(failoverAttributes.event).toBe("embedded_run_failover_decision");
926+expect(failoverAttributes.runId).toBe("run:overloaded-logging");
927+expect(failoverAttributes.decision).toBe("rotate_profile");
928+expect(failoverAttributes.failoverReason).toBe("overloaded");
929+expect(failoverAttributes.profileId).toBe(safeProfileId);
930+expect(failoverAttributes.sourceProvider).toBe("openai");
931+expect(failoverAttributes.sourceModel).toBe("mock-1");
932+expect(failoverAttributes.providerErrorType).toBe("overloaded_error");
933+expect(failoverAttributes.rawErrorPreview).toContain('"request_id":"sha256:');
934+935+const failureStateUpdate = requireLogRecord(
936+logCapture.records,
937+"auth profile failure state updated",
896938);
939+const failureStateAttributes = requireRecord(
940+failureStateUpdate.attributes,
941+"failure state attributes",
942+);
943+expect(failureStateAttributes.event).toBe("auth_profile_failure_state_updated");
944+expect(failureStateAttributes.runId).toBe("run:overloaded-logging");
945+expect(failureStateAttributes.profileId).toBe(safeProfileId);
946+expect(failureStateAttributes.reason).toBe("overloaded");
897947});
898948899949it("rotates for overloaded prompt failures across auto-pinned profiles", async () => {
@@ -908,7 +958,7 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
908958expect(sleepWithAbortMock).not.toHaveBeenCalled();
909959});
910960911-it("starts the retry attempt before prompt failure cooldown marking finishes", async () => {
961+it("waits for prompt failure cooldown marking before retrying", async () => {
912962let releaseMark: (() => void) | undefined;
913963const markCanFinish = new Promise<void>((resolve) => {
914964releaseMark = resolve;
@@ -934,10 +984,11 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
934984runId: "run:prompt-deferred-mark",
935985});
936986937-await vi.waitFor(() => expect(runEmbeddedAttemptMock).toHaveBeenCalledTimes(2));
938-expect(markStarted).toBe(true);
987+await vi.waitFor(() => expect(markStarted).toBe(true));
988+expect(runEmbeddedAttemptMock).toHaveBeenCalledTimes(1);
939989releaseMark?.();
940990releaseMark = undefined;
991+await vi.waitFor(() => expect(runEmbeddedAttemptMock).toHaveBeenCalledTimes(2));
941992await runPromise;
942993943994const usageStats = await readUsageStats(agentDir);
@@ -1067,7 +1118,7 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
1067111810681119mockSingleErrorAttempt({ errorMessage: "rate limit" });
106911201070-await expect(
1121+await expectFailoverError(
10711122runEmbeddedPiAgentInline({
10721123sessionId: "session:test",
10731124sessionKey: "agent:test:user",
@@ -1083,13 +1134,13 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
10831134timeoutMs: 5_000,
10841135runId: "run:user",
10851136}),
1086-).rejects.toMatchObject({
1087-name: "FailoverError",
1088-profileId: "openai:p1",
1089-reason: "rate_limit",
1090-provider: "openai",
1091-model: "mock-1",
1092-});
1137+ {
1138+ profileId: "openai:p1",
1139+ reason: "rate_limit",
1140+ provider: "openai",
1141+ model: "mock-1",
1142+},
1143+);
1093114410941145expect(runEmbeddedAttemptMock).toHaveBeenCalledTimes(1);
10951146await expectProfileP2UsageUnchanged(agentDir);
@@ -1164,11 +1215,13 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
11641215});
1165121611661217expect(runEmbeddedAttemptMock).toHaveBeenCalledTimes(1);
1167-expect(runEmbeddedAttemptMock.mock.calls[0]?.[0]).toMatchObject({
1168-authProfileId: "openai-codex:work",
1169-authProfileIdSource: "user",
1170-provider: "codex-cli",
1171-});
1218+const attemptParams = requireRecord(
1219+runEmbeddedAttemptMock.mock.calls[0]?.[0],
1220+"embedded attempt params",
1221+);
1222+expect(attemptParams.authProfileId).toBe("openai-codex:work");
1223+expect(attemptParams.authProfileIdSource).toBe("user");
1224+expect(attemptParams.provider).toBe("codex-cli");
11721225});
11731226});
11741227@@ -1227,7 +1280,7 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
12271280},
12281281});
122912821230-await expect(
1283+await expectFailoverError(
12311284runEmbeddedPiAgentInline({
12321285sessionId: "session:test",
12331286sessionKey: "agent:test:cooldown-failover",
@@ -1242,12 +1295,12 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
12421295timeoutMs: 5_000,
12431296runId: "run:cooldown-failover",
12441297}),
1245-).rejects.toMatchObject({
1246-name: "FailoverError",
1247-reason: "unknown",
1248-provider: "openai",
1249-model: "mock-1",
1250-});
1298+ {
1299+ reason: "unknown",
1300+ provider: "openai",
1301+ model: "mock-1",
1302+},
1303+);
1251130412521305expect(runEmbeddedAttemptMock).not.toHaveBeenCalled();
12531306});
@@ -1398,7 +1451,7 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
13981451},
13991452});
140014531401-await expect(
1454+await expectFailoverError(
14021455runEmbeddedPiAgentInline({
14031456sessionId: "session:test",
14041457sessionKey: "agent:support:cooldown-failover",
@@ -1414,12 +1467,12 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
14141467runId: "run:agent-override-fallback",
14151468agentId: "support",
14161469}),
1417-).rejects.toMatchObject({
1418-name: "FailoverError",
1419-reason: "unknown",
1420-provider: "openai",
1421-model: "mock-1",
1422-});
1470+ {
1471+ reason: "unknown",
1472+ provider: "openai",
1473+ model: "mock-1",
1474+},
1475+);
1423147614241477expect(runEmbeddedAttemptMock).not.toHaveBeenCalled();
14251478});
@@ -1443,7 +1496,7 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
14431496},
14441497});
144514981446-await expect(
1499+await expectFailoverError(
14471500runEmbeddedPiAgentInline({
14481501sessionId: "session:test",
14491502sessionKey: "agent:test:disabled-failover",
@@ -1458,12 +1511,12 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
14581511timeoutMs: 5_000,
14591512runId: "run:disabled-failover",
14601513}),
1461-).rejects.toMatchObject({
1462-name: "FailoverError",
1463-reason: "billing",
1464-provider: "openai",
1465-model: "mock-1",
1466-});
1514+ {
1515+ reason: "billing",
1516+ provider: "openai",
1517+ model: "mock-1",
1518+},
1519+);
1467152014681521expect(runEmbeddedAttemptMock).not.toHaveBeenCalled();
14691522});
@@ -1479,7 +1532,7 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
14791532await fs.writeFile(authPath, JSON.stringify({ version: 1, profiles: {} }));
14801533await fs.writeFile(authStatePath, JSON.stringify({ version: 1, usageStats: {} }));
148115341482-await expect(
1535+await expectFailoverError(
14831536runEmbeddedPiAgentInline({
14841537sessionId: "session:test",
14851538sessionKey: "agent:test:auth-unavailable",
@@ -1494,7 +1547,8 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
14941547timeoutMs: 5_000,
14951548runId: "run:auth-unavailable",
14961549}),
1497-).rejects.toMatchObject({ name: "FailoverError", reason: "auth" });
1550+{ reason: "auth" },
1551+);
1498155214991553expect(runEmbeddedAttemptMock).not.toHaveBeenCalled();
15001554});
@@ -1536,13 +1590,11 @@ describe("runEmbeddedPiAgent auth profile rotation", () => {
15361590} catch (err) {
15371591thrown = err;
15381592}
1539-1540-expect(thrown).toMatchObject({
1541-name: "FailoverError",
1542-reason: "billing",
1543-provider: "openai",
1544-model: "mock-rotated",
1545-});
1593+const errorRecord = requireRecord(thrown, "billing failover error");
1594+expect(errorRecord.name).toBe("FailoverError");
1595+expect(errorRecord.reason).toBe("billing");
1596+expect(errorRecord.provider).toBe("openai");
1597+expect(errorRecord.model).toBe("mock-rotated");
15461598expect(thrown).toBeInstanceOf(Error);
15471599expect((thrown as Error).message).toContain("openai (mock-rotated) returned a billing error");
15481600expect(runEmbeddedAttemptMock).toHaveBeenCalledTimes(1);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。