Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/just-cameron/tangled-cli. Automation-first CLI for Tangled.org
Something went wrong. Try again.
6.6 kB · 204 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205import type { AtpSessionData } from '@atproto/api';import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';import { clearCurrentSessionMetadata, deleteSession, getCurrentSessionMetadata, InvalidCredentialDataError, loadOAuthSession, loadOAuthState, loadSession, saveCurrentSessionMetadata, saveSession,} from '../../src/lib/session.js';import { mockSessionData, mockSessionData2, mockSessionMetadata } from '../helpers/mock-data.js';
const mockKeyringStorage = new Map<string, string>();
vi.mock('../../src/lib/keychain.js', () => ({ saveKeychainSecret: vi.fn( async (service: string, account: string, password: string): Promise<void> => { mockKeyringStorage.set(`${service}:${account}`, password); } ), loadKeychainSecret: vi.fn( async (service: string, account: string): Promise<string | null> => mockKeyringStorage.get(`${service}:${account}`) ?? null ), deleteKeychainSecret: vi.fn( async (service: string, account: string): Promise<boolean> => mockKeyringStorage.delete(`${service}:${account}`) ),}));
// Mock node:fs/promises for metadata file storageconst mockFileStorage = new Map<string, string>();
vi.mock('node:fs/promises', () => ({ mkdir: vi.fn().mockResolvedValue(undefined), writeFile: vi.fn().mockImplementation(async (path: string, content: string) => { mockFileStorage.set(path as string, content); }), readFile: vi.fn().mockImplementation(async (path: string) => { const content = mockFileStorage.get(path as string); if (content === undefined) { const err = Object.assign(new Error(`ENOENT: no such file or directory, open '${path}'`), { code: 'ENOENT', }); throw err; } return content; }), unlink: vi.fn().mockImplementation(async (path: string) => { if (!mockFileStorage.has(path as string)) { const err = Object.assign(new Error(`ENOENT: no such file or directory, unlink '${path}'`), { code: 'ENOENT', }); throw err; } mockFileStorage.delete(path as string); }),}));
describe('Session Management', () => { beforeEach(() => { // Clear mock storage before each test mockKeyringStorage.clear(); mockFileStorage.clear(); vi.clearAllMocks(); });
afterEach(() => { // Clean up after each test mockKeyringStorage.clear(); mockFileStorage.clear(); });
describe('saveSession', () => { it('should save session to keychain using DID', async () => { await saveSession(mockSessionData);
// Verify session was stored const loaded = await loadSession(mockSessionData.did); expect(loaded).toEqual(mockSessionData); });
it('should save and retrieve session with all required fields', async () => { await saveSession(mockSessionData2);
// Verify session was stored (keyed by DID) const loaded = await loadSession(mockSessionData2.did); expect(loaded).toEqual(mockSessionData2); });
it('should throw error if session has no DID or handle', async () => { const sessionData = { email: 'user@example.com', accessJwt: 'token123', refreshJwt: 'refresh123', } as AtpSessionData;
await expect(saveSession(sessionData)).rejects.toThrow( 'Session data must include DID or handle' ); }); });
describe('loadSession', () => { it('should load session from keychain', async () => { await saveSession(mockSessionData); const result = await loadSession(mockSessionData.did);
expect(result).toEqual(mockSessionData); });
it('should return null when session not found', async () => { const result = await loadSession('did:plc:notfound'); expect(result).toBeNull(); });
it('never includes malformed credential bytes in a parse error', async () => { const exposedPassword = 'this-must-never-appear-in-an-error'; mockKeyringStorage.set('tangled-cli-oauth-state:bad-state', exposedPassword);
let thrown: unknown; try { await loadOAuthState('bad-state'); } catch (error) { thrown = error; }
expect(thrown).toBeInstanceOf(InvalidCredentialDataError); expect((thrown as Error).message).toBe('Stored authentication data is invalid.'); expect((thrown as Error).message).not.toContain(exposedPassword); });
it('redacts malformed OAuth session credential bytes', async () => { const exposedPassword = 'oauth-session-secret-must-not-appear'; mockKeyringStorage.set('tangled-cli-oauth-session:bad-sub', exposedPassword);
let thrown: unknown; try { await loadOAuthSession('bad-sub'); } catch (error) { thrown = error; }
expect(thrown).toBeInstanceOf(InvalidCredentialDataError); expect((thrown as Error).message).toBe('Stored authentication data is invalid.'); expect((thrown as Error).message).not.toContain(exposedPassword); }); });
describe('deleteSession', () => { it('should delete session from keychain', async () => { await saveSession(mockSessionData);
// Verify session exists let loaded = await loadSession(mockSessionData.did); expect(loaded).toEqual(mockSessionData);
// Delete session const deleted = await deleteSession(mockSessionData.did); expect(deleted).toBe(true);
// Verify session no longer exists loaded = await loadSession(mockSessionData.did); expect(loaded).toBeNull(); });
it('should return false when deleting non-existent session', async () => { const deleted = await deleteSession('did:plc:notfound'); expect(deleted).toBe(false); }); });
describe('session metadata', () => { it('should save and load current session metadata', async () => { await saveCurrentSessionMetadata(mockSessionMetadata); const result = await getCurrentSessionMetadata();
expect(result).toEqual(mockSessionMetadata); });
it('should return null when no metadata exists', async () => { const result = await getCurrentSessionMetadata(); expect(result).toBeNull(); });
it('should clear current session metadata', async () => { await saveCurrentSessionMetadata(mockSessionMetadata);
// Verify metadata exists let result = await getCurrentSessionMetadata(); expect(result).toEqual(mockSessionMetadata);
// Clear metadata await clearCurrentSessionMetadata();
// Verify metadata no longer exists result = await getCurrentSessionMetadata(); expect(result).toBeNull(); }); });});