Skip to content

Commit 60f2a32

Browse files
Add custom mapper and trigger options transport test
Co-authored-by: Eric Allam <eric@trigger.dev>
1 parent babf007 commit 60f2a32

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

packages/ai/src/chatTransport.test.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,139 @@ describe("TriggerChatTransport", function () {
146146
expect(stream).toBeNull();
147147
});
148148

149+
it("supports custom payload mapping and trigger options resolver", async function () {
150+
let receivedTriggerBody: Record<string, unknown> | undefined;
151+
let receivedResolverChatId: string | undefined;
152+
let receivedResolverHeader: string | undefined;
153+
154+
const server = await startServer(function (req, res) {
155+
if (req.method === "POST" && req.url === "/api/v1/tasks/chat-task/trigger") {
156+
readJsonBody(req).then(function (body) {
157+
receivedTriggerBody = body;
158+
res.writeHead(200, {
159+
"content-type": "application/json",
160+
"x-trigger-jwt": "pk_run_789",
161+
});
162+
res.end(JSON.stringify({ id: "run_789" }));
163+
});
164+
return;
165+
}
166+
167+
if (req.method === "GET" && req.url === "/realtime/v1/streams/run_789/chat-stream") {
168+
res.writeHead(200, {
169+
"content-type": "text/event-stream",
170+
});
171+
writeSSE(
172+
res,
173+
"1-0",
174+
JSON.stringify({ type: "text-start", id: "mapped_1" })
175+
);
176+
writeSSE(
177+
res,
178+
"2-0",
179+
JSON.stringify({ type: "text-end", id: "mapped_1" })
180+
);
181+
res.end();
182+
return;
183+
}
184+
185+
res.writeHead(404);
186+
res.end();
187+
});
188+
189+
const transport = new TriggerChatTransport<
190+
UIMessage,
191+
{
192+
prompt: string;
193+
chatId: string;
194+
sourceHeader: string | undefined;
195+
}
196+
>({
197+
task: "chat-task",
198+
stream: "chat-stream",
199+
accessToken: "pk_trigger",
200+
baseURL: server.url,
201+
payloadMapper: function payloadMapper(request) {
202+
const firstMessage = request.messages[0];
203+
const firstPart = firstMessage?.parts[0];
204+
const prompt =
205+
firstPart && firstPart.type === "text"
206+
? firstPart.text
207+
: "";
208+
209+
return {
210+
prompt,
211+
chatId: request.chatId,
212+
sourceHeader: request.request.headers?.["x-source"],
213+
};
214+
},
215+
triggerOptions: function triggerOptions(request) {
216+
receivedResolverChatId = request.chatId;
217+
receivedResolverHeader = request.request.headers?.["x-source"];
218+
219+
return {
220+
queue: "chat-queue",
221+
concurrencyKey: `chat-${request.chatId}`,
222+
idempotencyKey: `idem-${request.chatId}`,
223+
ttl: "30m",
224+
tags: ["chat", "mapped"],
225+
metadata: {
226+
requester: request.request.headers?.["x-source"] ?? "unknown",
227+
},
228+
priority: 50,
229+
};
230+
},
231+
});
232+
233+
const stream = await transport.sendMessages({
234+
trigger: "submit-message",
235+
chatId: "chat-mapped",
236+
messageId: undefined,
237+
messages: [
238+
{
239+
id: "mapped-user",
240+
role: "user",
241+
parts: [{ type: "text", text: "Map me" }],
242+
} satisfies UIMessage,
243+
],
244+
abortSignal: undefined,
245+
headers: {
246+
"x-source": "sdk-test",
247+
},
248+
});
249+
250+
const chunks = await readChunks(stream);
251+
expect(chunks).toHaveLength(2);
252+
expect(chunks[0]).toMatchObject({
253+
chunk: { type: "text-start", id: "mapped_1" },
254+
});
255+
expect(chunks[1]).toMatchObject({
256+
chunk: { type: "text-end", id: "mapped_1" },
257+
});
258+
259+
expect(receivedResolverChatId).toBe("chat-mapped");
260+
expect(receivedResolverHeader).toBe("sdk-test");
261+
262+
expect(receivedTriggerBody).toBeDefined();
263+
const payloadString = receivedTriggerBody?.payload as string;
264+
const payload = (JSON.parse(payloadString) as { json: Record<string, unknown> }).json;
265+
expect(payload).toEqual({
266+
prompt: "Map me",
267+
chatId: "chat-mapped",
268+
sourceHeader: "sdk-test",
269+
});
270+
271+
const options = (receivedTriggerBody?.options ?? {}) as Record<string, unknown>;
272+
expect(options.queue).toEqual({ name: "chat-queue" });
273+
expect(options.concurrencyKey).toBe("chat-chat-mapped");
274+
expect(options.ttl).toBe("30m");
275+
expect(options.tags).toEqual(["chat", "mapped"]);
276+
expect(options.metadata).toEqual({ requester: "sdk-test" });
277+
expect(options.priority).toBe(50);
278+
expect(typeof options.idempotencyKey).toBe("string");
279+
expect((options.idempotencyKey as string).length).toBe(64);
280+
});
281+
149282
it("reconnects active streams using tracked lastEventId", async function () {
150283
let reconnectLastEventId: string | undefined;
151284
let firstStreamResponse: ServerResponse<IncomingMessage> | undefined;

0 commit comments

Comments
 (0)