Files
EJClaw/src/db-message-source.test.ts
ejclaw e036521054 fix: salvage EJClaw runtime hardening
- 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
2026-04-24 15:02:09 +09:00

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' }),
]);
});
});