@@ -16,10 +16,12 @@ import type { SessionStreamTestServer } from "@internal/testcontainers/webapp";
1616import { startSessionStreamTestServer } from "@internal/testcontainers/webapp" ;
1717import { seedTestEnvironment } from "./helpers/seedTestEnvironment" ;
1818import {
19+ appendInput ,
1920 collectSessionOut ,
2021 collectUntilCaughtUp ,
2122 isTurnComplete ,
2223 mintSessionToken ,
24+ openChannelRaw ,
2325 SessionStreamProducer ,
2426 sessionStreamName ,
2527} from "./helpers/sessionStream" ;
@@ -37,8 +39,21 @@ afterAll(async () => {
3739} , 120_000 ) ;
3840
3941async function setupSession ( ) {
40- const { organization, environment, apiKey } = await seedTestEnvironment ( server . prisma ) ;
42+ const { organization, project , environment, apiKey } = await seedTestEnvironment ( server . prisma ) ;
4143 const addressingKey = `sess-${ randomBytes ( 6 ) . toString ( "hex" ) } ` ;
44+ await server . prisma . session . create ( {
45+ data : {
46+ friendlyId : `session_${ randomBytes ( 8 ) . toString ( "hex" ) } ` ,
47+ externalId : addressingKey ,
48+ type : "chat.agent" ,
49+ projectId : project . id ,
50+ runtimeEnvironmentId : environment . id ,
51+ environmentType : environment . type ,
52+ organizationId : organization . id ,
53+ taskIdentifier : "chat-agent" ,
54+ triggerConfig : { } ,
55+ } ,
56+ } ) ;
4257 const token = await mintSessionToken ( { apiKey, envId : environment . id , addressingKey } ) ;
4358 const streamName = sessionStreamName ( {
4459 orgId : organization . id ,
@@ -213,4 +228,81 @@ describe("session stream e2e", () => {
213228 expect ( dataChunks ) . toEqual ( [ 0 , 1 , 2 ] ) ;
214229 expect ( parts . some ( isTurnComplete ) ) . toBe ( true ) ;
215230 } ) ;
231+
232+ it ( "E8 in/append 413 for an oversized body still carries CORS headers" , async ( ) => {
233+ const { addressingKey, token, baseUrl } = await setupSession ( ) ;
234+
235+ const oversized = "x" . repeat ( 2 * 1024 * 1024 ) ;
236+ const { status, acao } = await appendInput ( {
237+ baseUrl,
238+ addressingKey,
239+ token,
240+ origin : "http://example.com" ,
241+ body : JSON . stringify ( { kind : "message" , payload : { big : oversized } } ) ,
242+ } ) ;
243+
244+ expect ( status ) . toBe ( 413 ) ;
245+ expect ( acao ) . not . toBeNull ( ) ;
246+ } ) ;
247+
248+ it ( "E9 server peek fast-closes at a turn-complete tail with X-Session-Settled" , async ( ) => {
249+ const { addressingKey, token, producer, baseUrl } = await setupSession ( ) ;
250+
251+ await producer . appendData ( { n : 0 } , "p0" ) ;
252+ const tc = await producer . appendTurnComplete ( ) ;
253+
254+ const { status, sessionSettled, closedMs } = await openChannelRaw ( {
255+ baseUrl,
256+ addressingKey,
257+ token,
258+ lastEventId : String ( tc ) ,
259+ peekSettled : true ,
260+ timeoutInSeconds : 30 ,
261+ maxMs : 10_000 ,
262+ } ) ;
263+
264+ expect ( status ) . toBe ( 200 ) ;
265+ expect ( sessionSettled ) . toBe ( "true" ) ;
266+ expect ( closedMs ) . toBeLessThan ( 5_000 ) ;
267+ } ) ;
268+
269+ it ( "E11 in/append delivers the record on the .in channel" , async ( ) => {
270+ const { addressingKey, token, baseUrl } = await setupSession ( ) ;
271+
272+ const payload = JSON . stringify ( { kind : "message" , text : "hello from client" } ) ;
273+ const appended = await appendInput ( {
274+ baseUrl,
275+ addressingKey,
276+ token,
277+ partId : "in-0" ,
278+ body : payload ,
279+ } ) ;
280+ expect ( appended . status ) . toBe ( 200 ) ;
281+
282+ const { parts } = await collectSessionOut ( {
283+ baseUrl,
284+ addressingKey,
285+ token,
286+ io : "in" ,
287+ until : ( p ) => p . some ( ( x ) => x . chunk != null ) ,
288+ maxMs : 15_000 ,
289+ } ) ;
290+
291+ const got = parts . find ( ( p ) => p . chunk != null ) ;
292+ expect ( got ) . toBeTruthy ( ) ;
293+ expect ( String ( got ?. chunk ) ) . toContain ( "hello from client" ) ;
294+ } ) ;
295+
296+ it ( "E12 subscribe with an invalid token is rejected" , async ( ) => {
297+ const { addressingKey, baseUrl } = await setupSession ( ) ;
298+
299+ const { status } = await openChannelRaw ( {
300+ baseUrl,
301+ addressingKey,
302+ token : "tr_pub_invalid_not_a_real_token" ,
303+ maxMs : 5_000 ,
304+ } ) ;
305+
306+ expect ( [ 401 , 403 ] ) . toContain ( status ) ;
307+ } ) ;
216308} ) ;
0 commit comments