|
| 1 | +/** |
| 2 | + * @license |
| 3 | + * Copyright 2026 Google LLC |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; |
| 8 | +import * as fs from 'node:fs'; |
| 9 | +import * as path from 'node:path'; |
| 10 | +import * as os from 'node:os'; |
| 11 | +import { AthanorWeaver } from './athanorWeaver.js'; |
| 12 | + |
| 13 | +vi.mock('node:fs', () => ({ |
| 14 | + existsSync: vi.fn(), |
| 15 | + readFileSync: vi.fn(), |
| 16 | +})); |
| 17 | + |
| 18 | +describe('AthanorWeaver', () => { |
| 19 | + let weaver: AthanorWeaver; |
| 20 | + |
| 21 | + beforeEach(() => { |
| 22 | + vi.resetAllMocks(); |
| 23 | + weaver = new AthanorWeaver(); |
| 24 | + vi.stubEnv('VESTA_ATHANOR_DIR', ''); |
| 25 | + }); |
| 26 | + |
| 27 | + afterEach(() => { |
| 28 | + vi.unstubAllEnvs(); |
| 29 | + }); |
| 30 | + |
| 31 | + describe('Directory Resolution', () => { |
| 32 | + it('uses VESTA_ATHANOR_DIR if set', () => { |
| 33 | + const customPath = '/custom/athanor/path'; |
| 34 | + vi.stubEnv('VESTA_ATHANOR_DIR', customPath); |
| 35 | + expect(weaver.getAthanorDir()).toBe(customPath); |
| 36 | + }); |
| 37 | + |
| 38 | + it('falls back to ~/.gemini-vesta/athanor/ if env var is not set', () => { |
| 39 | + vi.stubEnv('VITEST', ''); // remove VITEST flag to test real fallback |
| 40 | + const expectedPath = path.join(os.homedir(), '.gemini-vesta', 'athanor'); |
| 41 | + expect(weaver.getAthanorDir()).toBe(expectedPath); |
| 42 | + }); |
| 43 | + }); |
| 44 | + |
| 45 | + describe('Reading and Caching', () => { |
| 46 | + const mockDir = '/mock/athanor'; |
| 47 | + |
| 48 | + beforeEach(() => { |
| 49 | + vi.stubEnv('VESTA_ATHANOR_DIR', mockDir); |
| 50 | + }); |
| 51 | + |
| 52 | + it('returns empty string if directory does not exist', () => { |
| 53 | + vi.mocked(fs.existsSync).mockReturnValue(false); |
| 54 | + expect(weaver.getAthanorContext()).toBe(''); |
| 55 | + expect(fs.readFileSync).not.toHaveBeenCalled(); |
| 56 | + }); |
| 57 | + |
| 58 | + it('reads BOOT.md and AXIOMS.md if they exist', () => { |
| 59 | + vi.mocked(fs.existsSync).mockImplementation((p) => { |
| 60 | + if (p === mockDir) return true; |
| 61 | + if (p === path.join(mockDir, 'BOOT.md')) return true; |
| 62 | + if (p === path.join(mockDir, 'AXIOMS.md')) return true; |
| 63 | + return false; |
| 64 | + }); |
| 65 | + |
| 66 | + vi.mocked(fs.readFileSync).mockImplementation((p) => { |
| 67 | + if (p === path.join(mockDir, 'BOOT.md')) return '# BOOT\nIdentity'; |
| 68 | + if (p === path.join(mockDir, 'AXIOMS.md')) return '# AXIOMS\nRules'; |
| 69 | + return ''; |
| 70 | + }); |
| 71 | + |
| 72 | + const context = weaver.getAthanorContext(); |
| 73 | + expect(context).toContain('# BOOT'); |
| 74 | + expect(context).toContain('Identity'); |
| 75 | + expect(context).toContain('# AXIOMS'); |
| 76 | + expect(context).toContain('Rules'); |
| 77 | + |
| 78 | + expect(fs.readFileSync).toHaveBeenCalledTimes(2); |
| 79 | + }); |
| 80 | + |
| 81 | + it('caches the result and avoids repeated disk reads', () => { |
| 82 | + vi.mocked(fs.existsSync).mockReturnValue(true); |
| 83 | + vi.mocked(fs.readFileSync).mockReturnValue('mock content'); |
| 84 | + |
| 85 | + weaver.getAthanorContext(); |
| 86 | + weaver.getAthanorContext(); |
| 87 | + weaver.getAthanorContext(); |
| 88 | + |
| 89 | + expect(fs.readFileSync).toHaveBeenCalledTimes(5); // 5 files to read |
| 90 | + }); |
| 91 | + |
| 92 | + it('refresh() clears the cache', () => { |
| 93 | + vi.mocked(fs.existsSync).mockReturnValue(true); |
| 94 | + vi.mocked(fs.readFileSync).mockReturnValue('mock content'); |
| 95 | + |
| 96 | + weaver.getAthanorContext(); // initial read |
| 97 | + weaver.refresh(); |
| 98 | + weaver.getAthanorContext(); // reads again |
| 99 | + |
| 100 | + expect(fs.readFileSync).toHaveBeenCalledTimes(10); // 5 files * 2 reads |
| 101 | + }); |
| 102 | + |
| 103 | + it('minifies the output by removing excessive blank lines', () => { |
| 104 | + vi.mocked(fs.existsSync).mockReturnValue(true); |
| 105 | + vi.mocked(fs.readFileSync).mockImplementation((p) => { |
| 106 | + if (p.toString().endsWith('BOOT.md')) return 'Line 1\n\n\n\nLine 2'; |
| 107 | + return ''; |
| 108 | + }); |
| 109 | + |
| 110 | + const context = weaver.getAthanorContext(); |
| 111 | + expect(context).not.toContain('\n\n\n'); |
| 112 | + expect(context).toContain('Line 1\n\nLine 2'); |
| 113 | + }); |
| 114 | + }); |
| 115 | +}); |
0 commit comments