Files
memind/intent-draft-service.test.mjs
T
john d58dc2a251 feat(wechat): add Intent Transaction Layer with unified task schema
Introduce Draft → Confirm → Commit flow for WeChat schedule intents behind
feature flags, plus h5_tasks dual-write/read aggregation and rollout scripts
so reminders and automations get explicit user confirmation before persisting.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 15:59:17 +08:00

122 lines
4.3 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { createIntentDraftService } from './intent-draft-service.mjs';
function createMemoryPool() {
const rows = new Map();
return {
async query(sql, params = []) {
if (sql.includes('UPDATE h5_intent_drafts') && sql.includes("status = 'expired'")) {
return [{ affectedRows: 0 }];
}
if (sql.includes('UPDATE h5_intent_drafts') && sql.includes("status = 'cancelled'") && sql.includes("status = 'draft'")) {
for (const row of rows.values()) {
if (row.user_id === params[1] && row.status === 'draft') {
row.status = 'cancelled';
row.updated_at = params[0];
}
}
return [{ affectedRows: 1 }];
}
if (sql.includes('INSERT INTO h5_intent_drafts')) {
const id = params[0];
rows.set(id, {
id,
user_id: params[1],
layer: params[2],
draft_type: params[3],
action_level: params[4],
title: params[5],
payload_json: params[6],
card_text: params[7],
status: 'draft',
source_channel: params[9],
source_message_id: params[10],
source_text: params[11],
committed_ref_json: null,
event_log_json: params[12],
expires_at: params[13],
created_at: params[14],
updated_at: params[15],
});
return [{ affectedRows: 1 }];
}
if (sql.includes('SELECT * FROM h5_intent_drafts WHERE id = ? AND user_id = ?')) {
const row = rows.get(params[0]);
return [[row && row.user_id === params[1] ? row : undefined].filter(Boolean)];
}
if (sql.includes('SELECT * FROM h5_intent_drafts') && sql.includes('user_id = ?') && sql.includes("status = 'draft'")) {
const userId = params[0];
const draft = [...rows.values()]
.filter((row) => row.user_id === userId && row.status === 'draft')
.sort((a, b) => b.created_at - a.created_at)[0];
return [[draft ?? undefined].filter(Boolean)];
}
if (sql.includes('SELECT * FROM h5_intent_drafts WHERE id = ?')) {
const row = rows.get(params[0]);
return [[row].filter(Boolean)];
}
if (sql.includes("SET status = 'cancelled'") && sql.includes('WHERE id = ?')) {
const row = rows.get(params[2]);
if (row) {
row.status = 'cancelled';
row.event_log_json = params[0];
row.updated_at = params[1];
}
return [{ affectedRows: 1 }];
}
if (sql.includes("SET status = 'committed'")) {
const row = rows.get(params[3]);
if (row) {
row.status = 'committed';
row.committed_ref_json = params[0];
row.event_log_json = params[1];
row.updated_at = params[2];
}
return [{ affectedRows: 1 }];
}
throw new Error(`Unexpected SQL: ${sql}`);
},
};
}
test('createDraft replaces previous pending draft for same user', async () => {
const pool = createMemoryPool();
const service = createIntentDraftService(pool, { clock: { now: () => 1000 }, ttlMs: 60000 });
const first = await service.createDraft({
userId: 'user-1',
layer: 'L1',
draftType: 'timed_reminder',
title: '第一次',
payload: { title: '第一次' },
cardText: 'card-1',
});
const second = await service.createDraft({
userId: 'user-1',
layer: 'L1',
draftType: 'timed_reminder',
title: '第二次',
payload: { title: '第二次' },
cardText: 'card-2',
});
const pending = await service.getPendingDraft('user-1');
assert.notEqual(first.id, second.id);
assert.equal(pending.title, '第二次');
});
test('markDraftCommitted stores committed ref', async () => {
const pool = createMemoryPool();
const service = createIntentDraftService(pool, { clock: { now: () => 2000 }, ttlMs: 60000 });
const draft = await service.createDraft({
userId: 'user-1',
layer: 'L1',
draftType: 'create_todo',
title: '跟进合同',
payload: { title: '跟进合同' },
cardText: 'card',
});
const committed = await service.markDraftCommitted(draft.id, 'user-1', { kind: 'create_todo', item: { id: 'item-1' } });
assert.equal(committed.status, 'committed');
assert.deepEqual(committed.committedRef, { kind: 'create_todo', item: { id: 'item-1' } });
});