import { describe, test, expect, beforeEach } from 'vitest'; import { Activities } from '~/src/wallet'; import { StoreInMemory } from '~/src/storage'; describe('Activities', () => { let store: StoreInMemory; let activities: Activities; beforeEach(() => { store = new StoreInMemory(); activities = new Activities(store); }); test('should add activity with auto-generated timestamp', async () => { const activity = { type: 'WalletName', id: 'wallet1', value: 'Test Wallet', }; await activities.addActivity(activity); await activities.compileSnapshot(); const storedActivity = activities.getActivity('WalletName', 'wallet1'); expect(storedActivity).toBeDefined(); expect(storedActivity?.timestamp).toBeDefined(); expect(storedActivity?.value).toBe('Test Wallet'); }); test('should preserve provided timestamp', async () => { const timestamp = BigInt(1234567890000); const activity = { timestamp, type: 'WalletName', id: 'wallet1', value: 'Test Wallet', }; await activities.addActivity(activity); const snapshot = await activities.compileSnapshot(); const storedActivity = activities.getActivity('WalletName', 'wallet1'); expect(storedActivity?.timestamp).toBe(timestamp); }); test('should update existing activity with newer timestamp', async () => { const oldTimestamp = BigInt(1000); const newTimestamp = BigInt(2000); await activities.addActivity({ timestamp: oldTimestamp, type: 'WalletName', id: 'wallet1', value: 'Old Name', }); await activities.addActivity({ timestamp: newTimestamp, type: 'WalletName', id: 'wallet1', value: 'New Name', }); await activities.compileSnapshot(); const activity = activities.getActivity('WalletName', 'wallet1'); expect(activity?.value).toBe('New Name'); expect(activity?.timestamp).toBe(newTimestamp); }); test('should honor first-seen policy', async () => { activities.registerPolicy('TransactionFirstSeen', (existing, newer) => { return existing || newer; }); const oldTimestamp = BigInt(1000); const newTimestamp = BigInt(2000); await activities.addActivity({ timestamp: oldTimestamp, type: 'TransactionFirstSeen', id: 'tx1', value: 'First Value', }); await activities.addActivity({ timestamp: newTimestamp, type: 'TransactionFirstSeen', id: 'tx1', value: 'Second Value', }); const snapshot = await activities.compileSnapshot(); const activity = activities.getActivity('TransactionFirstSeen', 'tx1'); expect(activity?.value).toBe('First Value'); expect(activity?.timestamp).toBe(oldTimestamp); }); test('should handle custom policy with comparison function', async () => { // Policy that keeps activity with highest value activities.registerPolicy('CustomType', (existing, newer) => { if (!existing) return newer; return existing.value > newer.value ? existing : newer; }); await activities.addActivity({ timestamp: BigInt(1000), type: 'CustomType', id: 'custom1', value: 50, }); await activities.addActivity({ timestamp: BigInt(2000), type: 'CustomType', id: 'custom1', value: 30, }); const snapshot = await activities.compileSnapshot(); const activity = activities.getActivity('CustomType', 'custom1'); expect(activity?.value).toBe(50); }); test('should process multiple activities in chronological order', async () => { const testActivities = [ { timestamp: BigInt(3000), type: 'WalletName', id: 'wallet1', value: 'Name 3', }, { timestamp: BigInt(1000), type: 'WalletName', id: 'wallet1', value: 'Name 1', }, { timestamp: BigInt(2000), type: 'WalletName', id: 'wallet1', value: 'Name 2', }, ]; for (const activity of testActivities) { await activities.addActivity(activity); } await activities.compileSnapshot(); const finalActivity = activities.getActivity('WalletName', 'wallet1'); expect(finalActivity?.value).toBe('Name 3'); }); test('should handle multiple activity types independently', async () => { await activities.addActivity({ timestamp: BigInt(1000), type: 'WalletName', id: 'wallet1', value: 'My Wallet', }); await activities.addActivity({ timestamp: BigInt(2000), type: 'WalletDescription', id: 'wallet1', value: 'My Description', }); await activities.compileSnapshot(); expect(activities.getActivity('WalletName', 'wallet1')?.value).toBe( 'My Wallet', ); expect(activities.getActivity('WalletDescription', 'wallet1')?.value).toBe( 'My Description', ); }); test('should update snapshot immediately when activity is added', async () => { const activity = { timestamp: BigInt(1000), type: 'WalletName', id: 'wallet1', value: 'Test Wallet', }; await activities.addActivity(activity); // Check snapshot without calling compileSnapshot const storedActivity = activities.getActivity('WalletName', 'wallet1'); expect(storedActivity?.value).toBe('Test Wallet'); }); test('should handle missing timestamp gracefully', async () => { const activity = { type: 'WalletName', id: 'wallet1', value: 'Test Wallet', }; await activities.addActivity(activity); await activities.compileSnapshot(); const storedActivity = activities.getActivity('WalletName', 'wallet1'); expect(storedActivity).toBeDefined(); expect(storedActivity?.timestamp).toBeDefined(); expect(typeof storedActivity?.timestamp).toBe('bigint'); }); test('should handle store errors gracefully', async () => { // Simulate store failure store.set = async () => { throw new Error('Store error'); }; await expect( activities.addActivity({ type: 'WalletName', id: 'wallet1', value: 'Test', }), ).rejects.toThrow('Store error'); }); test('should handle concurrent activity additions', async () => { const promises: Promise[] = []; // Add explicit type annotation for (let i = 0; i < 10; i++) { promises.push( activities.addActivity({ timestamp: BigInt(i), type: 'WalletName', id: 'wallet1', value: `Value ${i}`, }), ); } await Promise.all(promises); await activities.compileSnapshot(); // Should have the last value expect(activities.getActivity('WalletName', 'wallet1')?.value).toBe( 'Value 9', ); }); /* test('should cleanup old activities from store when newer versions are added', async () => { // Add first activity await activities.addActivity({ timestamp: BigInt(1000), type: 'WalletName', id: 'wallet1', value: 'Old Name', }); expect(Object.keys(await store.all()).length).toBe(1); // Add newer version - should trigger cleanup of old version await activities.addActivity({ timestamp: BigInt(2000), type: 'WalletName', id: 'wallet1', value: 'New Name', }); // Should still be 1 as old activity was culled expect(Object.keys(await store.all()).length).toBe(1); // Add different type await activities.addActivity({ timestamp: BigInt(1500), type: 'WalletDescription', id: 'wallet1', value: 'Description', }); // Should now have 2 activities expect(Object.keys(await store.all()).length).toBe(2); }); */ });