mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-06-04 10:14:47 +08:00
test: add host-side routing and session resolution tests
Host-side (vitest): - Routed message preserves platformId/channelType/threadId on messages_in - Fan-out gives each agent correct per-agent routing - writeSessionRouting populates session_routing from messaging group - writeSessionRouting writes null routing for agent-shared sessions - Per-thread session includes thread_id in session_routing - Agent-shared resolves to same session on repeated calls - Agent-shared session has null messaging_group_id - findSessionByAgentGroup returns channel-bound session (documents #2332) - Skip: agent-shared/channel-bound coexistence (blocked on #2332 fix) Container-side (bun:test): - Internal tags stripped between message blocks - Mixed task + chat batch with correct routing The agent-shared tests uncovered the exact bug from #2332: findSessionByAgentGroup doesn't distinguish agent-shared from channel-bound sessions, so A2A resolution reuses a channel session when one exists. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -249,6 +249,51 @@ describe('poll loop integration', () => {
|
||||
await loopPromise.catch(() => {});
|
||||
});
|
||||
|
||||
it('internal tags between message blocks are stripped from scratchpad', async () => {
|
||||
insertMessage('m1', { sender: 'Alice', text: 'hi' }, { platformId: 'chan-1', channelType: 'discord' });
|
||||
|
||||
const provider = new MockProvider(
|
||||
{},
|
||||
() => '<internal>thinking about this...</internal><message to="discord-test">answer</message><internal>done thinking</internal>',
|
||||
);
|
||||
const controller = new AbortController();
|
||||
const loopPromise = runPollLoopWithTimeout(provider, controller.signal, 2000);
|
||||
|
||||
await waitFor(() => getUndeliveredMessages().length > 0, 2000);
|
||||
controller.abort();
|
||||
|
||||
const out = getUndeliveredMessages();
|
||||
expect(out).toHaveLength(1);
|
||||
expect(JSON.parse(out[0].content).text).toBe('answer');
|
||||
|
||||
await loopPromise.catch(() => {});
|
||||
});
|
||||
|
||||
it('handles mixed task + chat batch with correct origin metadata', async () => {
|
||||
// Seed destination for routing lookup
|
||||
insertMessage('m-chat', { sender: 'Alice', text: 'check this' }, { platformId: 'chan-1', channelType: 'discord' });
|
||||
// Task with same routing — simulates a scheduled task in a channel session
|
||||
getInboundDb()
|
||||
.prepare(
|
||||
`INSERT INTO messages_in (id, kind, timestamp, status, platform_id, channel_type, content)
|
||||
VALUES ('t-task', 'task', datetime('now'), 'pending', 'chan-1', 'discord', ?)`,
|
||||
)
|
||||
.run(JSON.stringify({ prompt: 'daily check' }));
|
||||
|
||||
const provider = new MockProvider({}, () => '<message to="discord-test">done</message>');
|
||||
const controller = new AbortController();
|
||||
const loopPromise = runPollLoopWithTimeout(provider, controller.signal, 2000);
|
||||
|
||||
await waitFor(() => getUndeliveredMessages().length > 0, 2000);
|
||||
controller.abort();
|
||||
|
||||
const out = getUndeliveredMessages();
|
||||
expect(out).toHaveLength(1);
|
||||
expect(out[0].platform_id).toBe('chan-1');
|
||||
|
||||
await loopPromise.catch(() => {});
|
||||
});
|
||||
|
||||
it('should inject destination reminder after a compacted event', async () => {
|
||||
// Two destinations — required for the reminder to fire (single-destination
|
||||
// groups have a fallback path that works without <message to="…"> wrapping).
|
||||
|
||||
Reference in New Issue
Block a user