Files
Codewalkers/apps/server/logging/manager.test.ts
Lukas May 34578d39c6 refactor: Restructure monorepo to apps/server/ and apps/web/ layout
Move src/ → apps/server/ and packages/web/ → apps/web/ to adopt
standard monorepo conventions (apps/ for runnable apps, packages/
for reusable libraries). Update all config files, shared package
imports, test fixtures, and documentation to reflect new paths.

Key fixes:
- Update workspace config to ["apps/*", "packages/*"]
- Update tsconfig.json rootDir/include for apps/server/
- Add apps/web/** to vitest exclude list
- Update drizzle.config.ts schema path
- Fix ensure-schema.ts migration path detection (3 levels up in dev,
  2 levels up in dist)
- Fix tests/integration/cli-server.test.ts import paths
- Update packages/shared imports to apps/server/ paths
- Update all docs/ files with new paths
2026-03-03 11:22:53 +01:00

216 lines
6.7 KiB
TypeScript

/**
* LogManager Tests
*
* Tests for the log directory and file path management.
* Uses temporary directories to avoid polluting the real log directory.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdir, rm, writeFile, utimes } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { LogManager } from './manager.js';
describe('LogManager', () => {
let testDir: string;
let manager: LogManager;
beforeEach(async () => {
// Create a unique temp directory for each test
testDir = join(tmpdir(), `cw-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
manager = new LogManager({ baseDir: testDir });
});
afterEach(async () => {
// Clean up temp directory after each test
try {
await rm(testDir, { recursive: true, force: true });
} catch {
// Ignore cleanup errors
}
});
describe('getBaseDir', () => {
it('should return the configured base directory', () => {
expect(manager.getBaseDir()).toBe(testDir);
});
it('should use default directory when not configured', () => {
const defaultManager = new LogManager();
expect(defaultManager.getBaseDir()).toContain('.cw');
expect(defaultManager.getBaseDir()).toContain('logs');
});
});
describe('ensureLogDir', () => {
it('should create the base log directory', async () => {
expect(existsSync(testDir)).toBe(false);
await manager.ensureLogDir();
expect(existsSync(testDir)).toBe(true);
});
it('should not error if directory already exists', async () => {
await mkdir(testDir, { recursive: true });
expect(existsSync(testDir)).toBe(true);
// Should not throw
await manager.ensureLogDir();
expect(existsSync(testDir)).toBe(true);
});
});
describe('ensureProcessDir', () => {
it('should create the process-specific log directory', async () => {
const processId = 'test-process-123';
const expectedDir = join(testDir, processId);
expect(existsSync(expectedDir)).toBe(false);
await manager.ensureProcessDir(processId);
expect(existsSync(expectedDir)).toBe(true);
});
it('should create nested directories if base does not exist', async () => {
const processId = 'nested-process';
const expectedDir = join(testDir, processId);
expect(existsSync(testDir)).toBe(false);
await manager.ensureProcessDir(processId);
expect(existsSync(testDir)).toBe(true);
expect(existsSync(expectedDir)).toBe(true);
});
});
describe('getProcessDir', () => {
it('should return the correct path for a process', () => {
const processId = 'my-process';
const expected = join(testDir, processId);
expect(manager.getProcessDir(processId)).toBe(expected);
});
});
describe('getLogPath', () => {
it('should return correct path for stdout log', () => {
const processId = 'proc-1';
const expected = join(testDir, processId, 'stdout.log');
expect(manager.getLogPath(processId, 'stdout')).toBe(expected);
});
it('should return correct path for stderr log', () => {
const processId = 'proc-2';
const expected = join(testDir, processId, 'stderr.log');
expect(manager.getLogPath(processId, 'stderr')).toBe(expected);
});
});
describe('listLogs', () => {
it('should return empty array if base directory does not exist', async () => {
expect(existsSync(testDir)).toBe(false);
const logs = await manager.listLogs();
expect(logs).toEqual([]);
});
it('should return empty array if no log directories exist', async () => {
await mkdir(testDir, { recursive: true });
const logs = await manager.listLogs();
expect(logs).toEqual([]);
});
it('should return process IDs for existing log directories', async () => {
// Create some process directories
await mkdir(join(testDir, 'process-a'), { recursive: true });
await mkdir(join(testDir, 'process-b'), { recursive: true });
await mkdir(join(testDir, 'process-c'), { recursive: true });
const logs = await manager.listLogs();
expect(logs).toHaveLength(3);
expect(logs).toContain('process-a');
expect(logs).toContain('process-b');
expect(logs).toContain('process-c');
});
it('should only return directories, not files', async () => {
await mkdir(testDir, { recursive: true });
await mkdir(join(testDir, 'valid-process'), { recursive: true });
await writeFile(join(testDir, 'some-file.txt'), 'not a directory');
const logs = await manager.listLogs();
expect(logs).toEqual(['valid-process']);
});
});
describe('cleanOldLogs', () => {
it('should return 0 when retainDays is not configured', async () => {
const managerNoRetain = new LogManager({ baseDir: testDir });
const removed = await managerNoRetain.cleanOldLogs();
expect(removed).toBe(0);
});
it('should return 0 when no directories exist', async () => {
const managerWithRetain = new LogManager({ baseDir: testDir, retainDays: 7 });
const removed = await managerWithRetain.cleanOldLogs();
expect(removed).toBe(0);
});
it('should remove directories older than retainDays', async () => {
const managerWithRetain = new LogManager({ baseDir: testDir, retainDays: 7 });
// Create an "old" directory
const oldDir = join(testDir, 'old-process');
await mkdir(oldDir, { recursive: true });
// Set mtime to 10 days ago
const tenDaysAgo = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000);
await utimes(oldDir, tenDaysAgo, tenDaysAgo);
// Create a "new" directory
const newDir = join(testDir, 'new-process');
await mkdir(newDir, { recursive: true });
const removed = await managerWithRetain.cleanOldLogs();
expect(removed).toBe(1);
expect(existsSync(oldDir)).toBe(false);
expect(existsSync(newDir)).toBe(true);
});
it('should use provided retainDays over config value', async () => {
const managerWithRetain = new LogManager({ baseDir: testDir, retainDays: 30 });
// Create directory that is 10 days old
const oldDir = join(testDir, 'process');
await mkdir(oldDir, { recursive: true });
const tenDaysAgo = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000);
await utimes(oldDir, tenDaysAgo, tenDaysAgo);
// With config (30 days), should NOT remove
expect(await managerWithRetain.cleanOldLogs()).toBe(0);
expect(existsSync(oldDir)).toBe(true);
// With explicit 5 days, SHOULD remove
expect(await managerWithRetain.cleanOldLogs(5)).toBe(1);
expect(existsSync(oldDir)).toBe(false);
});
});
});