Fix mock pools for new Postgres ops

---

Build: not run | Tests: pass - 7 passed (7 files)

---
Build: pass | Tests: pass — Tests  869 passed (869)
This commit is contained in:
Clawdie AI 2026-04-11 10:38:55 +00:00
parent 8e661311b5
commit 1cf201c965
3 changed files with 117 additions and 46 deletions

View file

@ -19,16 +19,19 @@ function createTestPool(): pg.Pool {
} as pg.QueryResult;
}
if (/INSERT INTO registered_groups/i.test(sql)) {
if (/INSERT INTO REGISTERED_GROUPS/i.test(s)) {
const jid = params![0] as string;
const hasJailConfigParam = params!.length >= 7;
const jailConfig = hasJailConfigParam ? params![5] : null;
const requiresTrigger = hasJailConfigParam ? params![6] : params![5];
groups.set(jid, {
jid,
name: params![1],
folder: params![2],
trigger_pattern: params![3],
added_at: params![4],
jail_config: params![5],
requires_trigger: params![6],
jail_config: jailConfig,
requires_trigger: requiresTrigger,
});
return {
rows: [],
@ -39,8 +42,18 @@ function createTestPool(): pg.Pool {
} as pg.QueryResult;
}
if (/SELECT \* FROM registered_groups/i.test(sql)) {
if (sql.includes('WHERE jid = $1')) {
if (s.includes('SELECT COUNT')) {
return {
rows: [{ count: String(groups.size) }],
rowCount: 1,
command: 'SELECT',
oid: 0,
fields: [],
} as pg.QueryResult;
}
if (s.includes('FROM REGISTERED_GROUPS')) {
if (s.includes('WHERE JID = $1')) {
const row = groups.get(params![0] as string);
return {
rows: row ? [row] : [],
@ -59,16 +72,6 @@ function createTestPool(): pg.Pool {
} as pg.QueryResult;
}
if (/SELECT COUNT/i.test(sql)) {
return {
rows: [{ count: String(groups.size) }],
rowCount: 1,
command: 'SELECT',
oid: 0,
fields: [],
} as pg.QueryResult;
}
return {
rows: [],
rowCount: 0,

View file

@ -0,0 +1,58 @@
import { describe, expect, it } from 'vitest';
import {
defaultMemoryDbName,
defaultMemoryDbUser,
defaultOpsDbName,
defaultOpsDbUser,
defaultSkillsDbName,
defaultSkillsDbUser,
isEnvTemplateValue,
normalizeDbIdentifierBase,
usableEnvValue,
} from './db-identifiers.js';
describe('normalizeDbIdentifierBase', () => {
it('normalizes to lowercase and underscores', () => {
expect(normalizeDbIdentifierBase('Clawdie AI')).toBe('clawdie_ai');
});
it('trims extra separators', () => {
expect(normalizeDbIdentifierBase('---Clawdie---AI---')).toBe('clawdie_ai');
});
it('falls back to clawdie for empty values', () => {
expect(normalizeDbIdentifierBase(' ')).toBe('clawdie');
});
});
describe('default db identifiers', () => {
it('builds skills identifiers', () => {
expect(defaultSkillsDbUser('Clawdie AI')).toBe('clawdie_ai_reader');
expect(defaultSkillsDbName('Clawdie AI')).toBe('clawdie_ai_skills');
});
it('builds memory identifiers', () => {
expect(defaultMemoryDbUser('Clawdie AI')).toBe('clawdie_ai_brain');
expect(defaultMemoryDbName('Clawdie AI')).toBe('clawdie_ai_brain');
});
it('builds ops identifiers', () => {
expect(defaultOpsDbUser('Clawdie AI')).toBe('clawdie_ai_ops');
expect(defaultOpsDbName('Clawdie AI')).toBe('clawdie_ai_ops');
});
});
describe('env template helpers', () => {
it('detects env template placeholders', () => {
expect(isEnvTemplateValue('${OPS_DB_URL}')).toBe(true);
expect(isEnvTemplateValue('postgres://user@host/db')).toBe(false);
});
it('filters template placeholders', () => {
expect(usableEnvValue('${OPS_DB_URL}')).toBeUndefined();
expect(usableEnvValue('postgres://user@host/db')).toBe(
'postgres://user@host/db',
);
});
});

View file

@ -46,7 +46,7 @@ class MockPool {
} as QueryResult;
}
if (/INSERT INTO chats/i.test(s)) {
if (/INSERT INTO CHATS/i.test(s)) {
const [jid, name, last_message_time, channel, is_group] = (params ||
[]) as [string, string, string, string | null, number | null];
const existing = this.chats.get(jid);
@ -92,17 +92,20 @@ class MockPool {
} as QueryResult;
}
if (/SELECT.*FROM chats/i.test(s) && !/COUNT/i.test(s)) {
if (/SELECT[\s\S]*FROM CHATS/i.test(s) && !/COUNT/i.test(s)) {
let rows = [...this.chats.values()];
if (s.includes('jid = $1') && params?.[0]) {
if (s.includes('JID = $1') && params?.[0]) {
rows = rows.filter((r) => r.jid === params[0]);
}
if (s.includes("jid <> '__group_sync__'") && s.includes('name <> jid')) {
if (
s.includes("JID <> '__GROUP_SYNC__'") &&
s.includes('NAME <> JID')
) {
rows = rows.filter(
(r) => r.jid !== '__group_sync__' && r.name !== r.jid,
);
}
if (s.includes('ORDER BY last_message_time DESC')) {
if (s.includes('ORDER BY LAST_MESSAGE_TIME DESC')) {
rows.sort((a, b) =>
(b.last_message_time as string).localeCompare(
a.last_message_time as string,
@ -121,9 +124,9 @@ class MockPool {
} as QueryResult;
}
if (/SELECT COUNT.*FROM chats/i.test(s)) {
if (/SELECT COUNT[\s\S]*FROM CHATS/i.test(s)) {
let rows = [...this.chats.values()];
if (s.includes("jid <> '__group_sync__'")) {
if (s.includes("JID <> '__GROUP_SYNC__'")) {
rows = rows.filter((r) => r.jid !== '__group_sync__');
}
return {
@ -135,7 +138,7 @@ class MockPool {
} as QueryResult;
}
if (/INSERT INTO messages/i.test(s)) {
if (/INSERT INTO MESSAGES/i.test(s)) {
const [
id,
chat_jid,
@ -175,22 +178,22 @@ class MockPool {
} as QueryResult;
}
if (/SELECT.*FROM messages/i.test(s)) {
if (/SELECT[\s\S]*FROM MESSAGES/i.test(s)) {
let rows = [...this.messages.values()];
if (s.includes('chat_jid = $1')) {
if (s.includes('CHAT_JID = $1')) {
rows = rows.filter((r) => r.chat_jid === params![0]);
}
if (s.includes('chat_jid IN (')) {
if (s.includes('CHAT_JID IN (')) {
const jids = params!.slice(1, -1) as string[];
rows = rows.filter((r) => jids.includes(r.chat_jid as string));
}
if (s.includes('timestamp > $1') || s.includes('timestamp >')) {
const tsParam = s.includes('chat_jid = $1')
if (s.includes('TIMESTAMP > $1') || s.includes('TIMESTAMP >')) {
const tsParam = s.includes('CHAT_JID = $1')
? (params![1] as string)
: (params![0] as string);
rows = rows.filter((r) => (r.timestamp as string) > tsParam);
}
if (s.includes('is_bot_message = 0')) {
if (s.includes('IS_BOT_MESSAGE = 0')) {
rows = rows.filter((r) => r.is_bot_message === 0);
}
const botPrefixParam = s.includes('NOT LIKE $3')
@ -217,7 +220,7 @@ class MockPool {
} as QueryResult;
}
if (/INSERT INTO scheduled_tasks/i.test(s)) {
if (/INSERT INTO SCHEDULED_TASKS/i.test(s)) {
const [
id,
group_folder,
@ -264,9 +267,9 @@ class MockPool {
} as QueryResult;
}
if (/SELECT.*FROM scheduled_tasks/i.test(s) && !/COUNT/i.test(s)) {
if (/SELECT[\s\S]*FROM SCHEDULED_TASKS/i.test(s) && !/COUNT/i.test(s)) {
let rows = [...this.tasks.values()];
if (s.includes('WHERE id = $1')) {
if (s.includes('WHERE ID = $1')) {
rows = rows.filter((r) => r.id === params![0]);
return {
rows,
@ -276,10 +279,10 @@ class MockPool {
fields: [],
} as QueryResult;
}
if (s.includes('WHERE group_folder = $1')) {
if (s.includes('WHERE GROUP_FOLDER = $1')) {
rows = rows.filter((r) => r.group_folder === params![0]);
}
if (s.includes("status = 'active'") && s.includes('next_run')) {
if (s.includes("STATUS = 'ACTIVE'") && s.includes('NEXT_RUN')) {
const now = params![0] as string;
rows = rows.filter(
(r) =>
@ -288,12 +291,12 @@ class MockPool {
(r.next_run as string) <= now,
);
}
if (s.includes('ORDER BY created_at DESC')) {
if (s.includes('ORDER BY CREATED_AT DESC')) {
rows.sort((a, b) =>
(b.created_at as string).localeCompare(a.created_at as string),
);
}
if (s.includes('ORDER BY next_run')) {
if (s.includes('ORDER BY NEXT_RUN')) {
rows.sort((a, b) =>
((a.next_run as string) || '').localeCompare(
(b.next_run as string) || '',
@ -309,17 +312,17 @@ class MockPool {
} as QueryResult;
}
if (/UPDATE scheduled_tasks/i.test(s)) {
if (/UPDATE SCHEDULED_TASKS/i.test(s)) {
const lastParam = params![params!.length - 1] as string;
const task = this.tasks.get(lastParam);
if (task) {
if (s.includes('prompt =')) task.prompt = params!.shift();
if (s.includes('status =') && s.includes("'paused'") === false) {
const statusIdx = s.indexOf('status =');
if (s.includes('PROMPT =')) task.prompt = params!.shift();
if (s.includes('STATUS =') && s.includes("'PAUSED'") === false) {
const statusIdx = s.indexOf('STATUS =');
if (statusIdx > -1 && params!.length >= 2)
task.status = params![params!.length - 2];
}
if (s.includes('SET status = CASE') && s.includes("'completed'")) {
if (s.includes('SET STATUS = CASE') && s.includes("'COMPLETED'")) {
// updateTaskAfterRun: complex, parse from params
}
const id = params![params!.length - 1] as string;
@ -448,7 +451,7 @@ class MockPool {
} as QueryResult;
}
if (/INSERT INTO registered_groups/i.test(s)) {
if (/INSERT INTO REGISTERED_GROUPS/i.test(s)) {
const [
jid,
name,
@ -484,8 +487,8 @@ class MockPool {
} as QueryResult;
}
if (/SELECT.*FROM registered_groups/i.test(s)) {
if (s.includes('WHERE jid = $1')) {
if (/SELECT[\s\S]*FROM REGISTERED_GROUPS/i.test(s)) {
if (s.includes('WHERE JID = $1')) {
const row = this.registeredGroups.get(params![0] as string);
return {
rows: row ? [row] : [],
@ -504,7 +507,7 @@ class MockPool {
} as QueryResult;
}
if (/SELECT COUNT.*FROM registered_groups/i.test(s)) {
if (/SELECT COUNT[\s\S]*FROM REGISTERED_GROUPS/i.test(s)) {
return {
rows: [{ count: String(this.registeredGroups.size) }],
command: 'SELECT',
@ -514,7 +517,7 @@ class MockPool {
} as QueryResult;
}
if (/UPDATE registered_groups/i.test(s)) {
if (/UPDATE REGISTERED_GROUPS/i.test(s)) {
return {
rows: [],
command: '',
@ -534,6 +537,13 @@ class MockPool {
}
async end(): Promise<void> {}
async connect(): Promise<{ query: MockPool['query']; release: () => void }> {
return {
query: this.query.bind(this),
release: () => {},
};
}
}
export function createMockPool(): Pool {