187 lines
5.0 KiB
TypeScript
187 lines
5.0 KiB
TypeScript
/**
|
|
* Session state management tests
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { mkdirSync, rmSync, existsSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import {
|
|
loadSessionState,
|
|
saveSessionState,
|
|
clearSessionState,
|
|
getSessionStatePath,
|
|
type SessionState,
|
|
} from '../infra/config/project/sessionState.js';
|
|
|
|
describe('sessionState', () => {
|
|
const testDir = join(__dirname, '__temp_session_state_test__');
|
|
|
|
beforeEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
mkdirSync(testDir, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (existsSync(testDir)) {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
describe('getSessionStatePath', () => {
|
|
it('should return correct path', () => {
|
|
const path = getSessionStatePath(testDir);
|
|
expect(path).toContain('.takt');
|
|
expect(path).toContain('session-state.json');
|
|
});
|
|
});
|
|
|
|
describe('loadSessionState', () => {
|
|
it('should return null when file does not exist', () => {
|
|
const state = loadSessionState(testDir);
|
|
expect(state).toBeNull();
|
|
});
|
|
|
|
it('should load saved state', () => {
|
|
const savedState: SessionState = {
|
|
status: 'success',
|
|
taskResult: 'Task completed successfully',
|
|
timestamp: new Date().toISOString(),
|
|
pieceName: 'coding',
|
|
taskContent: 'Implement feature X',
|
|
lastMovement: 'implement',
|
|
};
|
|
|
|
saveSessionState(testDir, savedState);
|
|
const loadedState = loadSessionState(testDir);
|
|
|
|
expect(loadedState).toEqual(savedState);
|
|
});
|
|
|
|
it('should return null when JSON parsing fails', () => {
|
|
const path = getSessionStatePath(testDir);
|
|
const configDir = join(testDir, '.takt');
|
|
mkdirSync(configDir, { recursive: true });
|
|
|
|
// Write invalid JSON
|
|
const fs = require('node:fs');
|
|
fs.writeFileSync(path, 'invalid json', 'utf-8');
|
|
|
|
const state = loadSessionState(testDir);
|
|
expect(state).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('saveSessionState', () => {
|
|
it('should save state correctly', () => {
|
|
const state: SessionState = {
|
|
status: 'success',
|
|
taskResult: 'Task completed',
|
|
timestamp: new Date().toISOString(),
|
|
pieceName: 'minimal',
|
|
taskContent: 'Test task',
|
|
lastMovement: 'test-movement',
|
|
};
|
|
|
|
saveSessionState(testDir, state);
|
|
|
|
const path = getSessionStatePath(testDir);
|
|
expect(existsSync(path)).toBe(true);
|
|
|
|
const loaded = loadSessionState(testDir);
|
|
expect(loaded).toEqual(state);
|
|
});
|
|
|
|
it('should save error state', () => {
|
|
const state: SessionState = {
|
|
status: 'error',
|
|
errorMessage: 'Something went wrong',
|
|
timestamp: new Date().toISOString(),
|
|
pieceName: 'coding',
|
|
taskContent: 'Failed task',
|
|
};
|
|
|
|
saveSessionState(testDir, state);
|
|
const loaded = loadSessionState(testDir);
|
|
|
|
expect(loaded).toEqual(state);
|
|
});
|
|
|
|
it('should save user_stopped state', () => {
|
|
const state: SessionState = {
|
|
status: 'user_stopped',
|
|
timestamp: new Date().toISOString(),
|
|
pieceName: 'coding',
|
|
taskContent: 'Interrupted task',
|
|
};
|
|
|
|
saveSessionState(testDir, state);
|
|
const loaded = loadSessionState(testDir);
|
|
|
|
expect(loaded).toEqual(state);
|
|
});
|
|
});
|
|
|
|
describe('clearSessionState', () => {
|
|
it('should delete state file', () => {
|
|
const state: SessionState = {
|
|
status: 'success',
|
|
timestamp: new Date().toISOString(),
|
|
pieceName: 'coding',
|
|
};
|
|
|
|
saveSessionState(testDir, state);
|
|
const path = getSessionStatePath(testDir);
|
|
expect(existsSync(path)).toBe(true);
|
|
|
|
clearSessionState(testDir);
|
|
expect(existsSync(path)).toBe(false);
|
|
});
|
|
|
|
it('should not throw when file does not exist', () => {
|
|
expect(() => clearSessionState(testDir)).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('integration', () => {
|
|
it('should support one-time notification pattern', () => {
|
|
// Save state
|
|
const state: SessionState = {
|
|
status: 'success',
|
|
taskResult: 'Done',
|
|
timestamp: new Date().toISOString(),
|
|
pieceName: 'coding',
|
|
};
|
|
saveSessionState(testDir, state);
|
|
|
|
// Load once
|
|
const loaded1 = loadSessionState(testDir);
|
|
expect(loaded1).toEqual(state);
|
|
|
|
// Clear immediately
|
|
clearSessionState(testDir);
|
|
|
|
// Load again - should be null
|
|
const loaded2 = loadSessionState(testDir);
|
|
expect(loaded2).toBeNull();
|
|
});
|
|
|
|
it('should handle truncated strings', () => {
|
|
const longString = 'a'.repeat(2000);
|
|
const state: SessionState = {
|
|
status: 'success',
|
|
taskResult: longString,
|
|
timestamp: new Date().toISOString(),
|
|
pieceName: 'coding',
|
|
taskContent: longString,
|
|
};
|
|
|
|
saveSessionState(testDir, state);
|
|
const loaded = loadSessionState(testDir);
|
|
|
|
expect(loaded).toEqual(state);
|
|
});
|
|
});
|
|
});
|