learning_ai_common_plat/packages/secure-storage-web/src/secure-storage.test.ts
saravanakumardb1 ce08587680 feat(secure-storage-web): create @bytelyst/secure-storage-web — encrypted IndexedDB storage
- IndexedDB-backed key-value store with non-extractable AES-256-GCM CryptoKey
- Key material never leaves browser crypto subsystem
- API: set, get, delete, clear, has, keys — all async
- Namespace isolation for multi-app usage
- Falls back to localStorage when SubtleCrypto unavailable
- 16 Vitest tests (fake-indexeddb), all passing
- Add IndexedDB globals to root ESLint config
2026-03-21 11:19:19 -07:00

124 lines
4.1 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest';
import 'fake-indexeddb/auto';
import { SecureStorage } from './secure-storage.js';
describe('SecureStorage', () => {
let storage: SecureStorage;
beforeEach(async () => {
storage = new SecureStorage('com.test.app');
await storage.clear();
});
// ── Basic CRUD ──────────────────────────────────
it('set and get', async () => {
await storage.set('token', 'abc123');
const val = await storage.get('token');
expect(val).toBe('abc123');
});
it('get returns null for missing key', async () => {
const val = await storage.get('nonexistent');
expect(val).toBeNull();
});
it('overwrite existing key', async () => {
await storage.set('token', 'first');
await storage.set('token', 'second');
const val = await storage.get('token');
expect(val).toBe('second');
});
it('delete removes value', async () => {
await storage.set('token', 'abc');
await storage.delete('token');
const val = await storage.get('token');
expect(val).toBeNull();
});
it('delete non-existent key does not throw', async () => {
await expect(storage.delete('nope')).resolves.toBeUndefined();
});
// ── Clear ───────────────────────────────────────
it('clear removes all values', async () => {
await storage.set('a', '1');
await storage.set('b', '2');
await storage.clear();
expect(await storage.get('a')).toBeNull();
expect(await storage.get('b')).toBeNull();
});
// ── Has ─────────────────────────────────────────
it('has returns true for existing key', async () => {
await storage.set('x', 'val');
expect(await storage.has('x')).toBe(true);
});
it('has returns false for missing key', async () => {
expect(await storage.has('missing')).toBe(false);
});
// ── Keys ────────────────────────────────────────
it('keys lists stored keys', async () => {
await storage.set('alpha', '1');
await storage.set('beta', '2');
const keys = await storage.keys();
expect(keys.sort()).toEqual(['alpha', 'beta']);
});
it('keys returns empty for fresh storage', async () => {
const keys = await storage.keys();
expect(keys).toEqual([]);
});
// ── Namespace isolation ─────────────────────────
it('different namespaces are isolated', async () => {
const s1 = new SecureStorage('ns1');
const s2 = new SecureStorage('ns2');
await s1.set('key', 'from-s1');
await s2.set('key', 'from-s2');
expect(await s1.get('key')).toBe('from-s1');
expect(await s2.get('key')).toBe('from-s2');
});
// ── Data types ──────────────────────────────────
it('empty string', async () => {
await storage.set('empty', '');
const val = await storage.get('empty');
expect(val).toBe('');
});
it('unicode values', async () => {
const text = 'こんにちは世界 🌍 مرحبا Ñoño';
await storage.set('unicode', text);
expect(await storage.get('unicode')).toBe(text);
});
it('large value', async () => {
const big = 'X'.repeat(100_000);
await storage.set('big', big);
expect(await storage.get('big')).toBe(big);
});
it('JSON string value', async () => {
const json = JSON.stringify({ token: 'abc', user: { id: 1 } });
await storage.set('session', json);
const parsed = JSON.parse((await storage.get('session'))!);
expect(parsed.token).toBe('abc');
expect(parsed.user.id).toBe(1);
});
// ── isSupported ─────────────────────────────────
it('isSupported returns true in test environment', () => {
expect(SecureStorage.isSupported()).toBe(true);
});
});