@vacbo/opencode-anthropic-fix 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -88
- package/dist/opencode-anthropic-auth-cli.mjs +804 -507
- package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
- package/package.json +67 -59
- package/src/__tests__/billing-edge-cases.test.ts +59 -59
- package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
- package/src/__tests__/cc-comparison.test.ts +87 -87
- package/src/__tests__/cc-credentials.test.ts +254 -250
- package/src/__tests__/cch-drift-checker.test.ts +51 -51
- package/src/__tests__/cch-native-style.test.ts +56 -56
- package/src/__tests__/debug-gating.test.ts +42 -42
- package/src/__tests__/decomposition-smoke.test.ts +68 -68
- package/src/__tests__/fingerprint-regression.test.ts +575 -566
- package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
- package/src/__tests__/helpers/conversation-history.ts +119 -119
- package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
- package/src/__tests__/helpers/deferred.ts +69 -69
- package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
- package/src/__tests__/helpers/in-memory-storage.ts +88 -88
- package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
- package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
- package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
- package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
- package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
- package/src/__tests__/helpers/sse.ts +209 -209
- package/src/__tests__/index.parallel.test.ts +605 -595
- package/src/__tests__/sanitization-regex.test.ts +112 -112
- package/src/__tests__/state-bounds.test.ts +90 -90
- package/src/account-identity.test.ts +197 -192
- package/src/account-identity.ts +69 -67
- package/src/account-state.test.ts +86 -86
- package/src/account-state.ts +25 -25
- package/src/accounts/matching.test.ts +335 -0
- package/src/accounts/matching.ts +167 -0
- package/src/accounts/persistence.test.ts +345 -0
- package/src/accounts/persistence.ts +432 -0
- package/src/accounts/repair.test.ts +276 -0
- package/src/accounts/repair.ts +407 -0
- package/src/accounts.dedup.test.ts +621 -621
- package/src/accounts.test.ts +933 -929
- package/src/accounts.ts +633 -989
- package/src/backoff.test.ts +345 -345
- package/src/backoff.ts +219 -219
- package/src/betas.ts +124 -124
- package/src/bun-fetch.test.ts +345 -342
- package/src/bun-fetch.ts +424 -424
- package/src/bun-proxy.test.ts +25 -25
- package/src/bun-proxy.ts +209 -209
- package/src/cc-credentials.ts +111 -111
- package/src/circuit-breaker.test.ts +184 -184
- package/src/circuit-breaker.ts +169 -169
- package/src/cli/commands/auth.ts +963 -0
- package/src/cli/commands/config.ts +547 -0
- package/src/cli/formatting.test.ts +406 -0
- package/src/cli/formatting.ts +219 -0
- package/src/cli.ts +255 -2022
- package/src/commands/handlers/betas.ts +100 -0
- package/src/commands/handlers/config.ts +99 -0
- package/src/commands/handlers/files.ts +375 -0
- package/src/commands/oauth-flow.ts +181 -166
- package/src/commands/prompts.ts +61 -61
- package/src/commands/router.test.ts +421 -0
- package/src/commands/router.ts +143 -635
- package/src/config.test.ts +482 -482
- package/src/config.ts +412 -404
- package/src/constants.ts +48 -48
- package/src/drift/cch-constants.ts +95 -95
- package/src/env.ts +111 -105
- package/src/headers/billing.ts +33 -33
- package/src/headers/builder.ts +130 -130
- package/src/headers/cch.ts +75 -75
- package/src/headers/stainless.ts +25 -25
- package/src/headers/user-agent.ts +23 -23
- package/src/index.ts +436 -828
- package/src/models.ts +27 -27
- package/src/oauth.test.ts +102 -102
- package/src/oauth.ts +178 -178
- package/src/parent-pid-watcher.test.ts +148 -148
- package/src/parent-pid-watcher.ts +69 -69
- package/src/plugin-helpers.ts +82 -82
- package/src/refresh-helpers.ts +145 -139
- package/src/refresh-lock.test.ts +94 -94
- package/src/refresh-lock.ts +93 -93
- package/src/request/body.history.test.ts +579 -571
- package/src/request/body.ts +255 -255
- package/src/request/metadata.ts +65 -65
- package/src/request/retry.test.ts +156 -156
- package/src/request/retry.ts +67 -67
- package/src/request/url.ts +21 -21
- package/src/request-orchestration-helpers.ts +648 -0
- package/src/response/index.ts +5 -5
- package/src/response/mcp.ts +58 -58
- package/src/response/streaming.test.ts +313 -311
- package/src/response/streaming.ts +412 -410
- package/src/rotation.test.ts +304 -301
- package/src/rotation.ts +205 -205
- package/src/storage.test.ts +547 -547
- package/src/storage.ts +315 -291
- package/src/system-prompt/builder.ts +38 -38
- package/src/system-prompt/index.ts +5 -5
- package/src/system-prompt/normalize.ts +60 -60
- package/src/system-prompt/sanitize.ts +30 -30
- package/src/thinking.ts +21 -20
- package/src/token-refresh.test.ts +265 -265
- package/src/token-refresh.ts +219 -214
- package/src/types.ts +30 -30
- package/dist/bun-proxy.mjs +0 -291
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
export interface Deferred<T> {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
/** The promise that will resolve/reject when called */
|
|
17
|
+
promise: Promise<T>;
|
|
18
|
+
/** Resolve the promise with a value */
|
|
19
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
20
|
+
/** Reject the promise with a reason */
|
|
21
|
+
reject: (reason?: unknown) => void;
|
|
22
|
+
/** Whether the promise has settled (resolved or rejected) */
|
|
23
|
+
settled: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
@@ -31,44 +31,44 @@ export interface Deferred<T> {
|
|
|
31
31
|
* @returns Deferred object with promise, resolve, reject, and settled state
|
|
32
32
|
*/
|
|
33
33
|
export function createDeferred<T>(): Deferred<T> {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
let resolve: (value: T | PromiseLike<T>) => void;
|
|
35
|
+
let reject: (reason?: unknown) => void;
|
|
36
|
+
let settled = false;
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
const promise = new Promise<T>((res, rej) => {
|
|
39
|
+
resolve = (value) => {
|
|
40
|
+
if (!settled) {
|
|
41
|
+
settled = true;
|
|
42
|
+
res(value);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
reject = (reason) => {
|
|
46
|
+
if (!settled) {
|
|
47
|
+
settled = true;
|
|
48
|
+
rej(reason);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
return {
|
|
54
|
+
promise,
|
|
55
|
+
resolve: resolve!,
|
|
56
|
+
reject: reject!,
|
|
57
|
+
get settled() {
|
|
58
|
+
return settled;
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export interface DeferredQueue<T> {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
/** Add a new deferred to the queue */
|
|
65
|
+
enqueue: () => Deferred<T>;
|
|
66
|
+
/** Resolve the next deferred in FIFO order */
|
|
67
|
+
resolveNext: (value: T | PromiseLike<T>) => boolean;
|
|
68
|
+
/** Reject the next deferred in FIFO order */
|
|
69
|
+
rejectNext: (reason?: unknown) => boolean;
|
|
70
|
+
/** Number of pending deferreds in the queue */
|
|
71
|
+
pending: number;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
@@ -80,34 +80,34 @@ export interface DeferredQueue<T> {
|
|
|
80
80
|
* @returns Queue with enqueue, resolveNext, rejectNext, and pending count
|
|
81
81
|
*/
|
|
82
82
|
export function createDeferredQueue<T>(): DeferredQueue<T> {
|
|
83
|
-
|
|
83
|
+
const queue: Deferred<T>[] = [];
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
85
|
+
return {
|
|
86
|
+
enqueue: () => {
|
|
87
|
+
const deferred = createDeferred<T>();
|
|
88
|
+
queue.push(deferred);
|
|
89
|
+
return deferred;
|
|
90
|
+
},
|
|
91
|
+
resolveNext: (value) => {
|
|
92
|
+
const next = queue.shift();
|
|
93
|
+
if (next) {
|
|
94
|
+
next.resolve(value);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
},
|
|
99
|
+
rejectNext: (reason) => {
|
|
100
|
+
const next = queue.shift();
|
|
101
|
+
if (next) {
|
|
102
|
+
next.reject(reason);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
},
|
|
107
|
+
get pending() {
|
|
108
|
+
return queue.length;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
@@ -118,5 +118,5 @@ export function createDeferredQueue<T>(): DeferredQueue<T> {
|
|
|
118
118
|
* @returns Promise that resolves after one microtask
|
|
119
119
|
*/
|
|
120
120
|
export function nextTick(): Promise<void> {
|
|
121
|
-
|
|
121
|
+
return Promise.resolve();
|
|
122
122
|
}
|
|
@@ -2,165 +2,165 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { createInMemoryStorage, makeAccountsData, makeStoredAccount } from "./in-memory-storage.js";
|
|
3
3
|
|
|
4
4
|
describe("in-memory-storage smoke tests", () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("snapshot returns deep copy - mutations don't affect storage", () => {
|
|
38
|
-
const initial = makeAccountsData([{ refreshToken: "tok1", enabled: true }]);
|
|
39
|
-
const storage = createInMemoryStorage(initial);
|
|
40
|
-
|
|
41
|
-
const snap = storage.snapshot();
|
|
42
|
-
snap.accounts[0].enabled = false;
|
|
43
|
-
snap.accounts[0].refreshToken = "mutated";
|
|
44
|
-
|
|
45
|
-
// Storage should be unchanged
|
|
46
|
-
const snap2 = storage.snapshot();
|
|
47
|
-
expect(snap2.accounts[0].enabled).toBe(true);
|
|
48
|
-
expect(snap2.accounts[0].refreshToken).toBe("tok1");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("saveAccountsMock writes to disk state", async () => {
|
|
52
|
-
const storage = createInMemoryStorage();
|
|
53
|
-
const data = makeAccountsData([{ refreshToken: "tok1" }]);
|
|
54
|
-
|
|
55
|
-
// Save via mock
|
|
56
|
-
await storage.saveAccountsMock(data);
|
|
57
|
-
|
|
58
|
-
// Should be readable via loadAccountsMock
|
|
59
|
-
const loaded = await storage.loadAccountsMock();
|
|
60
|
-
expect(loaded).toEqual(data);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("mutateDiskOnly changes disk without affecting caller's snapshot", () => {
|
|
64
|
-
const initial = makeAccountsData([{ refreshToken: "tok1", enabled: true }]);
|
|
65
|
-
const storage = createInMemoryStorage(initial);
|
|
66
|
-
|
|
67
|
-
// Get current snapshot (what test subject holds)
|
|
68
|
-
const beforeSnapshot = storage.snapshot();
|
|
69
|
-
expect(beforeSnapshot.accounts[0].enabled).toBe(true);
|
|
70
|
-
|
|
71
|
-
// Simulate another process writing to disk
|
|
72
|
-
storage.mutateDiskOnly((disk) => ({
|
|
73
|
-
...disk,
|
|
74
|
-
accounts: disk.accounts.map((a) => ({ ...a, enabled: false })),
|
|
75
|
-
}));
|
|
76
|
-
|
|
77
|
-
// Caller's snapshot should be unchanged
|
|
78
|
-
const afterSnapshot = storage.snapshot();
|
|
79
|
-
expect(afterSnapshot.accounts[0].enabled).toBe(true);
|
|
80
|
-
|
|
81
|
-
// But disk state should be changed
|
|
82
|
-
expect(afterSnapshot.accounts[0].enabled).toBe(true); // Still true in memory
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("mutateDiskOnly affects subsequent loadAccountsMock calls", async () => {
|
|
86
|
-
const initial = makeAccountsData([{ refreshToken: "tok1", enabled: true }]);
|
|
87
|
-
const storage = createInMemoryStorage(initial);
|
|
88
|
-
|
|
89
|
-
// Mutate disk
|
|
90
|
-
storage.mutateDiskOnly((disk) => ({
|
|
91
|
-
...disk,
|
|
92
|
-
accounts: disk.accounts.map((a) => ({ ...a, enabled: false })),
|
|
93
|
-
}));
|
|
94
|
-
|
|
95
|
-
// Load should see the mutated state
|
|
96
|
-
const loaded = await storage.loadAccountsMock();
|
|
97
|
-
expect(loaded?.accounts[0].enabled).toBe(false);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("mutateDiskOnly throws when disk state is null", () => {
|
|
101
|
-
const storage = createInMemoryStorage();
|
|
102
|
-
|
|
103
|
-
expect(() =>
|
|
104
|
-
storage.mutateDiskOnly((disk) => ({
|
|
105
|
-
...disk,
|
|
106
|
-
accounts: [],
|
|
107
|
-
})),
|
|
108
|
-
).toThrow("Cannot mutate disk - disk state is null");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("makeStoredAccount creates valid account with defaults", () => {
|
|
112
|
-
const account = makeStoredAccount({ refreshToken: "my-token" });
|
|
113
|
-
|
|
114
|
-
expect(account.refreshToken).toBe("my-token");
|
|
115
|
-
expect(account.id).toMatch(/^acct-/);
|
|
116
|
-
expect(account.enabled).toBe(true);
|
|
117
|
-
expect(account.consecutiveFailures).toBe(0);
|
|
118
|
-
expect(account.lastFailureTime).toBeNull();
|
|
119
|
-
expect(account.stats.requests).toBe(0);
|
|
120
|
-
expect(account.addedAt).toBeGreaterThan(0);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("makeStoredAccount applies overrides", () => {
|
|
124
|
-
const account = makeStoredAccount({
|
|
125
|
-
refreshToken: "tok1",
|
|
126
|
-
enabled: false,
|
|
127
|
-
consecutiveFailures: 5,
|
|
128
|
-
email: "test@example.com",
|
|
5
|
+
it("createInMemoryStorage creates storage with initial data", () => {
|
|
6
|
+
const initial = makeAccountsData([{ refreshToken: "tok1" }]);
|
|
7
|
+
const storage = createInMemoryStorage(initial);
|
|
8
|
+
|
|
9
|
+
expect(storage.snapshot()).toEqual(initial);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("createInMemoryStorage creates storage with null initial state", async () => {
|
|
13
|
+
const storage = createInMemoryStorage();
|
|
14
|
+
|
|
15
|
+
// loadAccountsMock should return null
|
|
16
|
+
const loaded = await storage.loadAccountsMock();
|
|
17
|
+
expect(loaded).toBeNull();
|
|
18
|
+
|
|
19
|
+
// snapshot should throw when null
|
|
20
|
+
expect(() => storage.snapshot()).toThrow("Storage snapshot is null");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("setSnapshot updates both disk and memory state", async () => {
|
|
24
|
+
const storage = createInMemoryStorage();
|
|
25
|
+
const data = makeAccountsData([{ refreshToken: "tok1" }]);
|
|
26
|
+
|
|
27
|
+
storage.setSnapshot(data);
|
|
28
|
+
|
|
29
|
+
// Memory should match
|
|
30
|
+
expect(storage.snapshot()).toEqual(data);
|
|
31
|
+
|
|
32
|
+
// Disk should match (via loadAccountsMock)
|
|
33
|
+
const loaded = await storage.loadAccountsMock();
|
|
34
|
+
expect(loaded).toEqual(data);
|
|
129
35
|
});
|
|
130
36
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
37
|
+
it("snapshot returns deep copy - mutations don't affect storage", () => {
|
|
38
|
+
const initial = makeAccountsData([{ refreshToken: "tok1", enabled: true }]);
|
|
39
|
+
const storage = createInMemoryStorage(initial);
|
|
40
|
+
|
|
41
|
+
const snap = storage.snapshot();
|
|
42
|
+
snap.accounts[0].enabled = false;
|
|
43
|
+
snap.accounts[0].refreshToken = "mutated";
|
|
44
|
+
|
|
45
|
+
// Storage should be unchanged
|
|
46
|
+
const snap2 = storage.snapshot();
|
|
47
|
+
expect(snap2.accounts[0].enabled).toBe(true);
|
|
48
|
+
expect(snap2.accounts[0].refreshToken).toBe("tok1");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("saveAccountsMock writes to disk state", async () => {
|
|
52
|
+
const storage = createInMemoryStorage();
|
|
53
|
+
const data = makeAccountsData([{ refreshToken: "tok1" }]);
|
|
54
|
+
|
|
55
|
+
// Save via mock
|
|
56
|
+
await storage.saveAccountsMock(data);
|
|
57
|
+
|
|
58
|
+
// Should be readable via loadAccountsMock
|
|
59
|
+
const loaded = await storage.loadAccountsMock();
|
|
60
|
+
expect(loaded).toEqual(data);
|
|
155
61
|
});
|
|
156
62
|
|
|
157
|
-
|
|
158
|
-
|
|
63
|
+
it("mutateDiskOnly changes disk without affecting caller's snapshot", () => {
|
|
64
|
+
const initial = makeAccountsData([{ refreshToken: "tok1", enabled: true }]);
|
|
65
|
+
const storage = createInMemoryStorage(initial);
|
|
66
|
+
|
|
67
|
+
// Get current snapshot (what test subject holds)
|
|
68
|
+
const beforeSnapshot = storage.snapshot();
|
|
69
|
+
expect(beforeSnapshot.accounts[0].enabled).toBe(true);
|
|
70
|
+
|
|
71
|
+
// Simulate another process writing to disk
|
|
72
|
+
storage.mutateDiskOnly((disk) => ({
|
|
73
|
+
...disk,
|
|
74
|
+
accounts: disk.accounts.map((a) => ({ ...a, enabled: false })),
|
|
75
|
+
}));
|
|
159
76
|
|
|
160
|
-
|
|
161
|
-
|
|
77
|
+
// Caller's snapshot should be unchanged
|
|
78
|
+
const afterSnapshot = storage.snapshot();
|
|
79
|
+
expect(afterSnapshot.accounts[0].enabled).toBe(true);
|
|
80
|
+
|
|
81
|
+
// But disk state should be changed
|
|
82
|
+
expect(afterSnapshot.accounts[0].enabled).toBe(true); // Still true in memory
|
|
83
|
+
});
|
|
162
84
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
85
|
+
it("mutateDiskOnly affects subsequent loadAccountsMock calls", async () => {
|
|
86
|
+
const initial = makeAccountsData([{ refreshToken: "tok1", enabled: true }]);
|
|
87
|
+
const storage = createInMemoryStorage(initial);
|
|
88
|
+
|
|
89
|
+
// Mutate disk
|
|
90
|
+
storage.mutateDiskOnly((disk) => ({
|
|
91
|
+
...disk,
|
|
92
|
+
accounts: disk.accounts.map((a) => ({ ...a, enabled: false })),
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
// Load should see the mutated state
|
|
96
|
+
const loaded = await storage.loadAccountsMock();
|
|
97
|
+
expect(loaded?.accounts[0].enabled).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("mutateDiskOnly throws when disk state is null", () => {
|
|
101
|
+
const storage = createInMemoryStorage();
|
|
102
|
+
|
|
103
|
+
expect(() =>
|
|
104
|
+
storage.mutateDiskOnly((disk) => ({
|
|
105
|
+
...disk,
|
|
106
|
+
accounts: [],
|
|
107
|
+
})),
|
|
108
|
+
).toThrow("Cannot mutate disk - disk state is null");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("makeStoredAccount creates valid account with defaults", () => {
|
|
112
|
+
const account = makeStoredAccount({ refreshToken: "my-token" });
|
|
113
|
+
|
|
114
|
+
expect(account.refreshToken).toBe("my-token");
|
|
115
|
+
expect(account.id).toMatch(/^acct-/);
|
|
116
|
+
expect(account.enabled).toBe(true);
|
|
117
|
+
expect(account.consecutiveFailures).toBe(0);
|
|
118
|
+
expect(account.lastFailureTime).toBeNull();
|
|
119
|
+
expect(account.stats.requests).toBe(0);
|
|
120
|
+
expect(account.addedAt).toBeGreaterThan(0);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("makeStoredAccount applies overrides", () => {
|
|
124
|
+
const account = makeStoredAccount({
|
|
125
|
+
refreshToken: "tok1",
|
|
126
|
+
enabled: false,
|
|
127
|
+
consecutiveFailures: 5,
|
|
128
|
+
email: "test@example.com",
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(account.refreshToken).toBe("tok1");
|
|
132
|
+
expect(account.enabled).toBe(false);
|
|
133
|
+
expect(account.consecutiveFailures).toBe(5);
|
|
134
|
+
expect(account.email).toBe("test@example.com");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("makeAccountsData creates storage with multiple accounts", () => {
|
|
138
|
+
const data = makeAccountsData([
|
|
139
|
+
{ refreshToken: "tok1", email: "a@test.com" },
|
|
140
|
+
{ refreshToken: "tok2", email: "b@test.com" },
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
expect(data.version).toBe(1);
|
|
144
|
+
expect(data.activeIndex).toBe(0);
|
|
145
|
+
expect(data.accounts).toHaveLength(2);
|
|
146
|
+
expect(data.accounts[0].refreshToken).toBe("tok1");
|
|
147
|
+
expect(data.accounts[1].refreshToken).toBe("tok2");
|
|
148
|
+
expect(data.accounts[0].addedAt).toBe(1000);
|
|
149
|
+
expect(data.accounts[1].addedAt).toBe(2000);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("makeAccountsData accepts extra storage fields", () => {
|
|
153
|
+
const data = makeAccountsData([{ refreshToken: "tok1" }], {
|
|
154
|
+
activeIndex: 2,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(data.activeIndex).toBe(2);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("storage mocks are vi.fn() instances", () => {
|
|
161
|
+
const storage = createInMemoryStorage();
|
|
162
|
+
|
|
163
|
+
expect(storage.loadAccountsMock).toBeDefined();
|
|
164
|
+
expect(storage.saveAccountsMock).toBeDefined();
|
|
165
|
+
});
|
|
166
166
|
});
|