opencode-anthropic-fix 0.2.1 → 0.3.0
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 +24 -0
- package/index.mjs +155 -149
- package/lib/config.mjs +47 -25
- package/lib/mimicry/adapter-input.mjs +671 -0
- package/lib/mimicry/context-hint-threshold.mjs +127 -0
- package/lib/mimicry/headers.mjs +71 -64
- package/lib/mimicry/request-body.mjs +23 -22
- package/lib/mimicry/system-prompt.mjs +54 -23
- package/lib/mimicry/wire-compat.mjs +305 -0
- package/package.json +3 -3
- package/lib/account-state.test.mjs +0 -109
- package/lib/accounts.test.mjs +0 -912
- package/lib/adaptive-thinking-models.test.mjs +0 -30
- package/lib/backoff.test.mjs +0 -411
- package/lib/cc-credentials.test.mjs +0 -391
- package/lib/config.test.mjs +0 -764
- package/lib/context-hint-persist.mjs +0 -68
- package/lib/diagnose.test.mjs +0 -122
- package/lib/env.test.mjs +0 -25
- package/lib/haiku-call.test.mjs +0 -72
- package/lib/message-transform.test.mjs +0 -173
- package/lib/mimicry/cache.test.mjs +0 -112
- package/lib/mimicry/headers.test.mjs +0 -50
- package/lib/mimicry/models.test.mjs +0 -132
- package/lib/mimicry/request-body.test.mjs +0 -34
- package/lib/mimicry/request-helpers.test.mjs +0 -155
- package/lib/mimicry/response-stream.test.mjs +0 -127
- package/lib/mimicry/system-prompt.test.mjs +0 -73
- package/lib/oauth.test.mjs +0 -343
- package/lib/redact.test.mjs +0 -154
- package/lib/refresh-lock.test.mjs +0 -131
- package/lib/request-headers.test.mjs +0 -164
- package/lib/retry/overload-loop.test.mjs +0 -107
- package/lib/rotation.test.mjs +0 -392
- package/lib/session-metrics.test.mjs +0 -67
- package/lib/storage.test.mjs +0 -696
- package/lib/token-economy/microcompact.test.mjs +0 -28
- package/lib/token-economy/transforms.test.mjs +0 -192
- package/lib/tuning.test.mjs +0 -89
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
readCCCredentialsFromKeychain,
|
|
4
|
-
readCCCredentialsFromFile,
|
|
5
|
-
readCCCredentials,
|
|
6
|
-
parseCCCredentialData,
|
|
7
|
-
} from "./cc-credentials.mjs";
|
|
8
|
-
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// Mocks
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
|
|
13
|
-
vi.mock("node:child_process", () => ({
|
|
14
|
-
execSync: vi.fn(),
|
|
15
|
-
execFileSync: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
vi.mock("node:fs", () => ({
|
|
19
|
-
readFileSync: vi.fn(),
|
|
20
|
-
existsSync: vi.fn(),
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
vi.mock("node:os", () => ({
|
|
24
|
-
homedir: vi.fn(() => "/home/testuser"),
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
import { execSync, execFileSync } from "node:child_process";
|
|
28
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
29
|
-
import { homedir } from "node:os";
|
|
30
|
-
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
// Fixtures
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
|
|
35
|
-
const VALID_CRED = {
|
|
36
|
-
accessToken: "acc-123",
|
|
37
|
-
refreshToken: "ref-456",
|
|
38
|
-
expiresAt: Date.now() + 3_600_000,
|
|
39
|
-
subscriptionType: "claude_pro",
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const VALID_CRED_2 = {
|
|
43
|
-
accessToken: "acc-789",
|
|
44
|
-
refreshToken: "ref-012",
|
|
45
|
-
expiresAt: Date.now() + 7_200_000,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const MCP_ONLY_CRED = {
|
|
49
|
-
accessToken: "mcp-acc",
|
|
50
|
-
refreshToken: "mcp-ref",
|
|
51
|
-
expiresAt: Date.now() + 3_600_000,
|
|
52
|
-
scope: "mcp",
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const KEYCHAIN_DUMP_SINGLE = `
|
|
56
|
-
keychain: "/Users/test/Library/Keychains/login.keychain-db"
|
|
57
|
-
0x00000007 <blob>="Claude Code-credentials"
|
|
58
|
-
"svce"<blob>="Claude Code-credentials"
|
|
59
|
-
`;
|
|
60
|
-
|
|
61
|
-
const KEYCHAIN_DUMP_MULTI = `
|
|
62
|
-
keychain: "/Users/test/Library/Keychains/login.keychain-db"
|
|
63
|
-
"svce"<blob>="Claude Code-credentials-0"
|
|
64
|
-
"svce"<blob>="Claude Code-credentials-1"
|
|
65
|
-
"svce"<blob>="Claude Code-credentials-0"
|
|
66
|
-
`;
|
|
67
|
-
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
// parseCCCredentialData
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
|
|
72
|
-
describe("parseCCCredentialData", () => {
|
|
73
|
-
it("parses valid flat JSON", () => {
|
|
74
|
-
const result = parseCCCredentialData(JSON.stringify(VALID_CRED), "cc-file", "test");
|
|
75
|
-
expect(result).not.toBeNull();
|
|
76
|
-
expect(result.accessToken).toBe("acc-123");
|
|
77
|
-
expect(result.refreshToken).toBe("ref-456");
|
|
78
|
-
expect(result.subscriptionType).toBe("claude_pro");
|
|
79
|
-
expect(result.source).toBe("cc-file");
|
|
80
|
-
expect(result.label).toBe("test");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("parses wrapped claudeAiOauth format", () => {
|
|
84
|
-
const wrapped = { claudeAiOauth: VALID_CRED };
|
|
85
|
-
const result = parseCCCredentialData(JSON.stringify(wrapped), "cc-keychain", "kc");
|
|
86
|
-
expect(result).not.toBeNull();
|
|
87
|
-
expect(result.accessToken).toBe("acc-123");
|
|
88
|
-
expect(result.source).toBe("cc-keychain");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("returns null for invalid JSON", () => {
|
|
92
|
-
expect(parseCCCredentialData("not json", "cc-file", "x")).toBeNull();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("returns null for missing accessToken", () => {
|
|
96
|
-
const data = { refreshToken: "ref" };
|
|
97
|
-
expect(parseCCCredentialData(JSON.stringify(data), "cc-file", "x")).toBeNull();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("returns null for missing refreshToken", () => {
|
|
101
|
-
const data = { accessToken: "acc" };
|
|
102
|
-
expect(parseCCCredentialData(JSON.stringify(data), "cc-file", "x")).toBeNull();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("returns null for non-object JSON", () => {
|
|
106
|
-
expect(parseCCCredentialData('"hello"', "cc-file", "x")).toBeNull();
|
|
107
|
-
expect(parseCCCredentialData("42", "cc-file", "x")).toBeNull();
|
|
108
|
-
expect(parseCCCredentialData("null", "cc-file", "x")).toBeNull();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("handles expiresAt as ISO string", () => {
|
|
112
|
-
const data = { ...VALID_CRED, expiresAt: "2026-06-15T12:00:00Z" };
|
|
113
|
-
const result = parseCCCredentialData(JSON.stringify(data), "cc-file", "x");
|
|
114
|
-
expect(result).not.toBeNull();
|
|
115
|
-
expect(result.expiresAt).toBe(Date.parse("2026-06-15T12:00:00Z"));
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("handles expires_at fallback field", () => {
|
|
119
|
-
const data = { accessToken: "a", refreshToken: "r", expires_at: 999999 };
|
|
120
|
-
const result = parseCCCredentialData(JSON.stringify(data), "cc-file", "x");
|
|
121
|
-
expect(result).not.toBeNull();
|
|
122
|
-
expect(result.expiresAt).toBe(999999);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("defaults expiresAt to 0 when missing", () => {
|
|
126
|
-
const data = { accessToken: "a", refreshToken: "r" };
|
|
127
|
-
const result = parseCCCredentialData(JSON.stringify(data), "cc-file", "x");
|
|
128
|
-
expect(result.expiresAt).toBe(0);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
// readCCCredentialsFromKeychain
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
|
|
136
|
-
describe("readCCCredentialsFromKeychain", () => {
|
|
137
|
-
const origPlatform = process.platform;
|
|
138
|
-
|
|
139
|
-
afterEach(() => {
|
|
140
|
-
Object.defineProperty(process, "platform", { value: origPlatform });
|
|
141
|
-
vi.resetAllMocks();
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("returns empty on non-darwin platforms", () => {
|
|
145
|
-
Object.defineProperty(process, "platform", { value: "linux" });
|
|
146
|
-
expect(readCCCredentialsFromKeychain()).toEqual([]);
|
|
147
|
-
expect(execSync).not.toHaveBeenCalled();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("reads single keychain service on macOS", () => {
|
|
151
|
-
Object.defineProperty(process, "platform", { value: "darwin" });
|
|
152
|
-
|
|
153
|
-
// dump-keychain uses execSync, find-generic-password uses execFileSync
|
|
154
|
-
execSync.mockImplementation((cmd) => {
|
|
155
|
-
if (cmd === "security dump-keychain") return KEYCHAIN_DUMP_SINGLE;
|
|
156
|
-
throw new Error("unexpected execSync cmd");
|
|
157
|
-
});
|
|
158
|
-
execFileSync.mockImplementation((_bin, args) => {
|
|
159
|
-
if (args && args.includes("find-generic-password")) return JSON.stringify(VALID_CRED);
|
|
160
|
-
throw new Error("unexpected execFileSync cmd");
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const result = readCCCredentialsFromKeychain();
|
|
164
|
-
expect(result).toHaveLength(1);
|
|
165
|
-
expect(result[0].accessToken).toBe("acc-123");
|
|
166
|
-
expect(result[0].source).toBe("cc-keychain");
|
|
167
|
-
expect(result[0].label).toContain("keychain:");
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("reads multiple keychain services and deduplicates service names", () => {
|
|
171
|
-
Object.defineProperty(process, "platform", { value: "darwin" });
|
|
172
|
-
|
|
173
|
-
let callIdx = 0;
|
|
174
|
-
execSync.mockImplementation((cmd) => {
|
|
175
|
-
if (cmd === "security dump-keychain") return KEYCHAIN_DUMP_MULTI;
|
|
176
|
-
throw new Error("unexpected execSync cmd");
|
|
177
|
-
});
|
|
178
|
-
execFileSync.mockImplementation((_bin, args) => {
|
|
179
|
-
if (args && args.includes("find-generic-password")) {
|
|
180
|
-
callIdx++;
|
|
181
|
-
const cred = callIdx === 1 ? VALID_CRED : VALID_CRED_2;
|
|
182
|
-
return JSON.stringify(cred);
|
|
183
|
-
}
|
|
184
|
-
throw new Error("unexpected execFileSync cmd");
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const result = readCCCredentialsFromKeychain();
|
|
188
|
-
// KEYCHAIN_DUMP_MULTI has credentials-0 twice, but should be deduped to 2 services
|
|
189
|
-
expect(result).toHaveLength(2);
|
|
190
|
-
expect(result[0].accessToken).toBe("acc-123");
|
|
191
|
-
expect(result[1].accessToken).toBe("acc-789");
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it("returns empty when dump-keychain throws", () => {
|
|
195
|
-
Object.defineProperty(process, "platform", { value: "darwin" });
|
|
196
|
-
execSync.mockImplementation(() => {
|
|
197
|
-
throw new Error("no keychain");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
expect(readCCCredentialsFromKeychain()).toEqual([]);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("skips services where find-generic-password fails (exit 44)", () => {
|
|
204
|
-
Object.defineProperty(process, "platform", { value: "darwin" });
|
|
205
|
-
|
|
206
|
-
execSync.mockImplementation((cmd) => {
|
|
207
|
-
if (cmd === "security dump-keychain") return KEYCHAIN_DUMP_SINGLE;
|
|
208
|
-
throw new Error("unexpected");
|
|
209
|
-
});
|
|
210
|
-
execFileSync.mockImplementation(() => {
|
|
211
|
-
const err = new Error("not found");
|
|
212
|
-
err.status = 44;
|
|
213
|
-
throw err;
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
expect(readCCCredentialsFromKeychain()).toEqual([]);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// ---------------------------------------------------------------------------
|
|
221
|
-
// readCCCredentialsFromFile
|
|
222
|
-
// ---------------------------------------------------------------------------
|
|
223
|
-
|
|
224
|
-
describe("readCCCredentialsFromFile", () => {
|
|
225
|
-
beforeEach(() => {
|
|
226
|
-
vi.resetAllMocks();
|
|
227
|
-
homedir.mockReturnValue("/home/testuser");
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it("returns empty when credentials file does not exist", () => {
|
|
231
|
-
existsSync.mockReturnValue(false);
|
|
232
|
-
expect(readCCCredentialsFromFile()).toEqual([]);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it("reads flat credential object", () => {
|
|
236
|
-
existsSync.mockReturnValue(true);
|
|
237
|
-
readFileSync.mockReturnValue(JSON.stringify(VALID_CRED));
|
|
238
|
-
|
|
239
|
-
const result = readCCCredentialsFromFile();
|
|
240
|
-
expect(result).toHaveLength(1);
|
|
241
|
-
expect(result[0].accessToken).toBe("acc-123");
|
|
242
|
-
expect(result[0].source).toBe("cc-file");
|
|
243
|
-
expect(result[0].label).toBe("file:entry-0");
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it("reads wrapped claudeAiOauth format", () => {
|
|
247
|
-
existsSync.mockReturnValue(true);
|
|
248
|
-
readFileSync.mockReturnValue(JSON.stringify({ claudeAiOauth: VALID_CRED }));
|
|
249
|
-
|
|
250
|
-
const result = readCCCredentialsFromFile();
|
|
251
|
-
expect(result).toHaveLength(1);
|
|
252
|
-
expect(result[0].accessToken).toBe("acc-123");
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it("reads array of credentials", () => {
|
|
256
|
-
existsSync.mockReturnValue(true);
|
|
257
|
-
readFileSync.mockReturnValue(JSON.stringify([VALID_CRED, VALID_CRED_2]));
|
|
258
|
-
|
|
259
|
-
const result = readCCCredentialsFromFile();
|
|
260
|
-
expect(result).toHaveLength(2);
|
|
261
|
-
expect(result[0].accessToken).toBe("acc-123");
|
|
262
|
-
expect(result[1].accessToken).toBe("acc-789");
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it("filters MCP-only entries", () => {
|
|
266
|
-
existsSync.mockReturnValue(true);
|
|
267
|
-
readFileSync.mockReturnValue(JSON.stringify([VALID_CRED, MCP_ONLY_CRED]));
|
|
268
|
-
|
|
269
|
-
const result = readCCCredentialsFromFile();
|
|
270
|
-
expect(result).toHaveLength(1);
|
|
271
|
-
expect(result[0].accessToken).toBe("acc-123");
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("skips non-object entries in array", () => {
|
|
275
|
-
existsSync.mockReturnValue(true);
|
|
276
|
-
readFileSync.mockReturnValue(JSON.stringify([null, 42, "hello", VALID_CRED]));
|
|
277
|
-
|
|
278
|
-
const result = readCCCredentialsFromFile();
|
|
279
|
-
expect(result).toHaveLength(1);
|
|
280
|
-
expect(result[0].accessToken).toBe("acc-123");
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it("returns empty on invalid JSON", () => {
|
|
284
|
-
existsSync.mockReturnValue(true);
|
|
285
|
-
readFileSync.mockReturnValue("not json at all");
|
|
286
|
-
|
|
287
|
-
expect(readCCCredentialsFromFile()).toEqual([]);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it("returns empty on read error", () => {
|
|
291
|
-
existsSync.mockReturnValue(true);
|
|
292
|
-
readFileSync.mockImplementation(() => {
|
|
293
|
-
throw new Error("EACCES");
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
expect(readCCCredentialsFromFile()).toEqual([]);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it("handles entries missing required fields gracefully", () => {
|
|
300
|
-
existsSync.mockReturnValue(true);
|
|
301
|
-
readFileSync.mockReturnValue(JSON.stringify([{ accessToken: "only-access" }, VALID_CRED]));
|
|
302
|
-
|
|
303
|
-
const result = readCCCredentialsFromFile();
|
|
304
|
-
expect(result).toHaveLength(1);
|
|
305
|
-
expect(result[0].accessToken).toBe("acc-123");
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
// ---------------------------------------------------------------------------
|
|
310
|
-
// readCCCredentials (unified)
|
|
311
|
-
// ---------------------------------------------------------------------------
|
|
312
|
-
|
|
313
|
-
describe("readCCCredentials", () => {
|
|
314
|
-
const origPlatform = process.platform;
|
|
315
|
-
|
|
316
|
-
beforeEach(() => {
|
|
317
|
-
vi.resetAllMocks();
|
|
318
|
-
homedir.mockReturnValue("/home/testuser");
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
afterEach(() => {
|
|
322
|
-
Object.defineProperty(process, "platform", { value: origPlatform });
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it("combines keychain and file sources on macOS", () => {
|
|
326
|
-
Object.defineProperty(process, "platform", { value: "darwin" });
|
|
327
|
-
|
|
328
|
-
// Keychain: dump-keychain via execSync, find-generic-password via execFileSync
|
|
329
|
-
execSync.mockImplementation((cmd) => {
|
|
330
|
-
if (cmd === "security dump-keychain") return KEYCHAIN_DUMP_SINGLE;
|
|
331
|
-
throw new Error("unexpected execSync cmd");
|
|
332
|
-
});
|
|
333
|
-
execFileSync.mockImplementation((_bin, args) => {
|
|
334
|
-
if (args && args.includes("find-generic-password")) return JSON.stringify(VALID_CRED);
|
|
335
|
-
throw new Error("unexpected execFileSync cmd");
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
// File returns a different credential
|
|
339
|
-
existsSync.mockReturnValue(true);
|
|
340
|
-
readFileSync.mockReturnValue(JSON.stringify(VALID_CRED_2));
|
|
341
|
-
|
|
342
|
-
const result = readCCCredentials();
|
|
343
|
-
expect(result).toHaveLength(2);
|
|
344
|
-
expect(result.some((c) => c.source === "cc-keychain")).toBe(true);
|
|
345
|
-
expect(result.some((c) => c.source === "cc-file")).toBe(true);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it("deduplicates by refreshToken, preferring keychain", () => {
|
|
349
|
-
Object.defineProperty(process, "platform", { value: "darwin" });
|
|
350
|
-
|
|
351
|
-
// Both sources return same refreshToken
|
|
352
|
-
const keychainCred = { ...VALID_CRED, accessToken: "keychain-token" };
|
|
353
|
-
const fileCred = { ...VALID_CRED, accessToken: "file-token" };
|
|
354
|
-
|
|
355
|
-
execSync.mockImplementation((cmd) => {
|
|
356
|
-
if (cmd === "security dump-keychain") return KEYCHAIN_DUMP_SINGLE;
|
|
357
|
-
throw new Error("unexpected execSync cmd");
|
|
358
|
-
});
|
|
359
|
-
execFileSync.mockImplementation((_bin, args) => {
|
|
360
|
-
if (args && args.includes("find-generic-password")) return JSON.stringify(keychainCred);
|
|
361
|
-
throw new Error("unexpected execFileSync cmd");
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
existsSync.mockReturnValue(true);
|
|
365
|
-
readFileSync.mockReturnValue(JSON.stringify(fileCred));
|
|
366
|
-
|
|
367
|
-
const result = readCCCredentials();
|
|
368
|
-
expect(result).toHaveLength(1);
|
|
369
|
-
expect(result[0].source).toBe("cc-keychain");
|
|
370
|
-
expect(result[0].accessToken).toBe("keychain-token");
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
it("returns only file credentials on Linux", () => {
|
|
374
|
-
Object.defineProperty(process, "platform", { value: "linux" });
|
|
375
|
-
|
|
376
|
-
existsSync.mockReturnValue(true);
|
|
377
|
-
readFileSync.mockReturnValue(JSON.stringify(VALID_CRED));
|
|
378
|
-
|
|
379
|
-
const result = readCCCredentials();
|
|
380
|
-
expect(result).toHaveLength(1);
|
|
381
|
-
expect(result[0].source).toBe("cc-file");
|
|
382
|
-
expect(execSync).not.toHaveBeenCalled();
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it("returns empty when no sources available", () => {
|
|
386
|
-
Object.defineProperty(process, "platform", { value: "linux" });
|
|
387
|
-
existsSync.mockReturnValue(false);
|
|
388
|
-
|
|
389
|
-
expect(readCCCredentials()).toEqual([]);
|
|
390
|
-
});
|
|
391
|
-
});
|