- add dist freshness guard to deploy flow - preserve message source provenance for IPC injection - harden Codex usage fallback and workspace package manager detection - document owner branch return protocol
158 lines
4.6 KiB
TypeScript
158 lines
4.6 KiB
TypeScript
import { Database } from 'bun:sqlite';
|
|
import fs from 'fs';
|
|
import os from 'os';
|
|
import path from 'path';
|
|
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
_initTestDatabase,
|
|
_initTestDatabaseFromFile,
|
|
getMessagesSinceSeq,
|
|
getRecentChatMessages,
|
|
storeChatMetadata,
|
|
storeMessage,
|
|
} from './db.js';
|
|
|
|
describe('message_source_kind persistence', () => {
|
|
let tempDir: string | null = null;
|
|
|
|
beforeEach(() => {
|
|
_initTestDatabase();
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (tempDir) {
|
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
tempDir = null;
|
|
}
|
|
});
|
|
|
|
it('persists explicit message source kind through store and read paths', () => {
|
|
storeChatMetadata('room@g.us', '2026-04-24T00:00:00.000Z', 'Room');
|
|
storeMessage({
|
|
id: 'ipc-1',
|
|
chat_jid: 'room@g.us',
|
|
sender: 'hermes',
|
|
sender_name: 'Hermes',
|
|
content: 'wake up',
|
|
timestamp: '2026-04-24T00:00:01.000Z',
|
|
is_from_me: false,
|
|
is_bot_message: false,
|
|
message_source_kind: 'trusted_external_bot',
|
|
});
|
|
|
|
const [recent] = getRecentChatMessages('room@g.us', 1);
|
|
expect(recent).toMatchObject({
|
|
id: 'ipc-1',
|
|
is_bot_message: false,
|
|
message_source_kind: 'trusted_external_bot',
|
|
});
|
|
|
|
storeMessage({
|
|
id: 'ipc-1',
|
|
chat_jid: 'room@g.us',
|
|
sender: 'hermes',
|
|
sender_name: 'Hermes',
|
|
content: 'updated',
|
|
timestamp: '2026-04-24T00:00:02.000Z',
|
|
is_from_me: false,
|
|
is_bot_message: true,
|
|
message_source_kind: 'ipc_injected_bot',
|
|
});
|
|
|
|
const [updated] = getMessagesSinceSeq('room@g.us', 0, 'EJClaw', 10);
|
|
expect(updated).toMatchObject({
|
|
id: 'ipc-1',
|
|
content: 'updated',
|
|
is_bot_message: true,
|
|
message_source_kind: 'ipc_injected_bot',
|
|
});
|
|
});
|
|
|
|
it('defaults missing message source kind from is_bot_message', () => {
|
|
storeChatMetadata('room@g.us', '2026-04-24T00:00:00.000Z', 'Room');
|
|
storeMessage({
|
|
id: 'human-1',
|
|
chat_jid: 'room@g.us',
|
|
sender: 'user',
|
|
sender_name: 'User',
|
|
content: 'hello',
|
|
timestamp: '2026-04-24T00:00:01.000Z',
|
|
is_from_me: false,
|
|
is_bot_message: false,
|
|
});
|
|
storeMessage({
|
|
id: 'bot-1',
|
|
chat_jid: 'room@g.us',
|
|
sender: 'bot',
|
|
sender_name: 'Bot',
|
|
content: 'done',
|
|
timestamp: '2026-04-24T00:00:02.000Z',
|
|
is_from_me: false,
|
|
is_bot_message: true,
|
|
});
|
|
|
|
expect(getRecentChatMessages('room@g.us', 2)).toEqual([
|
|
expect.objectContaining({ id: 'human-1', message_source_kind: 'human' }),
|
|
expect.objectContaining({ id: 'bot-1', message_source_kind: 'bot' }),
|
|
]);
|
|
});
|
|
|
|
it('migrates legacy message tables and backfills source kind', () => {
|
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-msg-source-'));
|
|
const dbPath = path.join(tempDir, 'messages.db');
|
|
const legacyDb = new Database(dbPath);
|
|
legacyDb.exec(`
|
|
CREATE TABLE chats (
|
|
jid TEXT PRIMARY KEY,
|
|
name TEXT,
|
|
last_message_time TEXT,
|
|
channel TEXT,
|
|
is_group INTEGER DEFAULT 0
|
|
);
|
|
CREATE TABLE messages (
|
|
id TEXT,
|
|
chat_jid TEXT,
|
|
sender TEXT,
|
|
sender_name TEXT,
|
|
content TEXT,
|
|
timestamp TEXT,
|
|
seq INTEGER,
|
|
is_from_me INTEGER,
|
|
is_bot_message INTEGER DEFAULT 0,
|
|
PRIMARY KEY (id, chat_jid)
|
|
);
|
|
CREATE TABLE schema_migrations (
|
|
version INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
INSERT INTO chats (jid, name, last_message_time, channel, is_group)
|
|
VALUES ('room@g.us', 'Room', '2026-04-24T00:00:02.000Z', 'discord', 1);
|
|
INSERT INTO messages (
|
|
id, chat_jid, sender, sender_name, content, timestamp, seq, is_from_me, is_bot_message
|
|
) VALUES
|
|
('human-legacy', 'room@g.us', 'user', 'User', 'hello', '2026-04-24T00:00:01.000Z', 1, 0, 0),
|
|
('bot-legacy', 'room@g.us', 'bot', 'Bot', 'done', '2026-04-24T00:00:02.000Z', 2, 0, 1);
|
|
`);
|
|
const insertMigration = legacyDb.prepare(
|
|
'INSERT INTO schema_migrations (version, name) VALUES (?, ?)',
|
|
);
|
|
for (let version = 1; version <= 12; version += 1) {
|
|
insertMigration.run(version, `legacy-${version}`);
|
|
}
|
|
legacyDb.close();
|
|
|
|
_initTestDatabaseFromFile(dbPath);
|
|
|
|
expect(getRecentChatMessages('room@g.us', 2)).toEqual([
|
|
expect.objectContaining({
|
|
id: 'human-legacy',
|
|
message_source_kind: 'human',
|
|
}),
|
|
expect.objectContaining({ id: 'bot-legacy', message_source_kind: 'bot' }),
|
|
]);
|
|
});
|
|
});
|