mirror of
https://github.com/coleam00/Archon.git
synced 2026-01-02 12:48:54 -05:00
- Fix the clipboard functionality to work on non local hosts and https - Improvements in sockets on front-end and backend. Storing session in local browser storage for reconnect. Logic to prevent socket echos coausing rerender and performance issues. - Fixes and udpates to re-ordering logic in adding a new task, reordering items on the task table. - Allowing assignee to not be hardcoded enum. - Fix to Document Version Control (Improvements still needed in the Milkdown editor conversion to store in the docs. - Adding types to remove [any] typescript issues.
238 lines
7.1 KiB
TypeScript
238 lines
7.1 KiB
TypeScript
import { describe, test, expect, beforeEach, vi } from 'vitest'
|
|
import { OperationTracker } from '../../src/utils/operationTracker'
|
|
|
|
// Mock uuid
|
|
vi.mock('uuid', () => ({
|
|
v4: vi.fn(() => 'mock-uuid-123')
|
|
}))
|
|
|
|
describe('OperationTracker', () => {
|
|
let tracker: OperationTracker
|
|
|
|
beforeEach(() => {
|
|
tracker = new OperationTracker()
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('generateOperationId', () => {
|
|
test('generates unique operation IDs', () => {
|
|
const id1 = tracker.generateOperationId()
|
|
const id2 = tracker.generateOperationId()
|
|
|
|
expect(id1).toBe('mock-uuid-123')
|
|
expect(id2).toBe('mock-uuid-123') // Same because mock always returns same value
|
|
|
|
// In real implementation, these would be different
|
|
expect(id1).toBeTruthy()
|
|
expect(id2).toBeTruthy()
|
|
})
|
|
|
|
test('returns string IDs', () => {
|
|
const id = tracker.generateOperationId()
|
|
expect(typeof id).toBe('string')
|
|
})
|
|
})
|
|
|
|
describe('addOperation', () => {
|
|
test('adds operation to tracking set', () => {
|
|
const operationId = 'test-op-1'
|
|
tracker.addOperation(operationId)
|
|
|
|
expect(tracker.isOwnOperation(operationId)).toBe(true)
|
|
})
|
|
|
|
test('handles multiple operations', () => {
|
|
tracker.addOperation('op-1')
|
|
tracker.addOperation('op-2')
|
|
tracker.addOperation('op-3')
|
|
|
|
expect(tracker.isOwnOperation('op-1')).toBe(true)
|
|
expect(tracker.isOwnOperation('op-2')).toBe(true)
|
|
expect(tracker.isOwnOperation('op-3')).toBe(true)
|
|
})
|
|
|
|
test('handles duplicate operations gracefully', () => {
|
|
const operationId = 'duplicate-op'
|
|
|
|
tracker.addOperation(operationId)
|
|
tracker.addOperation(operationId) // Add same ID again
|
|
|
|
expect(tracker.isOwnOperation(operationId)).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('removeOperation', () => {
|
|
test('removes operation from tracking', () => {
|
|
const operationId = 'temp-op'
|
|
|
|
tracker.addOperation(operationId)
|
|
expect(tracker.isOwnOperation(operationId)).toBe(true)
|
|
|
|
tracker.removeOperation(operationId)
|
|
expect(tracker.isOwnOperation(operationId)).toBe(false)
|
|
})
|
|
|
|
test('handles removing non-existent operation', () => {
|
|
// Should not throw error
|
|
expect(() => {
|
|
tracker.removeOperation('non-existent')
|
|
}).not.toThrow()
|
|
})
|
|
|
|
test('removes only specified operation', () => {
|
|
tracker.addOperation('op-1')
|
|
tracker.addOperation('op-2')
|
|
tracker.addOperation('op-3')
|
|
|
|
tracker.removeOperation('op-2')
|
|
|
|
expect(tracker.isOwnOperation('op-1')).toBe(true)
|
|
expect(tracker.isOwnOperation('op-2')).toBe(false)
|
|
expect(tracker.isOwnOperation('op-3')).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('isOwnOperation', () => {
|
|
test('returns true for tracked operations', () => {
|
|
const operationId = 'tracked-op'
|
|
tracker.addOperation(operationId)
|
|
|
|
expect(tracker.isOwnOperation(operationId)).toBe(true)
|
|
})
|
|
|
|
test('returns false for untracked operations', () => {
|
|
expect(tracker.isOwnOperation('untracked-op')).toBe(false)
|
|
})
|
|
|
|
test('returns false after operation is removed', () => {
|
|
const operationId = 'temp-op'
|
|
|
|
tracker.addOperation(operationId)
|
|
tracker.removeOperation(operationId)
|
|
|
|
expect(tracker.isOwnOperation(operationId)).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('clear', () => {
|
|
test('removes all tracked operations', () => {
|
|
tracker.addOperation('op-1')
|
|
tracker.addOperation('op-2')
|
|
tracker.addOperation('op-3')
|
|
|
|
tracker.clear()
|
|
|
|
expect(tracker.isOwnOperation('op-1')).toBe(false)
|
|
expect(tracker.isOwnOperation('op-2')).toBe(false)
|
|
expect(tracker.isOwnOperation('op-3')).toBe(false)
|
|
})
|
|
|
|
test('works with empty tracker', () => {
|
|
expect(() => tracker.clear()).not.toThrow()
|
|
})
|
|
})
|
|
|
|
describe('echo suppression scenarios', () => {
|
|
test('prevents processing own operations', () => {
|
|
const operationId = tracker.generateOperationId()
|
|
tracker.addOperation(operationId)
|
|
|
|
// Simulate receiving an event with our operation ID
|
|
const event = { operationId, data: 'some data' }
|
|
|
|
// Should identify as own operation (skip processing)
|
|
if (tracker.isOwnOperation(event.operationId)) {
|
|
// Skip processing
|
|
expect(true).toBe(true) // Operation should be skipped
|
|
} else {
|
|
// Process event
|
|
expect(false).toBe(true) // Should not reach here
|
|
}
|
|
})
|
|
|
|
test('allows processing external operations', () => {
|
|
const externalOpId = 'external-op-123'
|
|
|
|
// Simulate receiving an event from another client
|
|
const event = { operationId: externalOpId, data: 'external data' }
|
|
|
|
// Should not identify as own operation
|
|
if (!tracker.isOwnOperation(event.operationId)) {
|
|
// Process event
|
|
expect(true).toBe(true) // Operation should be processed
|
|
} else {
|
|
// Skip processing
|
|
expect(false).toBe(true) // Should not reach here
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('cleanup patterns', () => {
|
|
test('supports operation cleanup after completion', () => {
|
|
const operationId = tracker.generateOperationId()
|
|
tracker.addOperation(operationId)
|
|
|
|
// Simulate operation completion
|
|
setTimeout(() => {
|
|
tracker.removeOperation(operationId)
|
|
}, 100)
|
|
|
|
// Initially tracked
|
|
expect(tracker.isOwnOperation(operationId)).toBe(true)
|
|
|
|
// After cleanup (would be false after timeout)
|
|
// Note: In real tests, would use fake timers or promises
|
|
})
|
|
|
|
test('handles batch cleanup', () => {
|
|
const operations = ['op-1', 'op-2', 'op-3', 'op-4', 'op-5']
|
|
|
|
// Add all operations
|
|
operations.forEach(op => tracker.addOperation(op))
|
|
|
|
// Remove specific operations
|
|
tracker.removeOperation('op-2')
|
|
tracker.removeOperation('op-4')
|
|
|
|
expect(tracker.isOwnOperation('op-1')).toBe(true)
|
|
expect(tracker.isOwnOperation('op-2')).toBe(false)
|
|
expect(tracker.isOwnOperation('op-3')).toBe(true)
|
|
expect(tracker.isOwnOperation('op-4')).toBe(false)
|
|
expect(tracker.isOwnOperation('op-5')).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('memory management', () => {
|
|
test('does not accumulate unlimited operations', () => {
|
|
// Add many operations
|
|
for (let i = 0; i < 1000; i++) {
|
|
tracker.addOperation(`op-${i}`)
|
|
}
|
|
|
|
// Clear to prevent memory leaks
|
|
tracker.clear()
|
|
|
|
// Verify all cleared
|
|
expect(tracker.isOwnOperation('op-0')).toBe(false)
|
|
expect(tracker.isOwnOperation('op-999')).toBe(false)
|
|
})
|
|
|
|
test('supports operation TTL pattern', () => {
|
|
// This test demonstrates a pattern for auto-cleanup
|
|
const operationWithTTL = (id: string, ttlMs: number) => {
|
|
tracker.addOperation(id)
|
|
|
|
setTimeout(() => {
|
|
tracker.removeOperation(id)
|
|
}, ttlMs)
|
|
}
|
|
|
|
const opId = 'ttl-op'
|
|
operationWithTTL(opId, 5000) // 5 second TTL
|
|
|
|
// Initially tracked
|
|
expect(tracker.isOwnOperation(opId)).toBe(true)
|
|
// Would be removed after TTL expires
|
|
})
|
|
})
|
|
}) |