Something went wrong. Try again.
WIP: A simple cli for daily tangled use cases and AI integration. This is for my personal use right now, but happy if others get mileage fro
Something went wrong. Try again.
5.9 kB · 173 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174import type { AtpSessionData } from '@atproto/api';import { beforeEach, describe, expect, it, vi } from 'vitest';import { TangledApiClient } from '../../src/lib/api-client.js';import * as sessionModule from '../../src/lib/session.js';import { mockSessionData, mockSessionMetadata } from '../helpers/mock-data.js';
// Mock @atproto/apivi.mock('@atproto/api', () => { return { AtpAgent: vi.fn().mockImplementation(() => { let currentSession: AtpSessionData | undefined = undefined;
return { service: { toString: () => 'https://bsky.social' }, get session() { return currentSession; }, login: vi.fn().mockImplementation(async () => { currentSession = mockSessionData; return { success: true, data: mockSessionData, }; }), resumeSession: vi.fn().mockImplementation(async (session) => { currentSession = session; }), }; }), };});
// Mock session managementvi.mock('../../src/lib/session.js', () => ({ saveSession: vi.fn(), loadSession: vi.fn(), deleteSession: vi.fn(), saveCurrentSessionMetadata: vi.fn(), getCurrentSessionMetadata: vi.fn(), clearCurrentSessionMetadata: vi.fn(),}));
describe('TangledApiClient', () => { let client: TangledApiClient;
beforeEach(() => { vi.clearAllMocks();
// Reset mock implementations vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue(null); vi.mocked(sessionModule.loadSession).mockResolvedValue(null);
client = new TangledApiClient(); });
describe('login', () => { it('should login successfully and save session', async () => { const result = await client.login('user.bsky.social', 'password');
expect(result).toEqual(mockSessionData); expect(vi.mocked(sessionModule.saveSession)).toHaveBeenCalledWith(mockSessionData); expect(vi.mocked(sessionModule.saveCurrentSessionMetadata)).toHaveBeenCalledWith({ handle: mockSessionData.handle, did: mockSessionData.did, pds: 'https://bsky.social', lastUsed: expect.any(String), }); });
it('should support custom domain handles', async () => { const result = await client.login('markbennett.ca', 'password');
expect(result).toEqual(mockSessionData); expect(vi.mocked(sessionModule.saveSession)).toHaveBeenCalled(); });
it('should throw error on login failure', async () => { const agent = client.getAgent(); vi.mocked(agent.login).mockResolvedValueOnce({ success: false, headers: {}, data: undefined, } as never);
await expect(client.login('user.bsky.social', 'wrong')).rejects.toThrow( 'Login failed: No session data received' ); }); });
describe('logout', () => { it('should logout and clear session', async () => { vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue(mockSessionMetadata);
await client.logout();
expect(vi.mocked(sessionModule.deleteSession)).toHaveBeenCalledWith(mockSessionMetadata.did); expect(vi.mocked(sessionModule.clearCurrentSessionMetadata)).toHaveBeenCalled(); });
it('should throw error if no active session', async () => { vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue(null);
await expect(client.logout()).rejects.toThrow('No active session found'); }); });
describe('resumeSession', () => { it('should resume session from stored data', async () => { vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue(mockSessionMetadata); vi.mocked(sessionModule.loadSession).mockResolvedValue(mockSessionData);
const resumed = await client.resumeSession();
expect(resumed).toBe(true); expect(vi.mocked(sessionModule.loadSession)).toHaveBeenCalledWith(mockSessionMetadata.did); expect(vi.mocked(sessionModule.saveCurrentSessionMetadata)).toHaveBeenCalledWith({ ...mockSessionMetadata, lastUsed: expect.any(String), }); });
it('should return false if no metadata exists', async () => { vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue(null);
const resumed = await client.resumeSession();
expect(resumed).toBe(false); });
it('should return false and cleanup if session data is missing', async () => { vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue(mockSessionMetadata); vi.mocked(sessionModule.loadSession).mockResolvedValue(null);
const resumed = await client.resumeSession();
expect(resumed).toBe(false); expect(vi.mocked(sessionModule.clearCurrentSessionMetadata)).toHaveBeenCalled(); });
it('should return false and cleanup on resume error', async () => { vi.mocked(sessionModule.getCurrentSessionMetadata).mockResolvedValue(mockSessionMetadata); vi.mocked(sessionModule.loadSession).mockResolvedValue(mockSessionData);
const agent = client.getAgent(); vi.mocked(agent.resumeSession).mockRejectedValueOnce(new Error('Resume failed'));
const resumed = await client.resumeSession();
expect(resumed).toBe(false); expect(vi.mocked(sessionModule.clearCurrentSessionMetadata)).toHaveBeenCalled(); }); });
describe('isAuthenticated', () => { it('should return false when not authenticated', () => { expect(client.isAuthenticated()).toBe(false); }); });
describe('getAgent', () => { it('should return the AtpAgent instance', () => { const agent = client.getAgent(); expect(agent).toBeDefined(); }); });
describe('getSession', () => { it('should return undefined when not authenticated', () => { expect(client.getSession()).toBeUndefined(); }); });});