| import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; |
| import { |
| writeValidation, |
| readValidation, |
| getAllValidations, |
| deleteValidation, |
| isValidationStale, |
| getValidationWithFreshness, |
| markValidationViewed, |
| getUnviewedValidationsCount, |
| type StoredValidation, |
| } from '@/lib/validation-storage.js'; |
| import fs from 'fs/promises'; |
| import path from 'path'; |
| import os from 'os'; |
|
|
| describe('validation-storage.ts', () => { |
| let testProjectPath: string; |
|
|
| beforeEach(async () => { |
| testProjectPath = path.join(os.tmpdir(), `validation-storage-test-${Date.now()}`); |
| await fs.mkdir(testProjectPath, { recursive: true }); |
| }); |
|
|
| afterEach(async () => { |
| try { |
| await fs.rm(testProjectPath, { recursive: true, force: true }); |
| } catch { |
| |
| } |
| }); |
|
|
| const createMockValidation = (overrides: Partial<StoredValidation> = {}): StoredValidation => ({ |
| issueNumber: 123, |
| issueTitle: 'Test Issue', |
| validatedAt: new Date().toISOString(), |
| model: 'haiku', |
| result: { |
| verdict: 'valid', |
| confidence: 'high', |
| reasoning: 'Test reasoning', |
| }, |
| ...overrides, |
| }); |
|
|
| describe('writeValidation', () => { |
| it('should write validation to storage', async () => { |
| const validation = createMockValidation(); |
|
|
| await writeValidation(testProjectPath, 123, validation); |
|
|
| |
| const validationPath = path.join( |
| testProjectPath, |
| '.automaker', |
| 'validations', |
| '123', |
| 'validation.json' |
| ); |
| const content = await fs.readFile(validationPath, 'utf-8'); |
| expect(JSON.parse(content)).toEqual(validation); |
| }); |
|
|
| it('should create nested directories if they do not exist', async () => { |
| const validation = createMockValidation({ issueNumber: 456 }); |
|
|
| await writeValidation(testProjectPath, 456, validation); |
|
|
| const validationPath = path.join( |
| testProjectPath, |
| '.automaker', |
| 'validations', |
| '456', |
| 'validation.json' |
| ); |
| const content = await fs.readFile(validationPath, 'utf-8'); |
| expect(JSON.parse(content)).toEqual(validation); |
| }); |
| }); |
|
|
| describe('readValidation', () => { |
| it('should read validation from storage', async () => { |
| const validation = createMockValidation(); |
| await writeValidation(testProjectPath, 123, validation); |
|
|
| const result = await readValidation(testProjectPath, 123); |
|
|
| expect(result).toEqual(validation); |
| }); |
|
|
| it('should return null when validation does not exist', async () => { |
| const result = await readValidation(testProjectPath, 999); |
|
|
| expect(result).toBeNull(); |
| }); |
| }); |
|
|
| describe('getAllValidations', () => { |
| it('should return all validations for a project', async () => { |
| const validation1 = createMockValidation({ issueNumber: 1, issueTitle: 'Issue 1' }); |
| const validation2 = createMockValidation({ issueNumber: 2, issueTitle: 'Issue 2' }); |
| const validation3 = createMockValidation({ issueNumber: 3, issueTitle: 'Issue 3' }); |
|
|
| await writeValidation(testProjectPath, 1, validation1); |
| await writeValidation(testProjectPath, 2, validation2); |
| await writeValidation(testProjectPath, 3, validation3); |
|
|
| const result = await getAllValidations(testProjectPath); |
|
|
| expect(result).toHaveLength(3); |
| expect(result[0]).toEqual(validation1); |
| expect(result[1]).toEqual(validation2); |
| expect(result[2]).toEqual(validation3); |
| }); |
|
|
| it('should return empty array when no validations exist', async () => { |
| const result = await getAllValidations(testProjectPath); |
|
|
| expect(result).toEqual([]); |
| }); |
|
|
| it('should skip non-numeric directories', async () => { |
| const validation = createMockValidation({ issueNumber: 1 }); |
| await writeValidation(testProjectPath, 1, validation); |
|
|
| |
| const invalidDir = path.join(testProjectPath, '.automaker', 'validations', 'invalid'); |
| await fs.mkdir(invalidDir, { recursive: true }); |
|
|
| const result = await getAllValidations(testProjectPath); |
|
|
| expect(result).toHaveLength(1); |
| expect(result[0]).toEqual(validation); |
| }); |
| }); |
|
|
| describe('deleteValidation', () => { |
| it('should delete validation from storage', async () => { |
| const validation = createMockValidation(); |
| await writeValidation(testProjectPath, 123, validation); |
|
|
| const result = await deleteValidation(testProjectPath, 123); |
|
|
| expect(result).toBe(true); |
|
|
| const readResult = await readValidation(testProjectPath, 123); |
| expect(readResult).toBeNull(); |
| }); |
|
|
| it('should return true even when validation does not exist', async () => { |
| const result = await deleteValidation(testProjectPath, 999); |
|
|
| expect(result).toBe(true); |
| }); |
| }); |
|
|
| describe('isValidationStale', () => { |
| it('should return false for recent validation', () => { |
| const validation = createMockValidation({ |
| validatedAt: new Date().toISOString(), |
| }); |
|
|
| const result = isValidationStale(validation); |
|
|
| expect(result).toBe(false); |
| }); |
|
|
| it('should return true for validation older than 24 hours', () => { |
| const oldDate = new Date(); |
| oldDate.setHours(oldDate.getHours() - 25); |
|
|
| const validation = createMockValidation({ |
| validatedAt: oldDate.toISOString(), |
| }); |
|
|
| const result = isValidationStale(validation); |
|
|
| expect(result).toBe(true); |
| }); |
|
|
| it('should return false for validation exactly at 24 hours', () => { |
| const exactDate = new Date(Date.now() - 24 * 60 * 60 * 1000 + 100); |
|
|
| const validation = createMockValidation({ |
| validatedAt: exactDate.toISOString(), |
| }); |
|
|
| const result = isValidationStale(validation); |
|
|
| expect(result).toBe(false); |
| }); |
| }); |
|
|
| describe('getValidationWithFreshness', () => { |
| it('should return validation with isStale false for recent validation', async () => { |
| const validation = createMockValidation({ |
| validatedAt: new Date().toISOString(), |
| }); |
| await writeValidation(testProjectPath, 123, validation); |
|
|
| const result = await getValidationWithFreshness(testProjectPath, 123); |
|
|
| expect(result).not.toBeNull(); |
| expect(result!.validation).toEqual(validation); |
| expect(result!.isStale).toBe(false); |
| }); |
|
|
| it('should return validation with isStale true for old validation', async () => { |
| const oldDate = new Date(); |
| oldDate.setHours(oldDate.getHours() - 25); |
|
|
| const validation = createMockValidation({ |
| validatedAt: oldDate.toISOString(), |
| }); |
| await writeValidation(testProjectPath, 123, validation); |
|
|
| const result = await getValidationWithFreshness(testProjectPath, 123); |
|
|
| expect(result).not.toBeNull(); |
| expect(result!.isStale).toBe(true); |
| }); |
|
|
| it('should return null when validation does not exist', async () => { |
| const result = await getValidationWithFreshness(testProjectPath, 999); |
|
|
| expect(result).toBeNull(); |
| }); |
| }); |
|
|
| describe('markValidationViewed', () => { |
| it('should mark validation as viewed', async () => { |
| const validation = createMockValidation(); |
| await writeValidation(testProjectPath, 123, validation); |
|
|
| const result = await markValidationViewed(testProjectPath, 123); |
|
|
| expect(result).toBe(true); |
|
|
| const updated = await readValidation(testProjectPath, 123); |
| expect(updated).not.toBeNull(); |
| expect(updated!.viewedAt).toBeDefined(); |
| }); |
|
|
| it('should return false when validation does not exist', async () => { |
| const result = await markValidationViewed(testProjectPath, 999); |
|
|
| expect(result).toBe(false); |
| }); |
| }); |
|
|
| describe('getUnviewedValidationsCount', () => { |
| it('should return count of unviewed non-stale validations', async () => { |
| const validation1 = createMockValidation({ issueNumber: 1 }); |
| const validation2 = createMockValidation({ issueNumber: 2 }); |
| const validation3 = createMockValidation({ |
| issueNumber: 3, |
| viewedAt: new Date().toISOString(), |
| }); |
|
|
| await writeValidation(testProjectPath, 1, validation1); |
| await writeValidation(testProjectPath, 2, validation2); |
| await writeValidation(testProjectPath, 3, validation3); |
|
|
| const result = await getUnviewedValidationsCount(testProjectPath); |
|
|
| expect(result).toBe(2); |
| }); |
|
|
| it('should not count stale validations', async () => { |
| const oldDate = new Date(); |
| oldDate.setHours(oldDate.getHours() - 25); |
|
|
| const validation1 = createMockValidation({ issueNumber: 1 }); |
| const validation2 = createMockValidation({ |
| issueNumber: 2, |
| validatedAt: oldDate.toISOString(), |
| }); |
|
|
| await writeValidation(testProjectPath, 1, validation1); |
| await writeValidation(testProjectPath, 2, validation2); |
|
|
| const result = await getUnviewedValidationsCount(testProjectPath); |
|
|
| expect(result).toBe(1); |
| }); |
|
|
| it('should return 0 when no validations exist', async () => { |
| const result = await getUnviewedValidationsCount(testProjectPath); |
|
|
| expect(result).toBe(0); |
| }); |
|
|
| it('should return 0 when all validations are viewed', async () => { |
| const validation = createMockValidation({ |
| issueNumber: 1, |
| viewedAt: new Date().toISOString(), |
| }); |
|
|
| await writeValidation(testProjectPath, 1, validation); |
|
|
| const result = await getUnviewedValidationsCount(testProjectPath); |
|
|
| expect(result).toBe(0); |
| }); |
| }); |
| }); |
|
|