import { describe, expect, it } from "vitest" ;
import type { FollowupRun, QueueSettings } from "./queue.js" ;
import { enqueueFollowupRun, scheduleFollowupDrain } from "./queue.js" ;
import {
createDeferred,
createQueueTestRun as createRun,
installQueueRuntimeErrorSilencer,
} from "./queue.test-helpers.js" ;
import { resolveFollowupAuthorizationKey } from "./queue/drain.js" ;
installQueueRuntimeErrorSilencer();
describe("followup queue collect routing" , () => {
it("does not collect when destinations differ" , async () => {
const key = `test-collect-diff-to-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const expectedCalls = 2 ;
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
if (calls.length >= expectedCalls) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
enqueueFollowupRun(
key,
createRun({
prompt: "one" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
}),
settings,
);
enqueueFollowupRun(
key,
createRun({
prompt: "two" ,
originatingChannel: "slack" ,
originatingTo: "channel:B" ,
}),
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.prompt).toBe("one" );
expect(calls[1 ]?.prompt).toBe("two" );
});
it("collects when channel+destination match" , async () => {
const key = `test-collect-same-to-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
enqueueFollowupRun(
key,
createRun({
prompt: "one" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
}),
settings,
);
enqueueFollowupRun(
key,
createRun({
prompt: "two" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
}),
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.prompt).toContain("[Queued messages while agent was busy]" );
expect(calls[0 ]?.originatingChannel).toBe("slack" );
expect(calls[0 ]?.originatingTo).toBe("channel:A" );
});
it("carries image payloads across collected batches" , async () => {
const key = `test-collect-images-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
const firstImage = { type: "image" as const , data: "first" , mimeType: "image/png" };
const secondImage = { type: "image" as const , data: "second" , mimeType: "image/png" };
enqueueFollowupRun(
key,
{
...createRun({
prompt: "one" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
}),
images: [firstImage],
imageOrder: ["inline" ],
},
settings,
);
enqueueFollowupRun(
key,
{
...createRun({
prompt: "two" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
}),
images: [secondImage],
imageOrder: ["inline" ],
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.images).toEqual([firstImage, secondImage]);
expect(calls[0 ]?.imageOrder).toEqual(["inline" , "inline" ]);
});
it("splits collect batches when sender authorization changes" , async () => {
const key = `test-collect-auth-split-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const expectedCalls = 2 ;
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
if (calls.length >= expectedCalls) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
const nonOwner = createRun({
prompt: "use the gateway tool" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
enqueueFollowupRun(
key,
{
...nonOwner,
run: {
...nonOwner.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderIsOwner: false ,
},
},
settings,
);
const owner = createRun({
prompt: "what's the weather?" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
enqueueFollowupRun(
key,
{
...owner,
run: {
...owner.run,
senderId: "owner-1" ,
senderName: "Owner" ,
senderIsOwner: true ,
},
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls.map((call) => call.run.senderIsOwner)).toEqual([false , true ]);
expect(calls[0 ]?.prompt).toContain("use the gateway tool" );
expect(calls[0 ]?.prompt).not.toContain("what's the weather?" );
expect(calls[1 ]?.prompt).toContain("what's the weather?" );
expect(calls[1 ]?.prompt).toContain("(from Owner)" );
});
it("keeps one collect batch when authorization context matches" , async () => {
const key = `test-collect-auth-match-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
const first = createRun({
prompt: "first" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
const second = createRun({
prompt: "second" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
enqueueFollowupRun(
key,
{
...first,
run: {
...first.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderUsername: "guest" ,
senderIsOwner: false ,
},
},
settings,
);
enqueueFollowupRun(
key,
{
...second,
run: {
...second.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderUsername: "guest" ,
senderIsOwner: false ,
},
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls).toHaveLength(1 );
expect(calls[0 ]?.prompt).toContain("first" );
expect(calls[0 ]?.prompt).toContain("second" );
expect(calls[0 ]?.prompt).toContain("(from Guest)" );
});
it("keeps one collect batch when only sender display fields drift" , async () => {
const key = `test-collect-auth-display-drift-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
const first = createRun({
prompt: "first" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
const second = createRun({
prompt: "second" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
enqueueFollowupRun(
key,
{
...first,
run: {
...first.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderUsername: "guest" ,
senderIsOwner: false ,
},
},
settings,
);
enqueueFollowupRun(
key,
{
...second,
run: {
...second.run,
senderId: "user-1" ,
senderName: "Guest User" ,
senderUsername: "guest-renamed" ,
senderIsOwner: false ,
},
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls).toHaveLength(1 );
expect(calls[0 ]?.prompt).toContain("first" );
expect(calls[0 ]?.prompt).toContain("second" );
expect(calls[0 ]?.prompt).toContain("(from Guest)" );
expect(calls[0 ]?.prompt).toContain("(from Guest User)" );
});
it("splits collect batches when exec context changes" , async () => {
const key = `test-collect-exec-split-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const expectedCalls = 2 ;
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
if (calls.length >= expectedCalls) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
const base = createRun({
prompt: "first" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
enqueueFollowupRun(
key,
{
...base,
run: {
...base.run,
senderId: "owner-1" ,
senderIsOwner: true ,
bashElevated: { enabled: false , allowed: true , defaultLevel: "off" },
},
},
settings,
);
enqueueFollowupRun(
key,
{
...createRun({
prompt: "second" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
}),
run: {
...base.run,
senderId: "owner-1" ,
senderIsOwner: true ,
bashElevated: { enabled: true , allowed: true , defaultLevel: "on" },
execOverrides: { ask: "always" },
},
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.prompt).toContain("first" );
expect(calls[0 ]?.prompt).not.toContain("second" );
expect(calls[1 ]?.prompt).toContain("second" );
expect(calls[1 ]?.run.bashElevated?.enabled).toBe(true );
expect(calls[1 ]?.run.execOverrides?.ask).toBe("always" );
});
it("uses the newest run within a matching authorization batch" , async () => {
const key = `test-collect-latest-run-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
const first = createRun({ prompt: "first" , originatingChannel: "slack" , originatingTo: "A" });
const second = createRun({
prompt: "second" ,
originatingChannel: "slack" ,
originatingTo: "A" ,
});
enqueueFollowupRun(
key,
{
...first,
run: {
...first.run,
provider: "openai" ,
model: "gpt-5.4" ,
senderId: "user-1" ,
senderName: "Guest" ,
senderIsOwner: false ,
},
},
settings,
);
enqueueFollowupRun(
key,
{
...second,
run: {
...second.run,
provider: "anthropic" ,
model: "sonnet-4.6" ,
senderId: "user-1" ,
senderName: "Guest" ,
senderIsOwner: false ,
},
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls).toHaveLength(1 );
expect(calls[0 ]?.run.provider).toBe("anthropic" );
expect(calls[0 ]?.run.model).toBe("sonnet-4.6" );
});
it("delivers and clears summary-only collect drains after cross-channel items" , async () => {
const key = `test-collect-summary-only-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const expectedCalls = 3 ;
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
if (calls.length >= expectedCalls) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 2 ,
dropPolicy: "summarize" ,
};
enqueueFollowupRun(
key,
createRun({
prompt: "first" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
}),
settings,
);
enqueueFollowupRun(
key,
createRun({
prompt: "second" ,
originatingChannel: "slack" ,
originatingTo: "channel:B" ,
}),
settings,
);
enqueueFollowupRun(
key,
createRun({
prompt: "third" ,
originatingChannel: "slack" ,
originatingTo: "channel:C" ,
}),
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls).toHaveLength(3 );
expect(calls[0 ]?.prompt).toBe("second" );
expect(calls[1 ]?.prompt).toBe("third" );
expect(calls[2 ]?.prompt).toContain("[Queue overflow] Dropped 1 message due to cap." );
expect(calls[2 ]?.prompt).toContain("- first" );
});
it("preserves collect order when authorization changes more than once" , async () => {
const key = `test-collect-auth-order-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const expectedCalls = 3 ;
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
if (calls.length >= expectedCalls) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
const first = createRun({ prompt: "first" , originatingChannel: "slack" , originatingTo: "A" });
const second = createRun({ prompt: "second" , originatingChannel: "slack" , originatingTo: "A" });
const third = createRun({ prompt: "third" , originatingChannel: "slack" , originatingTo: "A" });
enqueueFollowupRun(
key,
{
...first,
run: { ...first.run, senderId: "user-a" , senderName: "A" , senderIsOwner: false },
},
settings,
);
enqueueFollowupRun(
key,
{
...second,
run: { ...second.run, senderId: "owner-1" , senderName: "Owner" , senderIsOwner: true },
},
settings,
);
enqueueFollowupRun(
key,
{
...third,
run: { ...third.run, senderId: "user-a" , senderName: "A" , senderIsOwner: false },
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls.map((call) => call.prompt)).toEqual([
expect.stringContaining("first" ),
expect.stringContaining("second" ),
expect.stringContaining("third" ),
]);
});
it("collects Slack messages in same thread and preserves string thread id" , async () => {
const key = `test-collect-slack-thread -same-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
enqueueFollowupRun(
key,
createRun({
prompt: "one" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
originatingThreadId: "1706000000.000001" ,
}),
settings,
);
enqueueFollowupRun(
key,
createRun({
prompt: "two" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
originatingThreadId: "1706000000.000001" ,
}),
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.prompt).toContain("[Queued messages while agent was busy]" );
expect(calls[0 ]?.originatingThreadId).toBe("1706000000.000001" );
});
it("does not collect Slack messages when thread ids differ" , async () => {
const key = `test-collect-slack-thread -diff-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const expectedCalls = 2 ;
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
if (calls.length >= expectedCalls) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
enqueueFollowupRun(
key,
createRun({
prompt: "one" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
originatingThreadId: "1706000000.000001" ,
}),
settings,
);
enqueueFollowupRun(
key,
createRun({
prompt: "two" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
originatingThreadId: "1706000000.000002" ,
}),
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.prompt).toBe("one" );
expect(calls[1 ]?.prompt).toBe("two" );
expect(calls[0 ]?.originatingThreadId).toBe("1706000000.000001" );
expect(calls[1 ]?.originatingThreadId).toBe("1706000000.000002" );
});
it("retries collect-mode batches without losing queued items" , async () => {
const key = `test-collect-retry-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
let attempt = 0 ;
const runFollowup = async (run: FollowupRun) => {
attempt += 1 ;
if (attempt === 1 ) {
throw new Error("transient failure" );
}
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
enqueueFollowupRun(key, createRun({ prompt: "one" }), settings);
enqueueFollowupRun(key, createRun({ prompt: "two" }), settings);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.prompt).toContain("Queued #1\none" );
expect(calls[0 ]?.prompt).toContain("Queued #2\ntwo" );
});
it("retries only the remaining collect auth groups after a partial failure" , async () => {
const key = `test-collect-partial-retry-${Date.now()}`;
const attempts: FollowupRun[] = [];
const successfulCalls: FollowupRun[] = [];
const done = createDeferred<void >();
let attempt = 0 ;
const runFollowup = async (run: FollowupRun) => {
attempt += 1 ;
attempts.push(run);
if (attempt === 2 ) {
throw new Error("transient failure" );
}
successfulCalls.push(run);
if (attempt >= 3 ) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 50 ,
dropPolicy: "summarize" ,
};
const guest = createRun({
prompt: "guest message" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
const owner = createRun({
prompt: "owner message" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
enqueueFollowupRun(
key,
{
...guest,
run: {
...guest.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderIsOwner: false ,
},
},
settings,
);
enqueueFollowupRun(
key,
{
...owner,
run: {
...owner.run,
senderId: "owner-1" ,
senderName: "Owner" ,
senderIsOwner: true ,
},
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
const guestAttempts = attempts.filter((call) => call.prompt.includes("guest message" ));
const ownerAttempts = attempts.filter((call) => call.prompt.includes("owner message" ));
expect(attempts).toHaveLength(3 );
expect(guestAttempts).toHaveLength(1 );
expect(ownerAttempts).toHaveLength(2 );
expect(successfulCalls.map((call) => call.prompt)).toEqual([
expect.stringContaining("guest message" ),
expect.stringContaining("owner message" ),
]);
});
it("retries overflow summary delivery without losing dropped previews" , async () => {
const key = `test-overflow-summary-retry-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
let attempt = 0 ;
const runFollowup = async (run: FollowupRun) => {
attempt += 1 ;
if (attempt === 1 ) {
throw new Error("transient failure" );
}
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "followup" ,
debounceMs: 0 ,
cap: 1 ,
dropPolicy: "summarize" ,
};
enqueueFollowupRun(key, createRun({ prompt: "first" }), settings);
enqueueFollowupRun(key, createRun({ prompt: "second" }), settings);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.prompt).toContain("[Queue overflow] Dropped 1 message due to cap." );
expect(calls[0 ]?.prompt).toContain("- first" );
});
it("includes the overflow summary only in the first split auth group" , async () => {
const key = `test-collect-overflow-summary-once-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const expectedCalls = 2 ;
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
if (calls.length >= expectedCalls) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 2 ,
dropPolicy: "summarize" ,
};
const droppedGuest = createRun({
prompt: "dropped guest message" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
const guest = createRun({
prompt: "guest message" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
const owner = createRun({
prompt: "owner message" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
enqueueFollowupRun(
key,
{
...droppedGuest,
run: {
...droppedGuest.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderIsOwner: false ,
},
},
settings,
);
enqueueFollowupRun(
key,
{
...guest,
run: {
...guest.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderIsOwner: false ,
},
},
settings,
);
enqueueFollowupRun(
key,
{
...owner,
run: {
...owner.run,
senderId: "owner-1" ,
senderName: "Owner" ,
senderIsOwner: true ,
},
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls).toHaveLength(2 );
expect(calls[0 ]?.prompt).toContain("[Queue overflow] Dropped 1 message due to cap." );
expect(calls[0 ]?.prompt).toContain("- dropped guest message" );
expect(calls[1 ]?.prompt).not.toContain("[Queue overflow]" );
expect(calls[1 ]?.prompt).not.toContain("dropped guest message" );
});
it("does not re-deliver overflow summary on partial auth group failure retry" , async () => {
const key = `test-collect-overflow-partial-retry-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
let attempt = 0 ;
const runFollowup = async (run: FollowupRun) => {
attempt += 1 ;
// First group succeeds (attempt 1), second group fails (attempt 2),
// then second group succeeds on retry (attempt 3).
if (attempt === 2 ) {
throw new Error("transient failure" );
}
calls.push(run);
if (calls.length >= 2 ) {
done.resolve();
}
};
const settings: QueueSettings = {
mode: "collect" ,
debounceMs: 0 ,
cap: 2 ,
dropPolicy: "summarize" ,
};
const droppedGuest = createRun({
prompt: "dropped guest message" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
const guest = createRun({
prompt: "guest message" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
const owner = createRun({
prompt: "owner message" ,
originatingChannel: "slack" ,
originatingTo: "channel:A" ,
});
enqueueFollowupRun(
key,
{
...droppedGuest,
run: {
...droppedGuest.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderIsOwner: false ,
},
},
settings,
);
enqueueFollowupRun(
key,
{
...guest,
run: {
...guest.run,
senderId: "user-1" ,
senderName: "Guest" ,
senderIsOwner: false ,
},
},
settings,
);
enqueueFollowupRun(
key,
{
...owner,
run: {
...owner.run,
senderId: "owner-1" ,
senderName: "Owner" ,
senderIsOwner: true ,
},
},
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls).toHaveLength(2 );
// First group got the overflow summary
expect(calls[0 ]?.prompt).toContain("[Queue overflow] Dropped 1 message due to cap." );
expect(calls[0 ]?.prompt).toContain("- dropped guest message" );
// Second group (retried after failure) must NOT get the overflow summary again
expect(calls[1 ]?.prompt).not.toContain("[Queue overflow]" );
expect(calls[1 ]?.prompt).not.toContain("dropped guest message" );
expect(calls[1 ]?.prompt).toContain("owner message" );
});
it("preserves routing metadata on overflow summary followups" , async () => {
const key = `test-overflow-summary-routing-${Date.now()}`;
const calls: FollowupRun[] = [];
const done = createDeferred<void >();
const runFollowup = async (run: FollowupRun) => {
calls.push(run);
done.resolve();
};
const settings: QueueSettings = {
mode: "followup" ,
debounceMs: 0 ,
cap: 1 ,
dropPolicy: "summarize" ,
};
enqueueFollowupRun(
key,
createRun({
prompt: "first" ,
originatingChannel: "discord" ,
originatingTo: "channel:C1" ,
originatingAccountId: "work" ,
originatingThreadId: "1739142736.000100" ,
}),
settings,
);
enqueueFollowupRun(
key,
createRun({
prompt: "second" ,
originatingChannel: "discord" ,
originatingTo: "channel:C1" ,
originatingAccountId: "work" ,
originatingThreadId: "1739142736.000100" ,
}),
settings,
);
scheduleFollowupDrain(key, runFollowup);
await done.promise;
expect(calls[0 ]?.originatingChannel).toBe("discord" );
expect(calls[0 ]?.originatingTo).toBe("channel:C1" );
expect(calls[0 ]?.originatingAccountId).toBe("work" );
expect(calls[0 ]?.originatingThreadId).toBe("1739142736.000100" );
expect(calls[0 ]?.prompt).toContain("[Queue overflow] Dropped 1 message due to cap." );
});
});
describe("resolveFollowupAuthorizationKey" , () => {
it("changes when sender ownership changes" , () => {
const run = createRun({ prompt: "one" }).run;
expect(
resolveFollowupAuthorizationKey({
...run,
senderId: "user-1" ,
senderIsOwner: false ,
}),
).not.toBe(
resolveFollowupAuthorizationKey({
...run,
senderId: "user-1" ,
senderIsOwner: true ,
}),
);
});
it("changes when exec defaults change" , () => {
const run = createRun({ prompt: "one" }).run;
expect(
resolveFollowupAuthorizationKey({
...run,
senderId: "user-1" ,
bashElevated: { enabled: false , allowed: true , defaultLevel: "off" },
}),
).not.toBe(
resolveFollowupAuthorizationKey({
...run,
senderId: "user-1" ,
bashElevated: { enabled: true , allowed: true , defaultLevel: "on" },
execOverrides: { ask: "always" },
}),
);
});
it("does not change when only sender display fields change" , () => {
const run = createRun({ prompt: "one" }).run;
expect(
resolveFollowupAuthorizationKey({
...run,
senderId: "user-1" ,
senderName: "Guest" ,
senderUsername: "guest" ,
senderIsOwner: false ,
}),
).toBe(
resolveFollowupAuthorizationKey({
...run,
senderId: "user-1" ,
senderName: "Guest User" ,
senderUsername: "guest-renamed" ,
senderIsOwner: false ,
}),
);
});
});
Messung V0.5 in Prozent C=97 H=98 G=97
¤ Dauer der Verarbeitung: 0.9 Sekunden
¤
*© Formatika GbR, Deutschland