mirror of
https://github.com/tompro/sattle.git
synced 2026-10-26 01:49:58 +00:00
122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import { test, expect } from '../fixtures';
|
|
|
|
// Wallet lifecycle in the real browser: an unsupported LEGACY (alpha)
|
|
// install - the linking-key-only record, the pre-v2 bearer store and their
|
|
// ownerless residue - is never migrated (the cash root is unrecoverable
|
|
// from a linking key), so boot resets the device to the uninstalled state
|
|
// before any route can expose it, and a successor wallet starts without
|
|
// any of the old residue.
|
|
|
|
// every namespace the alpha wallet could have touched
|
|
const LEGACY_KEYS = [
|
|
'sattle_linking_key',
|
|
'sattle_bearers',
|
|
'sattle_wallet_material_v2',
|
|
'sattle_passkey_slots',
|
|
'sattle_nwc_connections',
|
|
'sattle_nwc_enabled',
|
|
'sattle_trusted_mints',
|
|
'sattle_biometric_wrap',
|
|
'sattle_activity',
|
|
'sattle_settings',
|
|
] as const;
|
|
|
|
test.describe('wallet lifecycle', () => {
|
|
test('a legacy alpha install is reset at boot and the successor starts clean', async ({
|
|
page,
|
|
}) => {
|
|
// Given a legacy encrypted linking-key record plus ownerless residue
|
|
await page.addInitScript(() => {
|
|
localStorage.setItem(
|
|
'sattle_linking_key',
|
|
JSON.stringify({
|
|
enc: true,
|
|
salt: '11'.repeat(16),
|
|
iv: '22'.repeat(12),
|
|
ciphertext: '33'.repeat(48),
|
|
}),
|
|
);
|
|
localStorage.setItem(
|
|
'sattle_bearers',
|
|
JSON.stringify([{ url: 'https://mint.example/note?k1=legacy', amount: 1000 }]),
|
|
);
|
|
localStorage.setItem(
|
|
'sattle_passkey_slots',
|
|
JSON.stringify([
|
|
{
|
|
credentialId: '11'.repeat(16),
|
|
hkdfSalt: '22'.repeat(16),
|
|
iv: '33'.repeat(12),
|
|
wrappedKey: '44'.repeat(48),
|
|
createdAt: 1,
|
|
},
|
|
]),
|
|
);
|
|
localStorage.setItem(
|
|
'sattle_nwc_connections',
|
|
JSON.stringify([
|
|
{
|
|
clientPubkey: '55'.repeat(32),
|
|
relays: ['wss://relay.example'],
|
|
budget: { maxMsat: 1000, periodMs: 60_000 },
|
|
spent: { periodStart: 0, msat: 0 },
|
|
createdAt: 1,
|
|
},
|
|
]),
|
|
);
|
|
localStorage.setItem('sattle_nwc_enabled', 'true');
|
|
localStorage.setItem(
|
|
'sattle_trusted_mints',
|
|
JSON.stringify([
|
|
{
|
|
server: 'legacy.example',
|
|
mintPubkey: `02${'aa'.repeat(32)}`,
|
|
addedAt: 1,
|
|
locked: false,
|
|
},
|
|
]),
|
|
);
|
|
});
|
|
|
|
// When the app boots
|
|
await page.goto('/#/');
|
|
|
|
// Then the alpha reset landed on the uninstalled state - no unlock
|
|
// surface for a wallet that can never be derived again
|
|
await expect(page.getByRole('button', { name: 'Get started' })).toBeVisible();
|
|
await expect(page.getByText('Wallet locked')).toHaveCount(0);
|
|
|
|
// ...and every namespace the alpha wallet could have touched is gone
|
|
const remaining = await page.evaluate(
|
|
(keys) => keys.filter((key) => localStorage.getItem(key) !== null),
|
|
[...LEGACY_KEYS],
|
|
);
|
|
expect(remaining).toEqual([]);
|
|
|
|
// When a successor wallet is created
|
|
await page.getByRole('button', { name: 'Get started' }).click();
|
|
await page.getByRole('button', { name: 'Create wallet' }).click();
|
|
await page.locator('.q-checkbox', { hasText: 'I wrote it down' }).click();
|
|
await page.getByRole('button', { name: 'Continue' }).click();
|
|
|
|
// Then it unlocks into a working wallet with zero adopted residue
|
|
// (settings/activity are the live app's own namespaces - the residue
|
|
// check covers only the stores a predecessor could have planted)
|
|
await expect(page.getByRole('button', { name: 'Receive' })).toBeVisible();
|
|
await expect(page.locator('.balance-card .text-h2')).toHaveText('0');
|
|
const residue = await page.evaluate(
|
|
(keys) => keys.filter((key) => localStorage.getItem(key) !== null),
|
|
[
|
|
'sattle_linking_key',
|
|
'sattle_bearers',
|
|
'sattle_passkey_slots',
|
|
'sattle_nwc_connections',
|
|
'sattle_nwc_enabled',
|
|
'sattle_trusted_mints',
|
|
'sattle_biometric_wrap',
|
|
],
|
|
);
|
|
expect(residue).toEqual([]);
|
|
});
|
|
});
|