Files
memind/health-wechat-channel.test.mjs
T
john 2baf29b3ae feat(health): complete P0 health channel — baseline engine, page-data, MindSpace UI
Deliver encrypted health zone, observation API, baseline maturity pipeline,
page-data bindings, and H5/WeChat channel integration for health P0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 18:04:57 +08:00

205 lines
6.4 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { createHealthChannelSessionStore } from './health-channel-session-store.mjs';
import { createInMemoryHealthObservationStore } from './health-observation-store.mjs';
import { createInMemoryHealthEventStore } from './health-event-store.mjs';
import { createHealthObservationService } from './health-observation-service.mjs';
import {
HEALTH_MENU_TEXT,
applyHealthChannelTurn,
applyHealthWechatTurn,
isHealthEnterText,
shouldFallThroughWechatHealthMenuEvent,
} from './health-wechat-turn.mjs';
import { createIdleHealthChannelState } from './health-channel-state.mjs';
import { handleWechatHealthChannel } from './wechat/handlers/health.mjs';
test('health menu event only falls through when the feature flag is on', () => {
const inbound = { event: 'click', eventKey: 'MEMIND_HEALTH' };
assert.equal(shouldFallThroughWechatHealthMenuEvent(inbound, {}), false);
assert.equal(
shouldFallThroughWechatHealthMenuEvent(inbound, { MEMIND_HEALTH_ENABLED: '1' }),
true,
);
});
test('wechat health channel requires confirm before writing observations', async () => {
const store = createHealthChannelSessionStore();
const observations = createInMemoryHealthObservationStore();
const observationService = createHealthObservationService({ store: observations });
const userId = 'user-1';
const entered = await handleWechatHealthChannel({
enabled: true,
userId,
intent: { msgType: 'text', agentText: '健康助手' },
inbound: {},
store,
observationService,
});
assert.match(entered, /专用通道/);
const parsed = await handleWechatHealthChannel({
enabled: true,
userId,
intent: { msgType: 'text', agentText: '137/82' },
inbound: {},
store,
observationService,
});
assert.match(parsed, /确认保存/);
assert.equal((await observations.list(userId)).length, 0);
const saved = await handleWechatHealthChannel({
enabled: true,
userId,
intent: { msgType: 'text', agentText: '1' },
inbound: {},
store,
observationService,
});
assert.match(saved, /已记录/);
const rows = await observations.list(userId);
assert.equal(rows.length, 2);
assert.equal(rows.some((row) => row.metricType === 'bp_systolic' && row.valueNum === 137), true);
});
test('wechat health channel prepends unread alerts on enter', async () => {
const store = createHealthChannelSessionStore();
const eventStore = createInMemoryHealthEventStore();
await eventStore.insertIfNew('user-alert', {
severity: 'alert',
eventType: 'threshold_breach',
ruleId: 'bp_sys_high',
metricType: 'bp_systolic',
message: '收缩压触发阈值',
});
const entered = await handleWechatHealthChannel({
enabled: true,
userId: 'user-alert',
intent: { msgType: 'text', agentText: '健康助手' },
inbound: {},
store,
eventStore,
});
assert.match(entered, /未读健康提醒/);
assert.match(entered, /专用通道/);
});
test('disabled health channel does not intercept ordinary wechat chat', async () => {
const reply = await handleWechatHealthChannel({
enabled: false,
userId: 'user-1',
intent: { msgType: 'text', agentText: '健康助手' },
inbound: {},
store: createHealthChannelSessionStore(),
});
assert.equal(reply, null);
});
test('enter keywords are specific and photo does not auto-commit', () => {
assert.equal(isHealthEnterText('健康助手'), true);
assert.equal(isHealthEnterText('健康小助手'), true);
assert.equal(isHealthEnterText('健康饮食怎么做'), false);
const inValue = applyHealthWechatTurn({
session: {
channelState: {
sessionKind: 'health',
channel: 'wechat',
step: 'await_value',
action: 'record',
metricSet: 'blood_pressure',
context: 'morning',
pendingDraftId: null,
lastPrompt: null,
},
pendingValues: { context: 'morning' },
},
msgType: 'image',
text: '',
});
assert.equal(inValue.handled, true);
assert.equal(inValue.commit, null);
assert.match(inValue.reply, /不会|手输|确认/);
});
test('unconfirmed insert is rejected by the observation store', async () => {
const store = createInMemoryHealthObservationStore();
await assert.rejects(
() => store.insert('user-1', { metricType: 'bp_systolic', valueNum: 120 }),
(error) => error.code === 'health_unconfirmed',
);
});
test('h5 channel can enter without menu event key', () => {
const entered = applyHealthChannelTurn({
channel: 'h5',
forceEnter: true,
text: '',
});
assert.equal(entered.handled, true);
assert.match(entered.reply, /专用通道/);
assert.equal(entered.session?.channelState?.channel, 'h5');
});
test('menu item 2 enters document upload mode', () => {
const picked = applyHealthChannelTurn({
channel: 'h5',
session: {
channelState: createIdleHealthChannelState({ channel: 'h5' }),
pendingValues: null,
},
text: '2',
});
assert.match(picked.reply, /报告照片/);
assert.equal(picked.session?.channelState?.action, 'document');
});
test('document image archives without device extraction commit shape', () => {
const uploaded = applyHealthChannelTurn({
channel: 'h5',
session: {
channelState: {
...createIdleHealthChannelState({ channel: 'h5' }),
action: 'document',
},
pendingValues: null,
},
msgType: 'image',
text: '',
});
assert.equal(uploaded.commit?.type, 'document');
assert.match(uploaded.reply, /归档/);
});
test('menu text is the exclusive wechat prompt', () => {
assert.match(HEALTH_MENU_TEXT, /0 退出健康通道/);
});
test('h5 idle free text falls through to agent instead of replaying menu', () => {
const result = applyHealthChannelTurn({
channel: 'h5',
session: {
channelState: createIdleHealthChannelState({ channel: 'h5' }),
pendingValues: null,
},
text: '帮我生成一份健康报告,分析一下最近血压',
});
assert.equal(result.handled, false);
});
test('wechat idle unknown text still replays menu', () => {
const result = applyHealthChannelTurn({
channel: 'wechat',
session: {
channelState: createIdleHealthChannelState({ channel: 'wechat' }),
pendingValues: null,
},
text: '帮我生成一份健康报告',
});
assert.equal(result.handled, true);
assert.match(result.reply, /专用通道/);
});