jazz-react-native 0.8.44 → 0.8.47

Sign up to get free protection for your applications and to get access to all the features.
package/src/provider.tsx CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from "jazz-tools";
15
15
  import { Linking } from "react-native";
16
16
  import {
17
+ BaseReactNativeContextOptions,
17
18
  KvStore,
18
19
  KvStoreContext,
19
20
  ReactNativeContext,
@@ -27,6 +28,11 @@ import { ExpoSecureStoreAdapter } from "./storage/expo-secure-store-adapter.js";
27
28
  export function createJazzRNApp<Acc extends Account>({
28
29
  kvStore = new ExpoSecureStoreAdapter(),
29
30
  AccountSchema = Account as unknown as AccountClass<Acc>,
31
+ CryptoProvider,
32
+ }: {
33
+ kvStore?: KvStore;
34
+ AccountSchema?: AccountClass<Acc>;
35
+ CryptoProvider?: BaseReactNativeContextOptions["CryptoProvider"];
30
36
  } = {}): JazzReactApp<Acc> {
31
37
  const JazzContext = React.createContext<
32
38
  ReactNativeContext<Acc> | ReactNativeGuestContext | undefined
@@ -61,12 +67,14 @@ export function createJazzRNApp<Acc extends Account>({
61
67
  ? {
62
68
  peer,
63
69
  storage,
70
+ CryptoProvider,
64
71
  }
65
72
  : {
66
73
  AccountSchema,
67
74
  auth: auth,
68
75
  peer,
69
76
  storage,
77
+ CryptoProvider,
70
78
  },
71
79
  ).then((context) => {
72
80
  setCtx({
@@ -0,0 +1,282 @@
1
+ import { AgentSecret } from "cojson";
2
+ import { Account, ID } from "jazz-tools";
3
+ import { beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { RNDemoAuth } from "../auth/DemoAuthMethod";
5
+ import { KvStore, KvStoreContext } from "../storage/kv-store-context";
6
+
7
+ // Initialize mock storage
8
+ const mockStorage: { [key: string]: string } = {};
9
+
10
+ // Mock KvStore implementation
11
+ const mockKvStore: KvStore = {
12
+ get: vi.fn(async (key: string) => mockStorage[key] || null),
13
+ set: vi.fn(async (key: string, value: string) => {
14
+ mockStorage[key] = value;
15
+ }),
16
+ delete: vi.fn(async (key: string) => {
17
+ delete mockStorage[key];
18
+ }),
19
+ clearAll: vi.fn(async () => {
20
+ Object.keys(mockStorage).forEach((key) => delete mockStorage[key]);
21
+ }),
22
+ };
23
+
24
+ KvStoreContext.getInstance().initialize(mockKvStore);
25
+
26
+ beforeEach(() => {
27
+ mockKvStore.clearAll();
28
+ vi.clearAllMocks();
29
+ });
30
+
31
+ function setup() {
32
+ const mockDriver: RNDemoAuth.Driver = {
33
+ onReady: vi.fn(),
34
+ onSignedIn: vi.fn(),
35
+ onError: vi.fn(),
36
+ };
37
+
38
+ return {
39
+ mockStorage,
40
+ mockKvStore,
41
+ mockDriver,
42
+ };
43
+ }
44
+
45
+ describe("RNDemoAuth", () => {
46
+ describe("initialization", () => {
47
+ it("should initialize with seed accounts", async () => {
48
+ const { mockKvStore, mockDriver } = setup();
49
+
50
+ const seedAccounts = {
51
+ testUser: {
52
+ accountID: "test-account-id" as ID<Account>,
53
+ accountSecret: "test-secret" as AgentSecret,
54
+ },
55
+ };
56
+
57
+ await RNDemoAuth.init(mockDriver, seedAccounts);
58
+
59
+ expect(mockKvStore.set).toHaveBeenCalledWith(
60
+ "demo-auth-existing-users",
61
+ "testUser",
62
+ );
63
+ expect(mockKvStore.set).toHaveBeenCalledWith(
64
+ "demo-auth-existing-users-" + btoa("testUser"),
65
+ expect.any(String),
66
+ );
67
+ });
68
+ });
69
+
70
+ describe("authentication", () => {
71
+ it("should handle new user signup", async () => {
72
+ const { mockDriver } = setup();
73
+
74
+ mockDriver.onReady = vi.fn(({ signUp }) => {
75
+ signUp("testUser");
76
+ });
77
+
78
+ const auth = await RNDemoAuth.init(mockDriver);
79
+ const result = await auth.start();
80
+
81
+ expect(mockDriver.onReady).toHaveBeenCalled();
82
+ expect(result.type).toBe("new");
83
+ expect(result.saveCredentials).toBeDefined();
84
+ });
85
+
86
+ it("should handle existing user login", async () => {
87
+ const { mockStorage, mockDriver } = setup();
88
+
89
+ // Set up existing user in storage
90
+ const existingUser = {
91
+ accountID: "test-account-id" as ID<Account>,
92
+ accountSecret: "test-secret" as AgentSecret,
93
+ };
94
+
95
+ mockStorage["demo-auth-logged-in-secret"] = JSON.stringify(existingUser);
96
+
97
+ const auth = await RNDemoAuth.init(mockDriver);
98
+ const result = await auth.start();
99
+
100
+ if (result.type !== "existing") {
101
+ throw new Error("Result is not a existing user");
102
+ }
103
+
104
+ expect(result.type).toBe("existing");
105
+ expect(result.credentials).toEqual({
106
+ accountID: existingUser.accountID,
107
+ secret: existingUser.accountSecret,
108
+ });
109
+ });
110
+
111
+ it("should handle logout", async () => {
112
+ const { mockKvStore, mockDriver } = setup();
113
+
114
+ mockDriver.onReady = vi.fn(({ signUp }) => {
115
+ signUp("testUser");
116
+ });
117
+
118
+ const auth = await RNDemoAuth.init(mockDriver);
119
+ const result = await auth.start();
120
+
121
+ await result.logOut();
122
+ expect(mockKvStore.delete).toHaveBeenCalledWith(
123
+ "demo-auth-logged-in-secret",
124
+ );
125
+ });
126
+ });
127
+
128
+ describe("user management", () => {
129
+ it("should signup a new user", async () => {
130
+ const { mockKvStore, mockDriver } = setup();
131
+
132
+ mockDriver.onReady = vi.fn(({ signUp }) => {
133
+ return signUp("testUser");
134
+ });
135
+ const auth = await RNDemoAuth.init(mockDriver);
136
+ const result = await auth.start();
137
+
138
+ if (result.type !== "new") {
139
+ throw new Error("Result is not a new user");
140
+ }
141
+
142
+ await result.saveCredentials({
143
+ accountID: "test-account-id" as ID<Account>,
144
+ secret: "test-secret" as AgentSecret,
145
+ });
146
+
147
+ expect(mockKvStore.set).toHaveBeenCalledWith(
148
+ "demo-auth-existing-users-" + btoa("testUser"),
149
+ expect.any(String),
150
+ );
151
+
152
+ expect(mockKvStore.set).toHaveBeenCalledWith(
153
+ "demo-auth-existing-users",
154
+ "testUser",
155
+ );
156
+
157
+ expect(mockKvStore.set).toHaveBeenCalledWith(
158
+ "demo-auth-logged-in-secret",
159
+ expect.any(String),
160
+ );
161
+ });
162
+
163
+ it("should login an existing user", async () => {
164
+ const { mockStorage, mockKvStore, mockDriver } = setup();
165
+
166
+ const credentials = {
167
+ accountID: "test-account-id" as ID<Account>,
168
+ accountSecret: "test-secret" as AgentSecret,
169
+ };
170
+
171
+ mockStorage["demo-auth-existing-users-" + btoa("testUser")] =
172
+ JSON.stringify(credentials);
173
+
174
+ mockDriver.onReady = vi.fn(({ logInAs }) => {
175
+ return logInAs("testUser");
176
+ });
177
+
178
+ const auth = await RNDemoAuth.init(mockDriver);
179
+ const result = await auth.start();
180
+
181
+ if (result.type !== "existing") {
182
+ throw new Error("Result is not a existing user");
183
+ }
184
+
185
+ expect(result.credentials).toEqual({
186
+ accountID: credentials.accountID,
187
+ secret: credentials.accountSecret,
188
+ });
189
+
190
+ expect(mockKvStore.set).toHaveBeenCalledWith(
191
+ "demo-auth-logged-in-secret",
192
+ JSON.stringify(credentials),
193
+ );
194
+ });
195
+
196
+ it("should handle duplicate usernames by adding suffix", async () => {
197
+ const { mockStorage, mockKvStore, mockDriver } = setup();
198
+
199
+ mockDriver.onReady = vi.fn(({ signUp }) => {
200
+ return signUp("testUser");
201
+ });
202
+ mockStorage["demo-auth-existing-users"] = "testUser";
203
+
204
+ const auth = await RNDemoAuth.init(mockDriver);
205
+ const result = await auth.start();
206
+
207
+ if (result.type !== "new") {
208
+ throw new Error("Result is not a new user");
209
+ }
210
+
211
+ await result.saveCredentials({
212
+ accountID: "test-account-id" as ID<Account>,
213
+ secret: "test-secret" as AgentSecret,
214
+ });
215
+
216
+ expect(mockKvStore.set).toHaveBeenCalledWith(
217
+ "demo-auth-existing-users-" + btoa("testUser-2"),
218
+ expect.any(String),
219
+ );
220
+
221
+ expect(mockKvStore.set).toHaveBeenCalledWith(
222
+ "demo-auth-existing-users",
223
+ "testUser,testUser-2",
224
+ );
225
+ });
226
+
227
+ it("should retrieve existing users", async () => {
228
+ const { mockStorage, mockDriver } = setup();
229
+
230
+ mockDriver.onReady = vi.fn(({ signUp }) => {
231
+ return signUp("testUser");
232
+ });
233
+
234
+ mockStorage["demo-auth-existing-users"] = "user1,user2,user3";
235
+
236
+ const auth = await RNDemoAuth.init(mockDriver);
237
+ const result = await auth.start();
238
+
239
+ if (result.type !== "new") {
240
+ throw new Error("Result is not a new user");
241
+ }
242
+
243
+ await result.saveCredentials({
244
+ accountID: "test-account-id" as ID<Account>,
245
+ secret: "test-secret" as AgentSecret,
246
+ });
247
+
248
+ const onReadyCall = vi.mocked(mockDriver.onReady).mock.calls[0]![0];
249
+ const existingUsers = await onReadyCall.getExistingUsers();
250
+
251
+ expect(existingUsers).toEqual(["user1", "user2", "user3", "testUser"]);
252
+ });
253
+
254
+ it("should migrate legacy user keys to the new format", async () => {
255
+ const { mockStorage, mockKvStore, mockDriver } = setup();
256
+
257
+ const value = JSON.stringify({
258
+ accountID: "test-account-id" as ID<Account>,
259
+ accountSecret: "test-secret" as AgentSecret,
260
+ });
261
+
262
+ mockStorage["demo-auth-existing-users"] = "testUser";
263
+ mockStorage["demo-auth-existing-users-testUser"] = value;
264
+
265
+ await RNDemoAuth.init(mockDriver);
266
+
267
+ expect(mockKvStore.set).toHaveBeenCalledWith(
268
+ "demo-auth-existing-users-" + btoa("testUser"),
269
+ value,
270
+ );
271
+
272
+ expect(mockKvStore.set).toHaveBeenCalledWith(
273
+ "demo-auth-storage-version",
274
+ "2",
275
+ );
276
+
277
+ expect(mockKvStore.delete).toHaveBeenCalledWith(
278
+ "demo-auth-existing-users-testUser",
279
+ );
280
+ });
281
+ });
282
+ });