import assert from 'node:assert/strict'; import { mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { test } from 'node:test'; import { formatChannelPeerLabel, lidFromWhatsappIdentity, parseConversationKey, resolveChannelPeerFromBinding, resolveContactIdentity, } from '../../../src/channels/shared/channel-peer.mjs'; import { ConversationStateStore } from '../../../src/channels/shared/conversation-state-store.mjs'; test('parseConversationKey covers direct, shared group, and per-user group', () => { assert.deepEqual(parseConversationKey('direct:8618111111111@s.whatsapp.net'), { kind: 'direct', conversationId: '8618111111111@s.whatsapp.net', senderId: null, }); assert.deepEqual(parseConversationKey('group:120363@g.us'), { kind: 'group', conversationId: '120363@g.us', senderId: null, }); assert.deepEqual(parseConversationKey('group:120363@g.us:user:8618222222222@s.whatsapp.net'), { kind: 'group', conversationId: '120363@g.us', senderId: '8618222222222@s.whatsapp.net', }); assert.equal(parseConversationKey(''), null); assert.equal(parseConversationKey('other:x'), null); }); test('resolveChannelPeerFromBinding builds DM and group labels from access grant', () => { const grant = { contacts: [{ phone: '8618222222222', lids: ['91010910658657@lid'], pushName: 'Alice', }], groups: { '120363@g.us': { title: 'Ops Room', admins: [], members: [] }, }, }; const direct = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'direct:8618222222222@s.whatsapp.net', grant, }); assert.equal(direct.label, 'Alice · 8618222222222'); assert.equal(direct.kind, 'direct'); assert.equal(direct.phone, '8618222222222'); const lidDirect = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'direct:91010910658657@lid', grant, }); assert.equal(lidDirect.label, 'Alice · 8618222222222'); assert.equal(lidDirect.phone, '8618222222222'); assert.doesNotMatch(lidDirect.label, /@lid/); const groupUser = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'group:120363@g.us:user:91010910658657@lid', grant, }); assert.equal(groupUser.label, 'Ops Room · Alice · 8618222222222'); assert.equal(groupUser.groupTitle, 'Ops Room'); assert.doesNotMatch(groupUser.label, /@lid/); const groupShared = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'group:120363@g.us', grant, }); assert.equal(groupShared.label, 'Ops Room'); }); test('resolveChannelPeerFromBinding exposes agentPreset from grant or bot default', () => { const grant = { version: 1, globalAdmins: ['8618111111111'], directMembers: [ { phone: '8618222222222', canExecuteCommands: true, agentPreset: 'ops-dm' }, ], groups: { '120363@g.us': { title: 'Ops Room', agentPreset: 'ops-group', admins: [], members: [], }, }, contacts: [{ phone: '8618222222222', lids: ['91010910658657@lid'], pushName: 'Alice', }], }; const direct = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'direct:8618222222222@s.whatsapp.net', grant, botAgentPreset: 'bot-default', }); assert.equal(direct.agentPreset, 'ops-dm'); const group = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'group:120363@g.us', grant, botAgentPreset: 'bot-default', }); assert.equal(group.agentPreset, 'ops-group'); const fallback = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'direct:8618999999999@s.whatsapp.net', grant, botAgentPreset: 'bot-default', }); assert.equal(fallback.agentPreset, 'bot-default'); }); test('resolveChannelPeerFromBinding never falls back to raw LID when contact is incomplete', () => { const grant = { contacts: [{ lids: ['91010910658657@lid'], pushName: 'Bob' }], groups: { '120363@g.us': { title: 'test2', admins: [], members: [] }, }, }; const direct = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'direct:91010910658657@lid', grant, }); assert.equal(direct.label, 'Bob'); assert.equal(direct.phone, null); const unknown = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'direct:111222333444@lid', grant, }); assert.equal(unknown?.kind, 'direct'); assert.equal(unknown?.label, 'WhatsApp 私聊'); assert.doesNotMatch(unknown.label, /@lid|111222333444/); const groupOnlyLid = resolveChannelPeerFromBinding({ channel: 'whatsapp', botId: 'bot-1', conversationKey: 'group:120363@g.us:user:111222333444@lid', grant, }); assert.equal(groupOnlyLid.label, 'test2'); assert.doesNotMatch(groupOnlyLid.label, /@lid|\d{10,}@/); }); test('lid and label helpers stay human-facing', () => { assert.equal(lidFromWhatsappIdentity('91010910658657@lid'), '91010910658657@lid'); assert.equal(lidFromWhatsappIdentity('91010910658657:12@lid'), '91010910658657@lid'); assert.equal(lidFromWhatsappIdentity('8618222222222@s.whatsapp.net'), null); assert.equal(formatChannelPeerLabel('Ops', '', '8618', null), 'Ops · 8618'); assert.deepEqual( resolveContactIdentity({ contacts: [{ phone: '8618', lids: ['9@lid'], pushName: 'N' }], }, '9@lid'), { phone: '8618', pushName: 'N' }, ); }); test('ConversationStateStore.keyForSession reverses session bindings', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-im-channel-peer-')); try { const store = await new ConversationStateStore(join(root, 'state.json')).load(); await store.setSession('direct:u1', 'session-a'); await store.setSession('group:g1:user:u2', 'session-b'); assert.equal(store.keyForSession('session-a'), 'direct:u1'); assert.equal(store.keyForSession('session-b'), 'group:g1:user:u2'); assert.equal(store.keyForSession('missing'), null); } finally { await rm(root, { recursive: true, force: true }); } }); test('ConversationStateStore keeps channel provenance after /new-style clearSession', async () => { const root = await mkdtemp(join(tmpdir(), 'dsh-im-session-origin-')); try { const path = join(root, 'state.json'); const store = await new ConversationStateStore(path).load(); await store.setSession('direct:8618222222222@s.whatsapp.net', 'session-old'); assert.equal(store.isLiveSession('session-old'), true); await store.clearSession('direct:8618222222222@s.whatsapp.net'); assert.equal(store.sessionFor('direct:8618222222222@s.whatsapp.net'), null); assert.equal(store.isLiveSession('session-old'), false); // Unbound Harness session must still resolve for DSH channel chip / cron peer. assert.equal(store.keyForSession('session-old'), 'direct:8618222222222@s.whatsapp.net'); await store.setSession('direct:8618222222222@s.whatsapp.net', 'session-new'); assert.equal(store.keyForSession('session-new'), 'direct:8618222222222@s.whatsapp.net'); assert.equal(store.keyForSession('session-old'), 'direct:8618222222222@s.whatsapp.net'); assert.equal(store.isLiveSession('session-new'), true); assert.equal(store.isLiveSession('session-old'), false); const reloaded = await new ConversationStateStore(path).load(); assert.equal(reloaded.keyForSession('session-old'), 'direct:8618222222222@s.whatsapp.net'); assert.equal(reloaded.keyForSession('session-new'), 'direct:8618222222222@s.whatsapp.net'); } finally { await rm(root, { recursive: true, force: true }); } });