feat(trash): active queries exclude deleted_at IS NOT NULL (#4 v0.2.3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
altair823
2026-05-01 20:53:03 +09:00
parent 70a69f0ae3
commit 2203bcf65b
2 changed files with 56 additions and 4 deletions

View File

@@ -83,17 +83,25 @@ export class NoteRepository {
const limit = Math.max(1, Math.min(200, opts.limit));
const rows = opts.cursor
? (this.db
.prepare(`SELECT * FROM notes WHERE created_at < ? ORDER BY created_at DESC, id DESC LIMIT ?`)
.prepare(
`SELECT * FROM notes
WHERE deleted_at IS NULL AND created_at < ?
ORDER BY created_at DESC, id DESC LIMIT ?`
)
.all(opts.cursor, limit) as any[])
: (this.db
.prepare(`SELECT * FROM notes ORDER BY created_at DESC, id DESC LIMIT ?`)
.prepare(
`SELECT * FROM notes
WHERE deleted_at IS NULL
ORDER BY created_at DESC, id DESC LIMIT ?`
)
.all(limit) as any[]);
return rows.map((r) => this.hydrate(r));
}
listAll(): Note[] {
const rows = this.db
.prepare(`SELECT * FROM notes ORDER BY created_at ASC, id ASC`)
.prepare(`SELECT * FROM notes WHERE deleted_at IS NULL ORDER BY created_at ASC, id ASC`)
.all() as any[];
return rows.map((r) => this.hydrate(r));
}
@@ -359,7 +367,10 @@ export class NoteRepository {
const startIso = new Date(kstMidnightUtc).toISOString();
const endIso = new Date(nextKstMidnightUtc).toISOString();
const row = this.db
.prepare(`SELECT COUNT(*) AS c FROM notes WHERE created_at >= ? AND created_at < ?`)
.prepare(
`SELECT COUNT(*) AS c FROM notes
WHERE deleted_at IS NULL AND created_at >= ? AND created_at < ?`
)
.get(startIso, endIso) as { c: number };
return row.c;
}

View File

@@ -363,3 +363,44 @@ describe('NoteRepository.listTrashed', () => {
expect(r).toHaveLength(3);
});
});
describe('Active queries exclude deleted notes', () => {
let db: Database.Database;
let repo: NoteRepository;
beforeEach(() => {
db = new Database(':memory:');
runMigrations(db);
repo = new NoteRepository(db);
});
it('list() excludes trashed', () => {
const a = repo.create({ rawText: 'a' }).id;
const b = repo.create({ rawText: 'b' }).id;
repo.trash(a, '2026-05-01T00:00:00.000Z');
const r = repo.list({ limit: 50 });
expect(r.map((n) => n.id)).toEqual([b]);
});
it('listAll() excludes trashed', () => {
const a = repo.create({ rawText: 'a' }).id;
repo.create({ rawText: 'b' });
repo.trash(a, '2026-05-01T00:00:00.000Z');
const r = repo.listAll();
expect(r.map((n) => n.rawText)).toEqual(['b']);
});
it('countToday() excludes trashed', () => {
const a = repo.create({ rawText: 'a' }).id;
repo.create({ rawText: 'b' });
repo.trash(a, new Date().toISOString());
expect(repo.countToday(new Date())).toBe(1);
});
it('findById() returns trashed notes (does NOT filter)', () => {
const { id } = repo.create({ rawText: 'a' });
repo.trash(id, '2026-05-01T00:00:00.000Z');
const note = repo.findById(id);
expect(note).not.toBeNull();
expect(note!.deletedAt).toBe('2026-05-01T00:00:00.000Z');
});
});