rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
108 lines (107 loc) • 4.46 kB
JavaScript
import { describe, expect, it } from "vitest";
import { packMessage, unpackMessage } from "./protocol";
import { MESSAGE_TYPE } from "./shared";
describe("Realtime Protocol pack/unpack", () => {
const textEncoder = new TextEncoder();
// Helper to generate a UUID string of the correct length
const generateId = () => "a".repeat(36);
const testMessages = [
{
type: MESSAGE_TYPE.ACTION_REQUEST,
id: "my-action",
args: { foo: "bar" },
requestId: generateId(),
clientUrl: "http://localhost:3000/",
},
{
type: MESSAGE_TYPE.ACTION_START,
id: generateId(),
status: 200,
},
{
type: MESSAGE_TYPE.ACTION_CHUNK,
id: generateId(),
payload: textEncoder.encode("chunk data"),
},
{
type: MESSAGE_TYPE.ACTION_END,
id: generateId(),
},
{
type: MESSAGE_TYPE.ACTION_ERROR,
id: generateId(),
error: "Something went wrong",
},
{
type: MESSAGE_TYPE.RSC_START,
id: generateId(),
status: 200,
},
{
type: MESSAGE_TYPE.RSC_CHUNK,
id: generateId(),
payload: textEncoder.encode("rsc chunk"),
},
{
type: MESSAGE_TYPE.RSC_END,
id: generateId(),
},
];
testMessages.forEach((message) => {
it(`should correctly pack and unpack a ${Object.keys(MESSAGE_TYPE).find((key) => MESSAGE_TYPE[key] === message.type) || "UNKNOWN"} message`, () => {
const packed = packMessage(message);
const unpacked = unpackMessage(packed);
expect(unpacked).toEqual(message);
});
});
describe("Error Handling", () => {
it("should throw an error for an unknown message type on pack", () => {
const invalidMessage = { type: 999 };
expect(() => packMessage(invalidMessage)).toThrow("Unknown message type for packing");
});
it("should throw an error for an unknown message type on unpack", () => {
const invalidData = new Uint8Array([99, 1, 2, 3]);
expect(() => unpackMessage(invalidData)).toThrow("Unknown message type for unpacking: 99");
});
it("should throw an error for an empty message on unpack", () => {
const emptyData = new Uint8Array([]);
expect(() => unpackMessage(emptyData)).toThrow("Cannot unpack empty message");
});
const invalidLengthTests = [
{ type: MESSAGE_TYPE.ACTION_START, name: "START" },
{ type: MESSAGE_TYPE.RSC_START, name: "START" },
{ type: MESSAGE_TYPE.ACTION_CHUNK, name: "CHUNK" },
{ type: MESSAGE_TYPE.RSC_CHUNK, name: "CHUNK" },
{ type: MESSAGE_TYPE.ACTION_END, name: "END" },
{ type: MESSAGE_TYPE.RSC_END, name: "END" },
{ type: MESSAGE_TYPE.ACTION_ERROR, name: "ERROR" },
];
invalidLengthTests.forEach(({ type, name }) => {
it(`should throw for invalid ${name} message length on unpack`, () => {
const invalidData = new Uint8Array([type, 1, 2, 3]); // Too short
expect(() => unpackMessage(invalidData)).toThrow(`Invalid ${name} message length`);
});
});
const invalidIdTests = [
{ type: MESSAGE_TYPE.ACTION_START, name: "START" },
{ type: MESSAGE_TYPE.RSC_START, name: "START" },
{ type: MESSAGE_TYPE.ACTION_CHUNK, name: "CHUNK" },
{ type: MESSAGE_TYPE.RSC_CHUNK, name: "CHUNK" },
{ type: MESSAGE_TYPE.ACTION_END, name: "END" },
{ type: MESSAGE_TYPE.RSC_END, name: "END" },
{ type: MESSAGE_TYPE.ACTION_ERROR, name: "ERROR" },
];
invalidIdTests.forEach(({ type, name }) => {
it(`should throw for invalid ID length on ${name} message pack`, () => {
const message = {
type,
id: "short-id",
status: 200, // For START types
payload: new Uint8Array(), // For CHUNK types
error: "", // For ERROR type
};
expect(() => packMessage(message)).toThrow(`Invalid message ID length for ${name} message`);
});
});
});
});