feat: ManualClock 및 StepClock 클래스 추가와 관련 테스트 케이스 작성
feat: vitest를 devDependencies에 추가하고 테스트 스크립트 수정 docs: 테스트에서 제어 가능한 시계 사용에 대한 내용 추가
This commit is contained in:
@@ -7,6 +7,9 @@
|
|||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
"dev": "node -e \"console.log('dev not configured')\"",
|
"dev": "node -e \"console.log('dev not configured')\"",
|
||||||
"lint": "node -e \"console.log('lint not configured')\"",
|
"lint": "node -e \"console.log('lint not configured')\"",
|
||||||
"test": "node -e \"console.log('test not configured')\""
|
"test": "vitest run --config vitest.config.ts"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^4.0.16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,3 +13,64 @@ export class SystemClock implements Clock {
|
|||||||
await new Promise((resolve) => setTimeout(resolve, ms));
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ManualClock implements Clock {
|
||||||
|
// 테스트에서 시간을 직접 이동시키는 수동 시계.
|
||||||
|
private currentMs: number;
|
||||||
|
|
||||||
|
constructor(initialMs = 0) {
|
||||||
|
this.currentMs = initialMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
nowMs(): number {
|
||||||
|
return this.currentMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
async sleepMs(ms: number): Promise<void> {
|
||||||
|
if (ms <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentMs += ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
advanceMs(ms: number): void {
|
||||||
|
if (ms <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentMs += ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMs(ms: number): void {
|
||||||
|
this.currentMs = ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StepClock implements Clock {
|
||||||
|
// 호출마다 일정 간격씩 시간이 진행되는 시계.
|
||||||
|
private currentMs: number;
|
||||||
|
private readonly stepMs: number;
|
||||||
|
|
||||||
|
constructor(stepMs: number, initialMs = 0) {
|
||||||
|
if (stepMs <= 0) {
|
||||||
|
throw new Error('stepMs must be positive');
|
||||||
|
}
|
||||||
|
this.stepMs = stepMs;
|
||||||
|
this.currentMs = initialMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
nowMs(): number {
|
||||||
|
this.currentMs += this.stepMs;
|
||||||
|
return this.currentMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
async sleepMs(ms: number): Promise<void> {
|
||||||
|
if (ms <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentMs += ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMs(ms: number): void {
|
||||||
|
this.currentMs = ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { ManualClock, StepClock } from '../src/lifecycle/clock.js';
|
||||||
|
|
||||||
|
describe('ManualClock', () => {
|
||||||
|
it('returns current time without advancing', () => {
|
||||||
|
const clock = new ManualClock(1000);
|
||||||
|
expect(clock.nowMs()).toBe(1000);
|
||||||
|
expect(clock.nowMs()).toBe(1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('advances with sleep and manual advance', async () => {
|
||||||
|
const clock = new ManualClock(0);
|
||||||
|
await clock.sleepMs(250);
|
||||||
|
expect(clock.nowMs()).toBe(250);
|
||||||
|
clock.advanceMs(750);
|
||||||
|
expect(clock.nowMs()).toBe(1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can set time explicitly', () => {
|
||||||
|
const clock = new ManualClock(10);
|
||||||
|
clock.setMs(5000);
|
||||||
|
expect(clock.nowMs()).toBe(5000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('StepClock', () => {
|
||||||
|
it('advances on each nowMs call', () => {
|
||||||
|
const clock = new StepClock(100, 0);
|
||||||
|
expect(clock.nowMs()).toBe(100);
|
||||||
|
expect(clock.nowMs()).toBe(200);
|
||||||
|
expect(clock.nowMs()).toBe(300);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('advances with sleep', async () => {
|
||||||
|
const clock = new StepClock(50, 1000);
|
||||||
|
await clock.sleepMs(200);
|
||||||
|
expect(clock.nowMs()).toBe(1250);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects non-positive step', () => {
|
||||||
|
expect(() => new StepClock(0)).toThrow('stepMs must be positive');
|
||||||
|
expect(() => new StepClock(-5)).toThrow('stepMs must be positive');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
environment: 'node',
|
||||||
|
globals: true,
|
||||||
|
include: ['test/**/*.test.ts'],
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -185,6 +185,13 @@ export type DaemonEvent =
|
|||||||
- On shutdown, stop accepting new triggers, finish the current run, flush, then
|
- On shutdown, stop accepting new triggers, finish the current run, flush, then
|
||||||
exit cleanly.
|
exit cleanly.
|
||||||
|
|
||||||
|
## Testing with a Controlled Clock
|
||||||
|
|
||||||
|
- Turn daemon tests should use a controllable `Clock` implementation (예: `ManualClock`) to
|
||||||
|
advance time deterministically without relying on wall-clock time.
|
||||||
|
- 기준 시간은 DB에 저장된 `game_env.turntime`을 우선으로 삼고, 테스트에서도 동일한 기준을
|
||||||
|
사용해 스케줄 계산과 체크포인트 동작을 검증한다.
|
||||||
|
|
||||||
## TypeScript Sketch (Draft)
|
## TypeScript Sketch (Draft)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
|||||||
Generated
+5
-1
@@ -17,7 +17,11 @@ importers:
|
|||||||
|
|
||||||
app/game-api: {}
|
app/game-api: {}
|
||||||
|
|
||||||
app/game-engine: {}
|
app/game-engine:
|
||||||
|
devDependencies:
|
||||||
|
vitest:
|
||||||
|
specifier: ^4.0.16
|
||||||
|
version: 4.0.16(@types/node@20.19.27)
|
||||||
|
|
||||||
app/game-frontend: {}
|
app/game-frontend: {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user