fix(v032): NoteRepository.create now param + time-dep test flake fix
- create(input, now?: Date) signature 추가 (기존 setStatus/updateRawText 패턴 정합) - NoteRevisions.test.ts 4 testcase v1 capture 시간 명시 주입 (2026-05-09T00:00:00Z) - upsertFromSync.test.ts 2 testcase v1 capture 시간 명시 주입 - 시스템 시계가 2026-05-10T00:00:00Z 초과 시 DESC ordering 깨지던 회귀 회복 backlog: time-dependent flake (Cut F audit 발견) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -79,25 +79,25 @@ const KEBAB_CASE_RE = /^[a-z0-9-]+$/;
|
||||
export class NoteRepository {
|
||||
constructor(private db: Database.Database) {}
|
||||
|
||||
create(input: CreateNoteInput): { id: string } {
|
||||
create(input: CreateNoteInput, now: Date = new Date()): { id: string } {
|
||||
const id = uuidv7();
|
||||
const now = new Date().toISOString();
|
||||
const ts = now.toISOString();
|
||||
const aiStatus: AiStatus = input.aiStatus ?? 'pending';
|
||||
const tx = this.db.transaction(() => {
|
||||
this.db
|
||||
.prepare(`INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?)`)
|
||||
.run(id, input.rawText, aiStatus, now, now);
|
||||
.run(id, input.rawText, aiStatus, ts, ts);
|
||||
this.db
|
||||
.prepare(`INSERT INTO note_revisions (note_id, raw_text, edited_at, edited_by)
|
||||
VALUES (?, ?, ?, 'capture')`)
|
||||
.run(id, input.rawText, now);
|
||||
.run(id, input.rawText, ts);
|
||||
// pending_jobs 는 'pending' 일 때만 생성 — 'disabled' 노트는 worker 가 처리 안 함.
|
||||
if (aiStatus === 'pending') {
|
||||
this.db
|
||||
.prepare(`INSERT INTO pending_jobs (note_id, attempts, next_run_at)
|
||||
VALUES (?, 0, ?)`)
|
||||
.run(id, now);
|
||||
.run(id, ts);
|
||||
}
|
||||
});
|
||||
tx();
|
||||
|
||||
@@ -310,6 +310,24 @@ describe('NoteRepository', () => {
|
||||
const job = db.prepare('SELECT * FROM pending_jobs WHERE note_id=?').get(id);
|
||||
expect(job).toBeDefined();
|
||||
});
|
||||
|
||||
it('create accepts explicit now param', () => {
|
||||
const fixed = new Date('2026-05-09T10:00:00.000Z');
|
||||
const { id } = repo.create({ rawText: 'hello' }, fixed);
|
||||
const note = repo.findById(id)!;
|
||||
expect(note.createdAt).toBe('2026-05-09T10:00:00.000Z');
|
||||
expect(note.updatedAt).toBe('2026-05-09T10:00:00.000Z');
|
||||
});
|
||||
|
||||
it('create defaults now to new Date when omitted', () => {
|
||||
const before = Date.now();
|
||||
const { id } = repo.create({ rawText: 'hello' });
|
||||
const after = Date.now();
|
||||
const note = repo.findById(id)!;
|
||||
const ts = new Date(note.createdAt).getTime();
|
||||
expect(ts).toBeGreaterThanOrEqual(before);
|
||||
expect(ts).toBeLessThanOrEqual(after);
|
||||
});
|
||||
});
|
||||
|
||||
describe('NoteRepository.trash', () => {
|
||||
|
||||
@@ -52,7 +52,7 @@ describe('NoteRepository.upsertFromSync', () => {
|
||||
});
|
||||
|
||||
it('id 있음 + raw_text 동일 + source 더 최신 → metadata 갱신 (status=updated)', () => {
|
||||
const created = repo.create({ rawText: 'sync 본문' });
|
||||
const created = repo.create({ rawText: 'sync 본문' }, new Date('2026-05-09T00:00:00Z'));
|
||||
repo.updateAiResult(created.id, { title: '옛 제목', summary: '옛 요약', tags: ['old'], provider: 'p' });
|
||||
db.prepare(`UPDATE notes SET updated_at=? WHERE id=?`).run('2026-05-08T00:00:00Z', created.id);
|
||||
const r = repo.upsertFromSync({ ...baseInput, id: created.id });
|
||||
@@ -75,7 +75,7 @@ describe('NoteRepository.upsertFromSync', () => {
|
||||
});
|
||||
|
||||
it('id 있음 + raw_text 다름 + source 더 최신 → updateRawText (status=updated) + new user revision', () => {
|
||||
const created = repo.create({ rawText: 'old text' });
|
||||
const created = repo.create({ rawText: 'old text' }, new Date('2026-05-09T00:00:00Z'));
|
||||
db.prepare(`UPDATE notes SET updated_at=? WHERE id=?`).run('2026-05-08T00:00:00Z', created.id);
|
||||
const r = repo.upsertFromSync({ ...baseInput, id: created.id, rawText: 'new sync text' });
|
||||
expect(r.status).toBe('updated');
|
||||
|
||||
@@ -18,7 +18,8 @@ describe('NoteRepository — note_revisions', () => {
|
||||
|
||||
describe('updateRawText', () => {
|
||||
it('notes.raw_text 갱신 + 새 user revision INSERT (single transaction)', () => {
|
||||
const { id } = repo.create({ rawText: 'v1' });
|
||||
const v1At = new Date('2026-05-09T00:00:00Z');
|
||||
const { id } = repo.create({ rawText: 'v1' }, v1At);
|
||||
const t = new Date('2026-05-10T00:00:00Z');
|
||||
repo.updateRawText(id, 'v2', t);
|
||||
|
||||
@@ -41,7 +42,8 @@ describe('NoteRepository — note_revisions', () => {
|
||||
});
|
||||
|
||||
it('atomic: 두 번 호출 시 두 revision 모두 누적 (chain history)', () => {
|
||||
const { id } = repo.create({ rawText: 'v1' });
|
||||
const v1At = new Date('2026-05-09T00:00:00Z');
|
||||
const { id } = repo.create({ rawText: 'v1' }, v1At);
|
||||
repo.updateRawText(id, 'v2', new Date('2026-05-10T00:00:00Z'));
|
||||
repo.updateRawText(id, 'v3', new Date('2026-05-11T00:00:00Z'));
|
||||
const revs = db
|
||||
@@ -53,7 +55,8 @@ describe('NoteRepository — note_revisions', () => {
|
||||
|
||||
describe('listRevisions', () => {
|
||||
it('DESC 순서 + edited_by + camelCase hydrate', () => {
|
||||
const { id } = repo.create({ rawText: 'v1' });
|
||||
const v1At = new Date('2026-05-09T00:00:00Z');
|
||||
const { id } = repo.create({ rawText: 'v1' }, v1At);
|
||||
repo.updateRawText(id, 'v2', new Date('2026-05-10T00:00:00Z'));
|
||||
repo.updateRawText(id, 'v3', new Date('2026-05-11T00:00:00Z'));
|
||||
|
||||
@@ -73,7 +76,8 @@ describe('NoteRepository — note_revisions', () => {
|
||||
|
||||
describe('restoreRevision', () => {
|
||||
it('옛 raw_text 를 새 user revision 으로 INSERT + notes.raw_text 갱신', () => {
|
||||
const { id } = repo.create({ rawText: 'v1' });
|
||||
const v1At = new Date('2026-05-09T00:00:00Z');
|
||||
const { id } = repo.create({ rawText: 'v1' }, v1At);
|
||||
repo.updateRawText(id, 'v2', new Date('2026-05-10T00:00:00Z'));
|
||||
repo.updateRawText(id, 'v3', new Date('2026-05-11T00:00:00Z'));
|
||||
|
||||
@@ -101,7 +105,8 @@ describe('NoteRepository — note_revisions', () => {
|
||||
|
||||
describe('AiWorker source 회귀', () => {
|
||||
it('updateRawText 후 findById 가 latest raw_text 반환 (옛 revision 미노출)', () => {
|
||||
const { id } = repo.create({ rawText: 'v1' });
|
||||
const v1At = new Date('2026-05-09T00:00:00Z');
|
||||
const { id } = repo.create({ rawText: 'v1' }, v1At);
|
||||
repo.updateRawText(id, 'v2 corrected', new Date('2026-05-10T00:00:00Z'));
|
||||
const note = repo.findById(id);
|
||||
expect(note?.rawText).toBe('v2 corrected');
|
||||
|
||||
Reference in New Issue
Block a user