3.5 KiB
createChatDbFixture
(no submodules)
Purpose
Builds a real chat.db-shaped SQLite database on a temp path using
the structural SqliteReader seam. This is the foundation of the
test strategy: every watcher / adapter test runs against a real
bun:sqlite database whose schema mirrors Apple's Messages app,
populated via the helpers returned by this factory. There are no
mocks anywhere in the call chain — the SQL the watcher executes in
production runs unchanged against the fixture.
Features
- DDL covers the six tables the watcher reads:
message,handle,chat,chat_message_join,attachment,message_attachment_join— with the exact column names + types Apple's chat.db uses. - Helpers return the inserted ROWID via a
SELECT ROWID ORDER BY ROWID DESC LIMIT 1so tests can wire foreign-key joins deterministically without depending onlast_insert_rowid()semantics across reader implementations. insertMessage(row, chatId)writes both the message and itschat_message_joinrow so the watcher'sJOIN cmjquery finds the chat without extra wiring.insertAttachment(row, messageId)writes both the attachment and itsmessage_attachment_joinrow.close()closes the handle so the temp file can be deleted by the caller'safterEach.
Usage: Basic
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
createBunSqliteReader,
createChatDbFixture,
} from "@suaveplan/imessage-adapter";
const reader = createBunSqliteReader();
const fixture = createChatDbFixture(reader, join(tmpdir(), "chat.db"));
const chatId = fixture.insertChat({
guid: "iMessage;+;+15551234567",
chat_identifier: "+15551234567",
display_name: null,
group_id: null,
service_name: "iMessage",
is_archived: 0,
});
fixture.close();
Usage: Advanced
const handleId = fixture.insertHandle({
id: "+15551234567",
country: "us",
service: "iMessage",
uncanonicalized_id: null,
});
const messageId = fixture.insertMessage({
guid: "m-1",
text: "Hello",
handle_id: handleId,
service: "iMessage",
date: 700_000_000,
date_read: 0,
date_delivered: 0,
is_from_me: 0,
is_read: 1,
is_delivered: 1,
is_sent: 0,
cache_has_attachments: 1,
associated_message_guid: null,
associated_message_type: 0,
balloon_bundle_id: null,
group_title: null,
}, chatId);
fixture.insertAttachment({
guid: "a-1",
filename: "/tmp/photo.jpg",
mime_type: "image/jpeg",
total_bytes: 1024,
transfer_name: "photo.jpg",
}, messageId);
API Reference
createChatDbFixture(reader, path): ChatDbFixture— DDLs the schema and returns the helpers.ChatDbFixture.handle: SqliteHandle— exposed for advanced custom inserts.insertHandle(row): number— returns the new ROWID.insertChat(row): number— returns the new ROWID.insertMessage(row, chatId?): number— wires thechat_message_joinrow whenchatIdis supplied.insertAttachment(row, messageId): number— wires themessage_attachment_joinrow.close(): void— closes the handle.
Implementation Notes
The factory is server-tier-only because it requires a real SQLite
implementation, but it accepts any SqliteReader so a future
in-process SQLite-WASM reader could also drive the fixture. The
schema uses AUTOINCREMENT so insertion order maps to monotonically
increasing ROWIDs, which the watcher's ROWID > lastRowId cursor
relies on for incremental polling.