jazz-tools 0.9.21 → 0.10.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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +26 -0
- package/dist/{chunk-OJIEP4WE.js → chunk-UBD75Z27.js} +566 -118
- package/dist/chunk-UBD75Z27.js.map +1 -0
- package/dist/index.native.js +17 -5
- package/dist/index.native.js.map +1 -1
- package/dist/index.web.js +17 -5
- package/dist/index.web.js.map +1 -1
- package/dist/testing.js +124 -33
- package/dist/testing.js.map +1 -1
- package/package.json +5 -3
- package/src/auth/AuthSecretStorage.ts +109 -0
- package/src/auth/DemoAuth.ts +188 -0
- package/src/auth/InMemoryKVStore.ts +25 -0
- package/src/auth/KvStoreContext.ts +39 -0
- package/src/auth/PassphraseAuth.ts +113 -0
- package/src/coValues/account.ts +8 -3
- package/src/coValues/coFeed.ts +1 -1
- package/src/coValues/coList.ts +1 -1
- package/src/coValues/coMap.ts +1 -1
- package/src/coValues/group.ts +9 -8
- package/src/coValues/interfaces.ts +14 -5
- package/src/exports.ts +17 -3
- package/src/implementation/ContextManager.ts +178 -0
- package/src/implementation/activeAccountContext.ts +6 -1
- package/src/implementation/createContext.ts +173 -149
- package/src/testing.ts +171 -33
- package/src/tests/AuthSecretStorage.test.ts +275 -0
- package/src/tests/ContextManager.test.ts +256 -0
- package/src/tests/DemoAuth.test.ts +269 -0
- package/src/tests/PassphraseAuth.test.ts +152 -0
- package/src/tests/coFeed.test.ts +44 -39
- package/src/tests/coList.test.ts +21 -20
- package/src/tests/coMap.test.ts +21 -20
- package/src/tests/coPlainText.test.ts +21 -20
- package/src/tests/coRichText.test.ts +21 -20
- package/src/tests/createContext.test.ts +339 -0
- package/src/tests/deepLoading.test.ts +41 -42
- package/src/tests/fixtures.ts +2050 -0
- package/src/tests/groupsAndAccounts.test.ts +2 -2
- package/src/tests/subscribe.test.ts +42 -9
- package/src/tests/testing.test.ts +56 -0
- package/src/tests/utils.ts +11 -11
- package/src/types.ts +54 -0
- package/dist/chunk-OJIEP4WE.js.map +0 -1
package/src/testing.ts
CHANGED
@@ -1,12 +1,22 @@
|
|
1
1
|
import { AgentSecret, CryptoProvider, LocalNode, Peer } from "cojson";
|
2
2
|
import { cojsonInternals } from "cojson";
|
3
3
|
import { PureJSCrypto } from "cojson/crypto";
|
4
|
-
import {
|
4
|
+
import {
|
5
|
+
Account,
|
6
|
+
AccountClass,
|
7
|
+
JazzContextManagerAuthProps,
|
8
|
+
} from "./exports.js";
|
9
|
+
import {
|
10
|
+
JazzContextManager,
|
11
|
+
JazzContextManagerBaseProps,
|
12
|
+
} from "./implementation/ContextManager.js";
|
5
13
|
import { activeAccountContext } from "./implementation/activeAccountContext.js";
|
6
14
|
import {
|
7
15
|
type AnonymousJazzAgent,
|
8
16
|
type CoValueClass,
|
9
17
|
createAnonymousJazzContext,
|
18
|
+
createJazzContext,
|
19
|
+
randomSessionProvider,
|
10
20
|
} from "./internal.js";
|
11
21
|
|
12
22
|
const syncServer: { current: LocalNode | null } = { current: null };
|
@@ -21,9 +31,9 @@ type TestAccountSchema<Acc extends Account> = CoValueClass<Acc> & {
|
|
21
31
|
}) => Promise<Acc>;
|
22
32
|
};
|
23
33
|
|
24
|
-
class TestJSCrypto extends PureJSCrypto {
|
34
|
+
export class TestJSCrypto extends PureJSCrypto {
|
25
35
|
static async create() {
|
26
|
-
if ("navigator" in globalThis && navigator.userAgent
|
36
|
+
if ("navigator" in globalThis && navigator.userAgent?.includes("jsdom")) {
|
27
37
|
// Mocking crypto seal & encrypt to make it work with JSDom. Getting "Error: Uint8Array expected" there
|
28
38
|
const crypto = new PureJSCrypto();
|
29
39
|
|
@@ -44,6 +54,27 @@ class TestJSCrypto extends PureJSCrypto {
|
|
44
54
|
}
|
45
55
|
}
|
46
56
|
|
57
|
+
export function getPeerConnectedToTestSyncServer() {
|
58
|
+
if (!syncServer.current) {
|
59
|
+
throw new Error("Sync server not initialized");
|
60
|
+
}
|
61
|
+
|
62
|
+
const [aPeer, bPeer] = cojsonInternals.connectedPeers(
|
63
|
+
Math.random().toString(),
|
64
|
+
Math.random().toString(),
|
65
|
+
{
|
66
|
+
peer1role: "server",
|
67
|
+
peer2role: "server",
|
68
|
+
},
|
69
|
+
);
|
70
|
+
syncServer.current.syncManager.addPeer(aPeer);
|
71
|
+
|
72
|
+
return bPeer;
|
73
|
+
}
|
74
|
+
|
75
|
+
const SecretSeedMap = new Map<string, Uint8Array>();
|
76
|
+
let isMigrationActive = false;
|
77
|
+
|
47
78
|
export async function createJazzTestAccount<Acc extends Account>(options?: {
|
48
79
|
isCurrentActiveAccount?: boolean;
|
49
80
|
AccountSchema?: CoValueClass<Acc>;
|
@@ -53,39 +84,50 @@ export async function createJazzTestAccount<Acc extends Account>(options?: {
|
|
53
84
|
Account) as unknown as TestAccountSchema<Acc>;
|
54
85
|
const peers = [];
|
55
86
|
if (syncServer.current) {
|
56
|
-
|
57
|
-
Math.random().toString(),
|
58
|
-
Math.random().toString(),
|
59
|
-
{
|
60
|
-
peer1role: "server",
|
61
|
-
peer2role: "server",
|
62
|
-
},
|
63
|
-
);
|
64
|
-
syncServer.current.syncManager.addPeer(aPeer);
|
65
|
-
peers.push(bPeer);
|
87
|
+
peers.push(getPeerConnectedToTestSyncServer());
|
66
88
|
}
|
67
89
|
|
90
|
+
const crypto = await TestJSCrypto.create();
|
91
|
+
const secretSeed = crypto.newRandomSecretSeed();
|
92
|
+
|
68
93
|
const { node } = await LocalNode.withNewlyCreatedAccount({
|
69
94
|
creationProps: {
|
70
95
|
name: "Test Account",
|
71
96
|
...options?.creationProps,
|
72
97
|
},
|
73
|
-
|
98
|
+
initialAgentSecret: crypto.agentSecretFromSecretSeed(secretSeed),
|
99
|
+
crypto,
|
74
100
|
peersToLoadFrom: peers,
|
75
101
|
migration: async (rawAccount, _node, creationProps) => {
|
102
|
+
if (isMigrationActive) {
|
103
|
+
throw new Error(
|
104
|
+
"It is not possible to create multiple accounts in parallel inside the test environment.",
|
105
|
+
);
|
106
|
+
}
|
107
|
+
|
108
|
+
isMigrationActive = true;
|
109
|
+
|
76
110
|
const account = new AccountSchema({
|
77
111
|
fromRaw: rawAccount,
|
78
112
|
});
|
79
113
|
|
80
|
-
|
81
|
-
|
82
|
-
|
114
|
+
// We need to set the account as current because the migration
|
115
|
+
// will probably rely on the global me
|
116
|
+
const prevActiveAccount = activeAccountContext.maybeGet();
|
117
|
+
activeAccountContext.set(account);
|
83
118
|
|
84
119
|
await account.applyMigration?.(creationProps);
|
120
|
+
|
121
|
+
if (!options?.isCurrentActiveAccount) {
|
122
|
+
activeAccountContext.set(prevActiveAccount);
|
123
|
+
}
|
124
|
+
|
125
|
+
isMigrationActive = false;
|
85
126
|
},
|
86
127
|
});
|
87
128
|
|
88
129
|
const account = AccountSchema.fromNode(node);
|
130
|
+
SecretSeedMap.set(account.id, secretSeed);
|
89
131
|
|
90
132
|
if (options?.isCurrentActiveAccount) {
|
91
133
|
activeAccountContext.set(account);
|
@@ -109,24 +151,120 @@ export async function createJazzTestGuest() {
|
|
109
151
|
};
|
110
152
|
}
|
111
153
|
|
112
|
-
export
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
154
|
+
export type TestJazzContextManagerProps<Acc extends Account> =
|
155
|
+
JazzContextManagerBaseProps<Acc> & {
|
156
|
+
defaultProfileName?: string;
|
157
|
+
AccountSchema?: AccountClass<Acc>;
|
158
|
+
isAuthenticated?: boolean;
|
159
|
+
};
|
160
|
+
|
161
|
+
export class TestJazzContextManager<
|
162
|
+
Acc extends Account,
|
163
|
+
> extends JazzContextManager<Acc, TestJazzContextManagerProps<Acc>> {
|
164
|
+
static fromAccountOrGuest<Acc extends Account>(
|
165
|
+
account?: Acc | { guest: AnonymousJazzAgent },
|
166
|
+
props?: TestJazzContextManagerProps<Acc>,
|
167
|
+
) {
|
168
|
+
if (account && "guest" in account) {
|
169
|
+
return this.fromGuest<Acc>(account, props);
|
170
|
+
}
|
171
|
+
|
172
|
+
return this.fromAccount<Acc>(account ?? (Account.getMe() as Acc), props);
|
122
173
|
}
|
123
174
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
175
|
+
static fromAccount<Acc extends Account>(
|
176
|
+
account: Acc,
|
177
|
+
props?: TestJazzContextManagerProps<Acc>,
|
178
|
+
) {
|
179
|
+
const context = new TestJazzContextManager<Acc>();
|
180
|
+
|
181
|
+
const provider = props?.isAuthenticated ? "testProvider" : "anonymous";
|
182
|
+
const storage = context.getAuthSecretStorage();
|
183
|
+
const node = account._raw.core.node;
|
184
|
+
|
185
|
+
storage.set({
|
186
|
+
accountID: account.id,
|
187
|
+
accountSecret: node.account.agentSecret,
|
188
|
+
secretSeed: SecretSeedMap.get(account.id),
|
189
|
+
provider,
|
190
|
+
});
|
191
|
+
storage.isAuthenticated = Boolean(props?.isAuthenticated);
|
192
|
+
|
193
|
+
context.updateContext(
|
194
|
+
{
|
195
|
+
AccountSchema: account.constructor as AccountClass<Acc>,
|
196
|
+
...props,
|
197
|
+
},
|
198
|
+
{
|
199
|
+
me: account,
|
200
|
+
node,
|
201
|
+
done: () => {
|
202
|
+
node.gracefulShutdown();
|
203
|
+
},
|
204
|
+
logOut: async () => {
|
205
|
+
node.gracefulShutdown();
|
206
|
+
},
|
207
|
+
},
|
208
|
+
);
|
209
|
+
|
210
|
+
return context;
|
211
|
+
}
|
212
|
+
|
213
|
+
static fromGuest<Acc extends Account>(
|
214
|
+
{ guest }: { guest: AnonymousJazzAgent },
|
215
|
+
props: TestJazzContextManagerProps<Acc> = {},
|
216
|
+
) {
|
217
|
+
const context = new TestJazzContextManager<Acc>();
|
218
|
+
const node = guest.node;
|
219
|
+
|
220
|
+
context.updateContext(props, {
|
221
|
+
guest,
|
222
|
+
node,
|
223
|
+
done: () => {
|
224
|
+
node.gracefulShutdown();
|
225
|
+
},
|
226
|
+
logOut: async () => {
|
227
|
+
node.gracefulShutdown();
|
228
|
+
},
|
229
|
+
});
|
230
|
+
|
231
|
+
return context;
|
232
|
+
}
|
233
|
+
|
234
|
+
async createContext(
|
235
|
+
props: TestJazzContextManagerProps<Acc>,
|
236
|
+
authProps?: JazzContextManagerAuthProps,
|
237
|
+
) {
|
238
|
+
this.props = props;
|
239
|
+
|
240
|
+
if (!syncServer.current) {
|
241
|
+
throw new Error(
|
242
|
+
"You need to setup a test sync server with setupJazzTestSync to use the Auth functions",
|
243
|
+
);
|
244
|
+
}
|
245
|
+
|
246
|
+
const context = await createJazzContext<Acc>({
|
247
|
+
credentials: authProps?.credentials,
|
248
|
+
defaultProfileName: props.defaultProfileName,
|
249
|
+
newAccountProps: authProps?.newAccountProps,
|
250
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
251
|
+
crypto: await TestJSCrypto.create(),
|
252
|
+
sessionProvider: randomSessionProvider,
|
253
|
+
authSecretStorage: this.getAuthSecretStorage(),
|
254
|
+
AccountSchema: props.AccountSchema,
|
255
|
+
});
|
256
|
+
|
257
|
+
this.updateContext(props, {
|
258
|
+
me: context.account,
|
259
|
+
node: context.node,
|
260
|
+
done: () => {
|
261
|
+
context.done();
|
262
|
+
},
|
263
|
+
logOut: () => {
|
264
|
+
return context.logOut();
|
265
|
+
},
|
266
|
+
});
|
267
|
+
}
|
130
268
|
}
|
131
269
|
|
132
270
|
export function linkAccounts(
|
@@ -0,0 +1,275 @@
|
|
1
|
+
// @vitest-environment happy-dom
|
2
|
+
|
3
|
+
import { Account } from "jazz-tools";
|
4
|
+
import { ID } from "jazz-tools";
|
5
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
6
|
+
import { AuthSecretStorage } from "../auth/AuthSecretStorage";
|
7
|
+
import { InMemoryKVStore } from "../auth/InMemoryKVStore.js";
|
8
|
+
import KvStoreContext from "../auth/KvStoreContext";
|
9
|
+
|
10
|
+
const kvStore = new InMemoryKVStore();
|
11
|
+
KvStoreContext.getInstance().initialize(kvStore);
|
12
|
+
|
13
|
+
let authSecretStorage = new AuthSecretStorage();
|
14
|
+
|
15
|
+
describe("AuthSecretStorage", () => {
|
16
|
+
beforeEach(() => {
|
17
|
+
kvStore.clearAll();
|
18
|
+
authSecretStorage = new AuthSecretStorage();
|
19
|
+
});
|
20
|
+
|
21
|
+
describe("migrate", () => {
|
22
|
+
it("should migrate demo auth secret", async () => {
|
23
|
+
const demoSecret = JSON.stringify({
|
24
|
+
accountID: "demo123",
|
25
|
+
accountSecret: "secret123",
|
26
|
+
});
|
27
|
+
await kvStore.set("demo-auth-logged-in-secret", demoSecret);
|
28
|
+
|
29
|
+
await authSecretStorage.migrate();
|
30
|
+
|
31
|
+
expect(await kvStore.get("jazz-logged-in-secret")).toBe(demoSecret);
|
32
|
+
expect(await kvStore.get("demo-auth-logged-in-secret")).toBeNull();
|
33
|
+
});
|
34
|
+
|
35
|
+
it("should migrate clerk auth secret", async () => {
|
36
|
+
const clerkSecret = JSON.stringify({
|
37
|
+
accountID: "clerk123",
|
38
|
+
accountSecret: "secret123",
|
39
|
+
});
|
40
|
+
await kvStore.set("jazz-clerk-auth", clerkSecret);
|
41
|
+
|
42
|
+
await authSecretStorage.migrate();
|
43
|
+
|
44
|
+
expect(await kvStore.get("jazz-logged-in-secret")).toBe(clerkSecret);
|
45
|
+
expect(await kvStore.get("jazz-clerk-auth")).toBeNull();
|
46
|
+
});
|
47
|
+
});
|
48
|
+
|
49
|
+
describe("get", () => {
|
50
|
+
it("should return null when no data exists", async () => {
|
51
|
+
expect(await authSecretStorage.get()).toBeNull();
|
52
|
+
});
|
53
|
+
|
54
|
+
it("should return credentials with secretSeed", async () => {
|
55
|
+
const credentials = {
|
56
|
+
accountID: "test123",
|
57
|
+
secretSeed: [1, 2, 3],
|
58
|
+
accountSecret: "secret123",
|
59
|
+
provider: "anonymous",
|
60
|
+
};
|
61
|
+
await kvStore.set("jazz-logged-in-secret", JSON.stringify(credentials));
|
62
|
+
|
63
|
+
const result = await authSecretStorage.get();
|
64
|
+
|
65
|
+
expect(result).toEqual({
|
66
|
+
accountID: "test123",
|
67
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
68
|
+
accountSecret: "secret123",
|
69
|
+
provider: "anonymous",
|
70
|
+
});
|
71
|
+
});
|
72
|
+
|
73
|
+
it("should return non-anonymous credentials without secretSeed", async () => {
|
74
|
+
const credentials = {
|
75
|
+
accountID: "test123",
|
76
|
+
accountSecret: "secret123",
|
77
|
+
provider: "passphrase",
|
78
|
+
};
|
79
|
+
await kvStore.set("jazz-logged-in-secret", JSON.stringify(credentials));
|
80
|
+
|
81
|
+
const result = await authSecretStorage.get();
|
82
|
+
|
83
|
+
expect(result).toEqual({
|
84
|
+
accountID: "test123",
|
85
|
+
accountSecret: "secret123",
|
86
|
+
provider: "passphrase",
|
87
|
+
});
|
88
|
+
});
|
89
|
+
|
90
|
+
it("should throw error for invalid data", async () => {
|
91
|
+
await kvStore.set(
|
92
|
+
"jazz-logged-in-secret",
|
93
|
+
JSON.stringify({ invalid: "data" }),
|
94
|
+
);
|
95
|
+
|
96
|
+
await expect(authSecretStorage.get()).rejects.toThrow(
|
97
|
+
"Invalid auth secret storage data",
|
98
|
+
);
|
99
|
+
});
|
100
|
+
});
|
101
|
+
|
102
|
+
describe("set", () => {
|
103
|
+
it("should set credentials with secretSeed", async () => {
|
104
|
+
const payload = {
|
105
|
+
accountID: "test123" as ID<Account>,
|
106
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
107
|
+
accountSecret:
|
108
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
109
|
+
provider: "passphrase",
|
110
|
+
};
|
111
|
+
|
112
|
+
await authSecretStorage.set(payload);
|
113
|
+
|
114
|
+
const stored = JSON.parse((await kvStore.get("jazz-logged-in-secret"))!);
|
115
|
+
expect(stored).toEqual({
|
116
|
+
accountID: "test123",
|
117
|
+
secretSeed: [1, 2, 3],
|
118
|
+
accountSecret: "secret123",
|
119
|
+
provider: "passphrase",
|
120
|
+
});
|
121
|
+
});
|
122
|
+
|
123
|
+
it("should set credentials without secretSeed", async () => {
|
124
|
+
const payload = {
|
125
|
+
accountID: "test123" as ID<Account>,
|
126
|
+
accountSecret:
|
127
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
128
|
+
provider: "passphrase",
|
129
|
+
};
|
130
|
+
|
131
|
+
await authSecretStorage.set(payload);
|
132
|
+
|
133
|
+
const stored = JSON.parse((await kvStore.get("jazz-logged-in-secret"))!);
|
134
|
+
expect(stored).toEqual(payload);
|
135
|
+
});
|
136
|
+
|
137
|
+
it("should emit update event when setting credentials", async () => {
|
138
|
+
const handler = vi.fn();
|
139
|
+
authSecretStorage.onUpdate(handler);
|
140
|
+
|
141
|
+
await authSecretStorage.set({
|
142
|
+
accountID: "test123" as ID<Account>,
|
143
|
+
accountSecret:
|
144
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
145
|
+
provider: "passphrase",
|
146
|
+
});
|
147
|
+
|
148
|
+
expect(handler).toHaveBeenCalled();
|
149
|
+
});
|
150
|
+
});
|
151
|
+
|
152
|
+
describe("isAuthenticated", () => {
|
153
|
+
it("should return false when no data exists", async () => {
|
154
|
+
expect(authSecretStorage.isAuthenticated).toBe(false);
|
155
|
+
});
|
156
|
+
|
157
|
+
it("should return false for anonymous credentials", async () => {
|
158
|
+
await authSecretStorage.set({
|
159
|
+
accountID: "test123" as ID<Account>,
|
160
|
+
accountSecret:
|
161
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
162
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
163
|
+
provider: "anonymous",
|
164
|
+
});
|
165
|
+
expect(authSecretStorage.isAuthenticated).toBe(false);
|
166
|
+
});
|
167
|
+
|
168
|
+
it("should return true for non-anonymous credentials", async () => {
|
169
|
+
await authSecretStorage.set({
|
170
|
+
accountID: "test123" as ID<Account>,
|
171
|
+
accountSecret:
|
172
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
173
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
174
|
+
provider: "demo",
|
175
|
+
});
|
176
|
+
expect(authSecretStorage.isAuthenticated).toBe(true);
|
177
|
+
});
|
178
|
+
|
179
|
+
it("should return true when the provider is missing", async () => {
|
180
|
+
await authSecretStorage.set({
|
181
|
+
accountID: "test123" as ID<Account>,
|
182
|
+
accountSecret:
|
183
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
184
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
185
|
+
} as any);
|
186
|
+
expect(authSecretStorage.isAuthenticated).toBe(true);
|
187
|
+
});
|
188
|
+
});
|
189
|
+
|
190
|
+
describe("onUpdate", () => {
|
191
|
+
it("should add and remove event listener", () => {
|
192
|
+
const handler = vi.fn();
|
193
|
+
|
194
|
+
const removeListener = authSecretStorage.onUpdate(handler);
|
195
|
+
|
196
|
+
authSecretStorage.emitUpdate({
|
197
|
+
accountID: "test123" as ID<Account>,
|
198
|
+
accountSecret:
|
199
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
200
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
201
|
+
provider: "demo",
|
202
|
+
});
|
203
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
204
|
+
|
205
|
+
handler.mockClear();
|
206
|
+
|
207
|
+
removeListener();
|
208
|
+
authSecretStorage.emitUpdate({
|
209
|
+
accountID: "test123" as ID<Account>,
|
210
|
+
accountSecret:
|
211
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
212
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
213
|
+
provider: "anonymous",
|
214
|
+
});
|
215
|
+
expect(handler).not.toHaveBeenCalled();
|
216
|
+
});
|
217
|
+
|
218
|
+
it("should emit events only when the isAuthenticated state changes", () => {
|
219
|
+
const handler = vi.fn();
|
220
|
+
|
221
|
+
authSecretStorage.onUpdate(handler);
|
222
|
+
|
223
|
+
authSecretStorage.emitUpdate({
|
224
|
+
accountID: "test123" as ID<Account>,
|
225
|
+
accountSecret:
|
226
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
227
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
228
|
+
provider: "demo",
|
229
|
+
});
|
230
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
231
|
+
|
232
|
+
handler.mockClear();
|
233
|
+
|
234
|
+
authSecretStorage.emitUpdate({
|
235
|
+
accountID: "test123" as ID<Account>,
|
236
|
+
accountSecret:
|
237
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
238
|
+
secretSeed: new Uint8Array([1, 2, 3]),
|
239
|
+
provider: "demo",
|
240
|
+
});
|
241
|
+
expect(handler).not.toHaveBeenCalled();
|
242
|
+
});
|
243
|
+
});
|
244
|
+
|
245
|
+
describe("clear", () => {
|
246
|
+
it("should remove stored credentials", async () => {
|
247
|
+
await authSecretStorage.set({
|
248
|
+
accountID: "test123" as ID<Account>,
|
249
|
+
accountSecret:
|
250
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
251
|
+
provider: "passphrase",
|
252
|
+
});
|
253
|
+
|
254
|
+
await authSecretStorage.clear();
|
255
|
+
|
256
|
+
expect(await authSecretStorage.get()).toBeNull();
|
257
|
+
});
|
258
|
+
|
259
|
+
it("should emit update event when clearing", async () => {
|
260
|
+
await authSecretStorage.set({
|
261
|
+
accountID: "test123" as ID<Account>,
|
262
|
+
accountSecret:
|
263
|
+
"secret123" as `sealerSecret_z${string}/signerSecret_z${string}`,
|
264
|
+
provider: "passphrase",
|
265
|
+
});
|
266
|
+
|
267
|
+
const handler = vi.fn();
|
268
|
+
authSecretStorage.onUpdate(handler);
|
269
|
+
|
270
|
+
await authSecretStorage.clear();
|
271
|
+
|
272
|
+
expect(handler).toHaveBeenCalled();
|
273
|
+
});
|
274
|
+
});
|
275
|
+
});
|