t3-code-android-nightly/.repos/alchemy-effect/packages/alchemy/test/AWS/IVSChat/fixtures/handler.ts
Julius Marminge 6f9cea00ae
chore(refs): sync Effect and Alchemy references to 4.0.1 and beta.80 (#16170)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:22:30 -07:00

160 lines
5.7 KiB
TypeScript

import * as IVSChat from "@/AWS/IVSChat";
import * as Lambda from "@/AWS/Lambda";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Redacted from "effect/Redacted";
import { HttpServerRequest } from "effect/http/HttpServerRequest";
import * as HttpServerResponse from "effect/http/HttpServerResponse";
import path from "pathe";
const main = path.resolve(import.meta.dirname, "handler.ts");
/** The user ID the fixture mints tokens for and disconnects. */
export const TEST_USER_ID = "alchemy-test-user";
export class IVSChatTestFunction extends Lambda.Function<Lambda.Function>()(
"IVSChatTestFunction",
) {}
export default IVSChatTestFunction.make(
{
main,
functionUrl: true,
timeout: Duration.seconds(30),
},
Effect.gen(function* () {
const room = yield* IVSChat.Room("BindingsRoom", {
roomName: "alchemy-test-ivschat-bindings",
tags: { fixture: "ivschat-bindings" },
});
const createChatToken = yield* IVSChat.CreateChatToken(room);
const sendEvent = yield* IVSChat.SendEvent(room);
const deleteMessage = yield* IVSChat.DeleteMessage(room);
const disconnectUser = yield* IVSChat.DisconnectUser(room);
// Message review handler: deny messages containing "deny-me"; stamp
// everything else with a marker so the test can observe the review.
yield* IVSChat.onReviewMessage(room, (event) =>
Effect.succeed(
event.Content.includes("deny-me")
? {
ReviewResult: "DENY" as const,
Attributes: { Reason: "alchemy-moderated" },
}
: {
ReviewResult: "ALLOW" as const,
Content: `${event.Content} [reviewed]`,
},
),
);
const RoomArn = yield* room.roomArn;
return {
fetch: Effect.gen(function* () {
const request = yield* HttpServerRequest;
const pathname = new URL(request.originalUrl).pathname;
// Cheap readiness route — no AWS call.
if (request.method === "GET" && pathname === "/ping") {
return yield* HttpServerResponse.json({ ok: true });
}
if (request.method === "POST" && pathname === "/token") {
const result = yield* createChatToken({
userId: TEST_USER_ID,
capabilities: ["SEND_MESSAGE"],
// Exercises the Duration.Input -> sessionDurationInMinutes wiring.
sessionDuration: "30 minutes",
attributes: { displayName: "Alchemy" },
});
// The token is sensitive — assert on its shape, never echo it.
const token = result.token;
const tokenLength =
token === undefined
? 0
: Redacted.isRedacted(token)
? Redacted.value(token).length
: token.length;
return yield* HttpServerResponse.json({
tokenLength,
tokenIsRedacted: token !== undefined && Redacted.isRedacted(token),
tokenExpirationTime: result.tokenExpirationTime,
sessionExpirationTime: result.sessionExpirationTime,
});
}
if (request.method === "POST" && pathname === "/ws-info") {
// The test drives the room's WebSocket messaging API directly to
// exercise the message review handler; it needs the raw token
// (scoped to this test room, 30-minute session) and the regional
// edge endpoint.
const result = yield* createChatToken({
userId: TEST_USER_ID,
capabilities: ["SEND_MESSAGE"],
sessionDuration: "30 minutes",
});
const token = result.token;
const raw =
token === undefined
? undefined
: Redacted.isRedacted(token)
? Redacted.value(token)
: token;
const region = yield* Effect.sync(() => process.env.AWS_REGION);
const roomArn = yield* RoomArn;
return yield* HttpServerResponse.json({
token: raw,
endpoint: `wss://edge.ivschat.${region}.amazonaws.com`,
roomArn,
});
}
if (request.method === "POST" && pathname === "/send-event") {
const { id } = yield* sendEvent({
eventName: "app:announcement",
attributes: { note: "hello from alchemy" },
});
return yield* HttpServerResponse.json({ id });
}
if (request.method === "POST" && pathname === "/delete-message") {
// Broadcast an event first and use its ID as the deletion target —
// DeleteMessage broadcasts a DELETEMESSAGE event and succeeds
// whether or not clients hold the message.
const sent = yield* sendEvent({ eventName: "app:to-delete" });
const { id } = yield* deleteMessage({
id: sent.id!,
reason: "moderated by alchemy test",
});
return yield* HttpServerResponse.json({ deleted: id });
}
if (request.method === "POST" && pathname === "/disconnect-user") {
yield* disconnectUser({
userId: TEST_USER_ID,
reason: "alchemy test",
});
return yield* HttpServerResponse.json({ ok: true });
}
return yield* HttpServerResponse.json(
{ error: "Not found", method: request.method, pathname },
{ status: 404 },
);
}).pipe(Effect.orDie),
};
}).pipe(
Effect.provide(
Layer.mergeAll(
IVSChat.CreateChatTokenHttp,
IVSChat.SendEventHttp,
IVSChat.DeleteMessageHttp,
IVSChat.DisconnectUserHttp,
Lambda.RoomMessageReviewEventSource,
),
),
),
);