routstrd 0.4.7 → 0.4.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.
@@ -1,378 +0,0 @@
1
- import { afterEach, describe, expect, it } from "bun:test";
2
- import { Database } from "bun:sqlite";
3
- import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs";
4
- import { tmpdir } from "os";
5
- import { join } from "path";
6
- import {
7
- diagnoseWallets,
8
- mnemonicFingerprint,
9
- renderWalletDoctor,
10
- summarizeWalletDirectory,
11
- WalletMigrationConflictError,
12
- } from "./diagnostics";
13
-
14
- const roots: string[] = [];
15
- function root(): string {
16
- const path = mkdtempSync(join(tmpdir(), "routstrd-diagnostics-"));
17
- roots.push(path);
18
- return path;
19
- }
20
- afterEach(() => {
21
- for (const path of roots.splice(0)) rmSync(path, { recursive: true, force: true });
22
- });
23
-
24
- const MNEMONIC_A =
25
- "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
26
- const MNEMONIC_B =
27
- "legal winner thank year wave sausage worth useful legal winner thank yellow";
28
-
29
- function writeWalletConfig(dir: string, config: unknown): void {
30
- mkdirSync(dir, { recursive: true });
31
- writeFileSync(join(dir, "config.json"), JSON.stringify(config));
32
- }
33
-
34
- function writeFakeDbFile(dir: string): void {
35
- mkdirSync(dir, { recursive: true });
36
- writeFileSync(join(dir, "coco.db"), "not a real sqlite database");
37
- }
38
-
39
- function writeProofsDb(dir: string): void {
40
- mkdirSync(dir, { recursive: true });
41
- const db = new Database(join(dir, "coco.db"));
42
- db.exec(`
43
- CREATE TABLE coco_cashu_proofs (mintUrl TEXT, state TEXT, amount INTEGER);
44
- INSERT INTO coco_cashu_proofs VALUES ('https://mint.example', 'ready', 100);
45
- INSERT INTO coco_cashu_proofs VALUES ('https://mint.example', 'ready', 50);
46
- INSERT INTO coco_cashu_proofs VALUES ('https://mint.example', 'pending', 30);
47
- INSERT INTO coco_cashu_proofs VALUES ('https://other.example', 'spent', 20);
48
- CREATE TABLE coco_cashu_mints (mintUrl TEXT, trusted INTEGER);
49
- INSERT INTO coco_cashu_mints VALUES ('https://mint.example', 1);
50
- INSERT INTO coco_cashu_mints VALUES ('https://other.example', 1);
51
- INSERT INTO coco_cashu_mints VALUES ('https://third.example', 0);
52
- `);
53
- db.close();
54
- }
55
-
56
- describe("mnemonicFingerprint", () => {
57
- it("is deterministic and normalizes whitespace", () => {
58
- const base = mnemonicFingerprint(MNEMONIC_A);
59
- expect(mnemonicFingerprint(MNEMONIC_A)).toBe(base);
60
- expect(
61
- mnemonicFingerprint(" abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about "),
62
- ).toBe(base);
63
- });
64
-
65
- it("distinguishes different mnemonics and never returns the mnemonic", () => {
66
- const a = mnemonicFingerprint(MNEMONIC_A);
67
- const b = mnemonicFingerprint(MNEMONIC_B);
68
- expect(a).not.toBe(b);
69
- expect(a).not.toContain("abandon");
70
- expect(b).not.toContain("legal");
71
- });
72
- });
73
-
74
- describe("summarizeWalletDirectory", () => {
75
- it("reports an empty directory", () => {
76
- const dir = join(root(), "empty");
77
- const diag = summarizeWalletDirectory(dir, "canonical");
78
- expect(diag.config.exists).toBe(false);
79
- expect(diag.db.exists).toBe(false);
80
- });
81
-
82
- it("summarizes a decrypted config with fingerprint and metadata", () => {
83
- const dir = join(root(), "wallet");
84
- writeWalletConfig(dir, {
85
- version: 1,
86
- mnemonic: MNEMONIC_A,
87
- encrypted: false,
88
- createdAt: "2026-08-20T14:03:22.000Z",
89
- defaultMintUrl: "https://mint.example",
90
- });
91
- const diag = summarizeWalletDirectory(dir, "canonical");
92
- expect(diag.config.exists).toBe(true);
93
- expect(diag.config.fingerprint).toBe(mnemonicFingerprint(MNEMONIC_A));
94
- expect(diag.config.hasMnemonic).toBe(true);
95
- expect(diag.config.encrypted).toBe(false);
96
- expect(diag.config.createdAt).toBe("2026-08-20T14:03:22.000Z");
97
- expect(diag.config.defaultMintUrl).toBe("https://mint.example");
98
- expect(diag.config.mtimeMs).toBeTypeOf("number");
99
- });
100
-
101
- it("does not derive a fingerprint for encrypted wallets", () => {
102
- const dir = join(root(), "encrypted");
103
- writeWalletConfig(dir, { mnemonic: MNEMONIC_A, encrypted: true });
104
- const diag = summarizeWalletDirectory(dir, "canonical");
105
- expect(diag.config.exists).toBe(true);
106
- expect(diag.config.encrypted).toBe(true);
107
- expect(diag.config.fingerprint).toBeUndefined();
108
- });
109
-
110
- it("degrades gracefully when config.json is malformed", () => {
111
- const dir = join(root(), "bad-config");
112
- mkdirSync(dir, { recursive: true });
113
- writeFileSync(join(dir, "config.json"), "not json");
114
- const diag = summarizeWalletDirectory(dir, "canonical");
115
- expect(diag.config.exists).toBe(true);
116
- expect(diag.config.error).toBeTruthy();
117
- expect(diag.config.fingerprint).toBeUndefined();
118
- });
119
-
120
- it("summarizes a proof database without opening it read-write", () => {
121
- const dir = join(root(), "with-db");
122
- writeProofsDb(dir);
123
- const diag = summarizeWalletDirectory(dir, "canonical");
124
- expect(diag.db.exists).toBe(true);
125
- expect(diag.db.summary).toBeDefined();
126
- expect(diag.db.summary?.totalProofs).toBe(4);
127
- expect(diag.db.summary?.totalAmount).toBe(200);
128
- expect(diag.db.summary?.distinctMints).toBe(2);
129
- expect(diag.db.summary?.mints).toHaveLength(3);
130
- expect(diag.db.summary?.amountByState).toEqual({
131
- ready: 150,
132
- pending: 30,
133
- spent: 20,
134
- });
135
- });
136
- });
137
-
138
- describe("WalletMigrationConflictError", () => {
139
- it("renders a structured conflict message without leaking mnemonics", () => {
140
- const targetDir = join(root(), "wallet");
141
- const sourceDir = join(root(), ".cocod");
142
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
143
- writeWalletConfig(sourceDir, { mnemonic: MNEMONIC_B });
144
-
145
- const err = new WalletMigrationConflictError(
146
- summarizeWalletDirectory(targetDir, "canonical"),
147
- summarizeWalletDirectory(sourceDir, "legacy"),
148
- );
149
- expect(err.target.dir).toBe(targetDir);
150
- expect(err.source.dir).toBe(sourceDir);
151
- expect(err.message).toContain(targetDir);
152
- expect(err.message).toContain(sourceDir);
153
- expect(err.message).toContain("routstrd wallet doctor");
154
- expect(err.message).toContain("routstrd stop");
155
- expect(err.message).not.toContain("abandon");
156
- expect(err.message).not.toContain("legal winner");
157
- });
158
- });
159
-
160
- describe("renderWalletDoctor", () => {
161
- it("reports different mnemonics as a conflict with resolution steps", () => {
162
- const targetDir = join(root(), "wallet");
163
- const sourceDir = join(root(), ".cocod");
164
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
165
- writeWalletConfig(sourceDir, { mnemonic: MNEMONIC_B });
166
-
167
- const report = renderWalletDoctor(
168
- summarizeWalletDirectory(targetDir, "canonical"),
169
- summarizeWalletDirectory(sourceDir, "legacy"),
170
- );
171
- expect(report).toContain("DIFFERENT mnemonics");
172
- expect(report).toContain("routstrd stop");
173
- expect(report).toContain("mv \"");
174
- });
175
-
176
- it("reports matching mnemonics and warns startup still refuses", () => {
177
- const targetDir = join(root(), "wallet");
178
- const sourceDir = join(root(), ".cocod");
179
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
180
- // Same mnemonic, but different bytes (extra metadata) — migration compares
181
- // files, not mnemonics, so startup still refuses.
182
- writeWalletConfig(sourceDir, {
183
- mnemonic: MNEMONIC_A,
184
- createdAt: "2026-01-01T00:00:00.000Z",
185
- });
186
-
187
- const report = renderWalletDoctor(
188
- summarizeWalletDirectory(targetDir, "canonical"),
189
- summarizeWalletDirectory(sourceDir, "legacy"),
190
- );
191
- expect(report).toContain("share the same mnemonic");
192
- expect(report).toContain("startup still refuses");
193
- expect(report).toContain("mv \"");
194
- });
195
-
196
- it("omits resolution steps when there is nothing to resolve", () => {
197
- const targetDir = join(root(), "wallet");
198
- const emptyDir = join(root(), ".cocod");
199
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
200
-
201
- const report = renderWalletDoctor(
202
- summarizeWalletDirectory(targetDir, "canonical"),
203
- summarizeWalletDirectory(emptyDir, "legacy"),
204
- );
205
- expect(report).toContain("no migration needed");
206
- expect(report).not.toContain("routstrd stop");
207
- expect(report).not.toContain("mv \"");
208
- });
209
-
210
- it("omits resolution steps for a fresh install", () => {
211
- const report = renderWalletDoctor(
212
- summarizeWalletDirectory(join(root(), "nope"), "canonical"),
213
- summarizeWalletDirectory(join(root(), "alsonope"), "legacy"),
214
- );
215
- expect(report).toContain("fresh install");
216
- expect(report).not.toContain("mv \"");
217
- });
218
-
219
- it("counts trusted mints from the mints table and formats amounts", () => {
220
- const targetDir = join(root(), "wallet");
221
- const sourceDir = join(root(), ".cocod");
222
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
223
- writeWalletConfig(sourceDir, { mnemonic: MNEMONIC_B });
224
- writeProofsDb(sourceDir);
225
-
226
- const report = renderWalletDoctor(
227
- summarizeWalletDirectory(targetDir, "canonical"),
228
- summarizeWalletDirectory(sourceDir, "legacy"),
229
- );
230
- // 3 registered mints, not the 2 mints that happen to hold proofs.
231
- expect(report).toContain("3 mints");
232
- });
233
-
234
- it("formats large balances with thousands separators", () => {
235
- const targetDir = join(root(), "wallet");
236
- const sourceDir = join(root(), ".cocod");
237
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
238
- writeWalletConfig(sourceDir, { mnemonic: MNEMONIC_B });
239
- mkdirSync(sourceDir, { recursive: true });
240
- const db = new Database(join(sourceDir, "coco.db"));
241
- db.exec(`
242
- CREATE TABLE coco_cashu_proofs (mintUrl TEXT, state TEXT, amount INTEGER);
243
- INSERT INTO coco_cashu_proofs VALUES ('https://mint.example', 'ready', 21000);
244
- `);
245
- db.close();
246
-
247
- const report = renderWalletDoctor(
248
- summarizeWalletDirectory(targetDir, "canonical"),
249
- summarizeWalletDirectory(sourceDir, "legacy"),
250
- );
251
- expect(report).toContain("ready: 21,000 sats");
252
- });
253
- });
254
-
255
- describe("diagnoseWallets", () => {
256
- it("flags conflicting mnemonics as a conflict needing resolution", () => {
257
- const targetDir = join(root(), "wallet");
258
- const sourceDir = join(root(), ".cocod");
259
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
260
- writeWalletConfig(sourceDir, { mnemonic: MNEMONIC_B });
261
-
262
- const verdict = diagnoseWallets(
263
- summarizeWalletDirectory(targetDir, "canonical"),
264
- summarizeWalletDirectory(sourceDir, "legacy"),
265
- );
266
- expect(verdict.conflict).toBe(true);
267
- expect(verdict.showResolution).toBe(true);
268
- });
269
-
270
- it("treats a single existing wallet as no conflict", () => {
271
- const targetDir = join(root(), "wallet");
272
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
273
-
274
- const verdict = diagnoseWallets(
275
- summarizeWalletDirectory(targetDir, "canonical"),
276
- summarizeWalletDirectory(join(root(), ".cocod"), "legacy"),
277
- );
278
- expect(verdict.conflict).toBe(false);
279
- expect(verdict.showResolution).toBe(false);
280
- });
281
-
282
- it("flags a legacy database without config as an incomplete conflict", () => {
283
- const sourceDir = join(root(), ".cocod");
284
- writeProofsDb(sourceDir);
285
-
286
- const verdict = diagnoseWallets(
287
- summarizeWalletDirectory(join(root(), "wallet"), "canonical"),
288
- summarizeWalletDirectory(sourceDir, "legacy"),
289
- );
290
- expect(verdict.conflict).toBe(true);
291
- expect(verdict.text).toContain("incomplete");
292
- });
293
-
294
- it("treats a stray legacy database as already-current when the canonical wallet exists", () => {
295
- const targetDir = join(root(), "wallet");
296
- const sourceDir = join(root(), ".cocod");
297
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
298
- writeFakeDbFile(sourceDir);
299
-
300
- const verdict = diagnoseWallets(
301
- summarizeWalletDirectory(targetDir, "canonical"),
302
- summarizeWalletDirectory(sourceDir, "legacy"),
303
- );
304
- expect(verdict.conflict).toBe(false);
305
- expect(verdict.showResolution).toBe(false);
306
- });
307
-
308
- it("flags an incomplete canonical database as a conflict", () => {
309
- const targetDir = join(root(), "wallet");
310
- writeFakeDbFile(targetDir);
311
-
312
- const verdict = diagnoseWallets(
313
- summarizeWalletDirectory(targetDir, "canonical"),
314
- summarizeWalletDirectory(join(root(), ".cocod"), "legacy"),
315
- );
316
- expect(verdict.conflict).toBe(true);
317
- expect(verdict.showResolution).toBe(false);
318
- });
319
-
320
- it("flags orphaned legacy sidecars as a conflict", () => {
321
- const sourceDir = join(root(), ".cocod");
322
- mkdirSync(sourceDir, { recursive: true });
323
- writeFileSync(join(sourceDir, "coco.db-wal"), "wal");
324
- writeFileSync(join(sourceDir, "coco.db-shm"), "shm");
325
-
326
- const verdict = diagnoseWallets(
327
- summarizeWalletDirectory(join(root(), "wallet"), "canonical"),
328
- summarizeWalletDirectory(sourceDir, "legacy"),
329
- );
330
- expect(verdict.conflict).toBe(true);
331
- expect(verdict.showResolution).toBe(false);
332
- });
333
-
334
- it("treats byte-identical wallets as already-current", () => {
335
- const targetDir = join(root(), "wallet");
336
- const sourceDir = join(root(), ".cocod");
337
- writeWalletConfig(targetDir, { mnemonic: MNEMONIC_A });
338
- writeWalletConfig(sourceDir, { mnemonic: MNEMONIC_A });
339
-
340
- const verdict = diagnoseWallets(
341
- summarizeWalletDirectory(targetDir, "canonical"),
342
- summarizeWalletDirectory(sourceDir, "legacy"),
343
- );
344
- expect(verdict.conflict).toBe(false);
345
- expect(verdict.showResolution).toBe(false);
346
- });
347
-
348
- it("survives an unreadable wallet file without crashing", () => {
349
- const targetDir = join(root(), "wallet");
350
- const sourceDir = join(root(), ".cocod");
351
- // Equal sizes force the classifier's filesEqual past the size check into
352
- // raw reads, where the permission error would otherwise crash the doctor.
353
- mkdirSync(targetDir, { recursive: true });
354
- mkdirSync(sourceDir, { recursive: true });
355
- writeFileSync(join(targetDir, "config.json"), `{"mnemonic":"${"a".repeat(50)}"}`);
356
- const sourceConfig = join(sourceDir, "config.json");
357
- writeFileSync(sourceConfig, `{"mnemonic":"${"b".repeat(50)}"}`);
358
- chmodSync(sourceConfig, 0o000);
359
-
360
- try {
361
- const target = summarizeWalletDirectory(targetDir, "canonical");
362
- const source = summarizeWalletDirectory(sourceDir, "legacy");
363
- expect(source.config.error).toBeTruthy();
364
-
365
- const verdict = diagnoseWallets(target, source);
366
- expect(verdict.conflict).toBe(true);
367
- expect(verdict.showResolution).toBe(true);
368
- expect(verdict.text).toContain("could not be fully read");
369
-
370
- // The full report must render too — this is the user-facing path.
371
- const report = renderWalletDoctor(target, source);
372
- expect(report).toContain("could not be fully read");
373
- expect(report).toContain("mv \"");
374
- } finally {
375
- chmodSync(sourceConfig, 0o600);
376
- }
377
- });
378
- });
@@ -1,171 +0,0 @@
1
- import { afterEach, describe, expect, it, mock } from "bun:test";
2
- import { Database } from "bun:sqlite";
3
- import {
4
- existsSync,
5
- mkdirSync,
6
- mkdtempSync,
7
- readFileSync,
8
- readdirSync,
9
- rmSync,
10
- statSync,
11
- writeFileSync,
12
- } from "fs";
13
- import { tmpdir } from "os";
14
- import { join } from "path";
15
- import { migrateLegacyWallet } from "./migration";
16
- import { WalletMigrationConflictError } from "./diagnostics";
17
-
18
- const roots: string[] = [];
19
- function root(): string {
20
- const path = mkdtempSync(join(tmpdir(), "routstrd-migration-"));
21
- roots.push(path);
22
- return path;
23
- }
24
- afterEach(() => {
25
- for (const path of roots.splice(0)) rmSync(path, { recursive: true, force: true });
26
- });
27
-
28
- describe("migrateLegacyWallet", () => {
29
- it("snapshots committed WAL data and leaves process files behind", async () => {
30
- const base = root();
31
- const legacyDir = join(base, ".cocod");
32
- const walletDir = join(base, ".routstrd", "wallet");
33
- mkdirSync(legacyDir);
34
- const config = JSON.stringify({
35
- mnemonic: "seed words",
36
- encrypted: false,
37
- defaultMintUrl: "https://mint.example",
38
- unknown: { preserved: true },
39
- });
40
- writeFileSync(join(legacyDir, "config.json"), config);
41
- writeFileSync(join(legacyDir, "cocod.pid"), "123");
42
- writeFileSync(join(legacyDir, "cocod.sock"), "not-a-real-socket");
43
-
44
- const source = new Database(join(legacyDir, "coco.db"));
45
- source.exec("PRAGMA journal_mode=WAL; PRAGMA wal_autocheckpoint=0");
46
- source.exec(`
47
- CREATE TABLE coco_cashu_proofs (mintUrl TEXT, state TEXT, amount INTEGER);
48
- CREATE TABLE coco_cashu_counters (mintUrl TEXT, keysetId TEXT, counter INTEGER);
49
- CREATE TABLE coco_cashu_mint_operations (state TEXT);
50
- CREATE TABLE coco_cashu_send_operations (state TEXT);
51
- CREATE TABLE coco_cashu_melt_operations (state TEXT);
52
- CREATE TABLE coco_cashu_mints (mintUrl TEXT, trusted INTEGER);
53
- INSERT INTO coco_cashu_proofs VALUES ('https://mint.example', 'ready', 21);
54
- INSERT INTO coco_cashu_counters VALUES ('https://mint.example', 'keyset', 3);
55
- INSERT INTO coco_cashu_mints VALUES ('https://mint.example', 1);
56
- `);
57
- expect(existsSync(join(legacyDir, "coco.db-wal"))).toBe(true);
58
-
59
- const assertStopped = mock(async () => {});
60
- const releaseLock = mock(() => {});
61
- const acquireLock = mock(() => releaseLock);
62
- const result = await migrateLegacyWallet({
63
- walletDir,
64
- legacyDir,
65
- assertLegacyStopped: assertStopped,
66
- acquireLegacyLock: acquireLock,
67
- });
68
- source.close();
69
-
70
- expect(result.status).toBe("migrated");
71
- expect(assertStopped).toHaveBeenCalledTimes(2);
72
- expect(acquireLock).toHaveBeenCalledTimes(1);
73
- expect(releaseLock).toHaveBeenCalledTimes(1);
74
- expect(readFileSync(join(walletDir, "config.json"), "utf8")).toBe(config);
75
- const migrated = new Database(join(walletDir, "coco.db"), { readonly: true });
76
- expect(migrated.query("PRAGMA quick_check").values()).toEqual([["ok"]]);
77
- expect(migrated.query("SELECT state, amount FROM coco_cashu_proofs").values()).toEqual([
78
- ["ready", 21],
79
- ]);
80
- expect(migrated.query("SELECT counter FROM coco_cashu_counters").get()).toEqual({
81
- counter: 3,
82
- });
83
- migrated.close();
84
- expect(existsSync(join(walletDir, "coco.db-wal"))).toBe(false);
85
- expect(statSync(walletDir).mode & 0o777).toBe(0o700);
86
- expect(statSync(join(walletDir, "config.json")).mode & 0o777).toBe(0o600);
87
- expect(existsSync(join(walletDir, "cocod.pid"))).toBe(false);
88
- expect(existsSync(join(walletDir, "cocod.sock"))).toBe(false);
89
- expect(existsSync(join(legacyDir, "config.json"))).toBe(false);
90
- expect(existsSync(join(legacyDir, "coco.db"))).toBe(false);
91
- expect(existsSync(join(legacyDir, "cocod.pid"))).toBe(true);
92
- expect(existsSync(join(legacyDir, "cocod.sock"))).toBe(true);
93
- const archive = readdirSync(legacyDir).find((name) => name.startsWith("wallet-migrated-"));
94
- expect(archive).toBeDefined();
95
- expect(existsSync(join(legacyDir, archive!, "config.json"))).toBe(true);
96
- expect(existsSync(join(legacyDir, archive!, "coco.db"))).toBe(true);
97
- });
98
-
99
- it("treats a config-only initialized wallet as migratable", async () => {
100
- const base = root();
101
- const legacyDir = join(base, ".cocod");
102
- const walletDir = join(base, ".routstrd", "wallet");
103
- mkdirSync(legacyDir);
104
- writeFileSync(join(legacyDir, "config.json"), '{"mnemonic":"seed"}');
105
-
106
- expect((await migrateLegacyWallet({ walletDir, legacyDir })).status).toBe("migrated");
107
- expect(existsSync(join(walletDir, "config.json"))).toBe(true);
108
- expect(existsSync(join(walletDir, "coco.db"))).toBe(false);
109
- });
110
-
111
- it("refuses a database without its mnemonic config", async () => {
112
- const base = root();
113
- const legacyDir = join(base, ".cocod");
114
- mkdirSync(legacyDir);
115
- writeFileSync(join(legacyDir, "coco.db"), "database");
116
-
117
- await expect(migrateLegacyWallet({ walletDir: join(base, "wallet"), legacyDir })).rejects.toThrow(
118
- "without config.json",
119
- );
120
- });
121
-
122
- it("refuses orphaned SQLite sidecars", async () => {
123
- const base = root();
124
- const legacyDir = join(base, ".cocod");
125
- mkdirSync(legacyDir);
126
- writeFileSync(join(legacyDir, "coco.db-wal"), "orphaned WAL");
127
-
128
- await expect(migrateLegacyWallet({ walletDir: join(base, "wallet"), legacyDir })).rejects.toThrow(
129
- "sidecar files without coco.db",
130
- );
131
- });
132
-
133
- it("refuses when both canonical and legacy configs exist", async () => {
134
- const base = root();
135
- const legacyDir = join(base, ".cocod");
136
- const walletDir = join(base, ".routstrd", "wallet");
137
- mkdirSync(legacyDir, { recursive: true });
138
- mkdirSync(walletDir, { recursive: true });
139
- writeFileSync(
140
- join(legacyDir, "config.json"),
141
- JSON.stringify({ mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" }),
142
- );
143
- writeFileSync(
144
- join(walletDir, "config.json"),
145
- JSON.stringify({ mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow" }),
146
- );
147
-
148
- let thrown: unknown;
149
- try {
150
- await migrateLegacyWallet({ walletDir, legacyDir });
151
- } catch (error) {
152
- thrown = error;
153
- }
154
-
155
- expect(thrown).toBeInstanceOf(WalletMigrationConflictError);
156
- const err = thrown as WalletMigrationConflictError;
157
- expect(err.target.dir).toBe(walletDir);
158
- expect(err.source.dir).toBe(legacyDir);
159
- expect(err.message).toContain("two different wallets were found");
160
- expect(err.message).toContain(walletDir);
161
- expect(err.message).toContain(legacyDir);
162
- expect(err.message).not.toContain("abandon");
163
- expect(err.message).not.toContain("legal winner");
164
- expect(readFileSync(join(walletDir, "config.json"), "utf8")).toContain(
165
- "legal winner",
166
- );
167
- expect(readFileSync(join(legacyDir, "config.json"), "utf8")).toContain(
168
- "abandon abandon",
169
- );
170
- });
171
- });