{"version":3,"sources":["/Users/nathan/rivetkit/packages/rivetkit/dist/tsup/driver-test-suite/mod.cjs","../../../src/driver-test-suite/mod.ts","../../../src/driver-test-suite/log.ts","../../../src/driver-test-suite/tests/action-features.ts","../../../src/driver-test-suite/utils.ts","../../../src/driver-test-suite/test-inline-client-driver.ts","../../../src/driver-test-suite/tests/actor-conn.ts","../../../src/driver-test-suite/tests/actor-conn-state.ts","../../../src/driver-test-suite/tests/actor-driver.ts","../../../src/driver-test-suite/tests/actor-schedule.ts","../../../src/driver-test-suite/tests/actor-sleep.ts","../../../fixtures/driver-test-suite/sleep.ts","../../../src/driver-test-suite/tests/actor-state.ts","../../../src/driver-test-suite/tests/actor-error-handling.ts","../../../src/driver-test-suite/tests/actor-handle.ts","../../../src/driver-test-suite/tests/actor-inline-client.ts","../../../src/driver-test-suite/tests/actor-inspector.ts","../../../src/driver-test-suite/tests/actor-metadata.ts","../../../src/driver-test-suite/tests/actor-onstatechange.ts","../../../src/driver-test-suite/tests/actor-reconnect.ts","../../../src/driver-test-suite/tests/actor-vars.ts","../../../src/driver-test-suite/tests/manager-driver.ts","../../../src/driver-test-suite/tests/raw-http.ts","../../../src/driver-test-suite/tests/raw-http-request-properties.ts","../../../src/driver-test-suite/tests/raw-websocket.ts","../../../src/driver-test-suite/tests/request-access.ts"],"names":["WebSocket","expect","response","actor","_a","describe"],"mappings":"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AAA;AACU,MAAA;AACV,IAAA;AAAA;AAEA,IAAA;AAAuC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","file":"/Users/nathan/rivetkit/packages/rivetkit/dist/tsup/driver-test-suite/mod.cjs","sourcesContent":[null,"import { serve as honoServe } from \"@hono/node-server\";\nimport { createNodeWebSocket, type NodeWebSocket } from \"@hono/node-ws\";\nimport { bundleRequire } from \"bundle-require\";\nimport invariant from \"invariant\";\nimport { describe } from \"vitest\";\nimport type { Encoding, Transport } from \"@/client/mod\";\nimport { configureInspectorAccessToken } from \"@/inspector/utils\";\nimport { createManagerRouter } from \"@/manager/router\";\nimport {\n\tcreateClientWithDriver,\n\ttype DriverConfig,\n\ttype Registry,\n\ttype RunConfig,\n} from \"@/mod\";\nimport { RunnerConfigSchema } from \"@/registry/run-config\";\nimport { getPort } from \"@/test/mod\";\nimport { logger } from \"./log\";\nimport { runActionFeaturesTests } from \"./tests/action-features\";\nimport { runActorConnTests } from \"./tests/actor-conn\";\nimport { runActorConnStateTests } from \"./tests/actor-conn-state\";\nimport {\n\trunActorDriverTests,\n\trunActorDriverTestsWithTransport,\n} from \"./tests/actor-driver\";\nimport { runActorErrorHandlingTests } from \"./tests/actor-error-handling\";\nimport { runActorHandleTests } from \"./tests/actor-handle\";\nimport { runActorInlineClientTests } from \"./tests/actor-inline-client\";\nimport { runActorInspectorTests } from \"./tests/actor-inspector\";\nimport { runActorMetadataTests } from \"./tests/actor-metadata\";\nimport { runActorOnStateChangeTests } from \"./tests/actor-onstatechange\";\nimport { runActorReconnectTests } from \"./tests/actor-reconnect\";\nimport { runActorVarsTests } from \"./tests/actor-vars\";\nimport { runManagerDriverTests } from \"./tests/manager-driver\";\nimport { runRawHttpTests } from \"./tests/raw-http\";\nimport { runRawHttpRequestPropertiesTests } from \"./tests/raw-http-request-properties\";\nimport { runRawWebSocketTests } from \"./tests/raw-websocket\";\nimport { runRequestAccessTests } from \"./tests/request-access\";\n\nexport interface SkipTests {\n\tschedule?: boolean;\n\tsleep?: boolean;\n\tsse?: boolean;\n\tinline?: boolean;\n}\n\nexport interface DriverTestConfig {\n\t/** Deploys an registry and returns the connection endpoint. */\n\tstart(): Promise<DriverDeployOutput>;\n\n\t/**\n\t * If we're testing with an external system, we should use real timers\n\t * instead of Vitest's mocked timers.\n\t **/\n\tuseRealTimers?: boolean;\n\n\t/** Cloudflare Workers has some bugs with cleanup. */\n\tHACK_skipCleanupNet?: boolean;\n\n\tskip?: SkipTests;\n\n\ttransport?: Transport;\n\n\tencoding?: Encoding;\n\n\tclientType: ClientType;\n\n\tcleanup?: () => Promise<void>;\n}\n\n/**\n * The type of client to run the test with.\n *\n * The logic for HTTP vs inline is very different, so this helps validate all behavior matches.\n **/\ntype ClientType = \"http\" | \"inline\";\n\nexport interface DriverDeployOutput {\n\tendpoint: string;\n\tnamespace: string;\n\trunnerName: string;\n\n\t/** Cleans up the test. */\n\tcleanup(): Promise<void>;\n}\n\n/** Runs all Vitest tests against the provided drivers. */\nexport function runDriverTests(\n\tdriverTestConfigPartial: Omit<\n\t\tDriverTestConfig,\n\t\t\"clientType\" | \"transport\" | \"encoding\"\n\t>,\n) {\n\tconst clientTypes: ClientType[] = driverTestConfigPartial.skip?.inline\n\t\t? [\"http\"]\n\t\t: [\"http\", \"inline\"];\n\tfor (const clientType of clientTypes) {\n\t\tdescribe(`client type (${clientType})`, () => {\n\t\t\tconst encodings: Encoding[] = [\"bare\", \"cbor\", \"json\"];\n\n\t\t\tfor (const encoding of encodings) {\n\t\t\t\tdescribe(`encoding (${encoding})`, () => {\n\t\t\t\t\tconst driverTestConfig: DriverTestConfig = {\n\t\t\t\t\t\t...driverTestConfigPartial,\n\t\t\t\t\t\tclientType,\n\t\t\t\t\t\tencoding,\n\t\t\t\t\t};\n\n\t\t\t\t\trunActorDriverTests(driverTestConfig);\n\t\t\t\t\trunManagerDriverTests(driverTestConfig);\n\n\t\t\t\t\tconst transports: Transport[] = driverTestConfig.skip?.sse\n\t\t\t\t\t\t? [\"websocket\"]\n\t\t\t\t\t\t: [\"websocket\", \"sse\"];\n\t\t\t\t\tfor (const transport of transports) {\n\t\t\t\t\t\tdescribe(`transport (${transport})`, () => {\n\t\t\t\t\t\t\trunActorConnTests({\n\t\t\t\t\t\t\t\t...driverTestConfig,\n\t\t\t\t\t\t\t\ttransport,\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\trunActorConnStateTests({ ...driverTestConfig, transport });\n\n\t\t\t\t\t\t\trunActorReconnectTests({ ...driverTestConfig, transport });\n\n\t\t\t\t\t\t\trunRequestAccessTests({ ...driverTestConfig, transport });\n\n\t\t\t\t\t\t\trunActorDriverTestsWithTransport({\n\t\t\t\t\t\t\t\t...driverTestConfig,\n\t\t\t\t\t\t\t\ttransport,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\trunActorHandleTests(driverTestConfig);\n\n\t\t\t\t\trunActionFeaturesTests(driverTestConfig);\n\n\t\t\t\t\trunActorVarsTests(driverTestConfig);\n\n\t\t\t\t\trunActorMetadataTests(driverTestConfig);\n\n\t\t\t\t\trunActorOnStateChangeTests(driverTestConfig);\n\n\t\t\t\t\trunActorErrorHandlingTests(driverTestConfig);\n\n\t\t\t\t\trunActorInlineClientTests(driverTestConfig);\n\n\t\t\t\t\trunRawHttpTests(driverTestConfig);\n\n\t\t\t\t\trunRawHttpRequestPropertiesTests(driverTestConfig);\n\n\t\t\t\t\trunRawWebSocketTests(driverTestConfig);\n\n\t\t\t\t\t// TODO: re-expose this once we can have actor queries on the gateway\n\t\t\t\t\t// runRawHttpDirectRegistryTests(driverTestConfig);\n\n\t\t\t\t\t// TODO: re-expose this once we can have actor queries on the gateway\n\t\t\t\t\t// runRawWebSocketDirectRegistryTests(driverTestConfig);\n\n\t\t\t\t\trunActorInspectorTests(driverTestConfig);\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n}\n\n/**\n * Helper function to adapt the drivers to the Node.js runtime for tests.\n *\n * This is helpful for drivers that run in-process as opposed to drivers that rely on external tools.\n */\nexport async function createTestRuntime(\n\tregistryPath: string,\n\tdriverFactory: (registry: Registry<any>) => Promise<{\n\t\trivetEngine?: {\n\t\t\tendpoint: string;\n\t\t\tnamespace: string;\n\t\t\trunnerName: string;\n\t\t\ttoken: string;\n\t\t};\n\t\tdriver: DriverConfig;\n\t\tcleanup?: () => Promise<void>;\n\t}>,\n): Promise<DriverDeployOutput> {\n\tconst {\n\t\tmod: { registry },\n\t} = await bundleRequire<{ registry: Registry<any> }>({\n\t\tfilepath: registryPath,\n\t});\n\n\t// TODO: Find a cleaner way of flagging an registry as test mode (ideally not in the config itself)\n\t// Force enable test\n\tregistry.config.test.enabled = true;\n\n\t// Build drivers\n\tconst {\n\t\tdriver,\n\t\tcleanup: driverCleanup,\n\t\trivetEngine,\n\t} = await driverFactory(registry);\n\n\tif (rivetEngine) {\n\t\t// TODO: We don't need createTestRuntime fort his\n\t\t// Using external Rivet engine\n\n\t\tconst cleanup = async () => {\n\t\t\tawait driverCleanup?.();\n\t\t};\n\n\t\treturn {\n\t\t\tendpoint: rivetEngine.endpoint,\n\t\t\tnamespace: rivetEngine.namespace,\n\t\t\trunnerName: rivetEngine.runnerName,\n\t\t\tcleanup,\n\t\t};\n\t} else {\n\t\t// Start server for Rivet engine\n\n\t\t// Build driver config\n\t\t// biome-ignore lint/style/useConst: Assigned later\n\t\tlet upgradeWebSocket: any;\n\t\tconst config: RunConfig = RunnerConfigSchema.parse({\n\t\t\tdriver,\n\t\t\tgetUpgradeWebSocket: () => upgradeWebSocket!,\n\t\t\tinspector: {\n\t\t\t\tenabled: true,\n\t\t\t\ttoken: () => \"token\",\n\t\t\t},\n\t\t});\n\n\t\t// Create router\n\t\tconst managerDriver = driver.manager(registry.config, config);\n\t\tconst client = createClientWithDriver(managerDriver);\n\t\tconfigureInspectorAccessToken(config, managerDriver);\n\t\tconst { router } = createManagerRouter(\n\t\t\tregistry.config,\n\t\t\tconfig,\n\t\t\tmanagerDriver,\n\t\t\tdriver,\n\t\t\tclient,\n\t\t);\n\n\t\t// Inject WebSocket\n\t\tconst nodeWebSocket = createNodeWebSocket({ app: router });\n\t\tupgradeWebSocket = nodeWebSocket.upgradeWebSocket;\n\n\t\t// Start server\n\t\tconst port = await getPort();\n\t\tconst server = honoServe({\n\t\t\tfetch: router.fetch,\n\t\t\thostname: \"127.0.0.1\",\n\t\t\tport,\n\t\t});\n\t\tinvariant(\n\t\t\tnodeWebSocket.injectWebSocket !== undefined,\n\t\t\t\"should have injectWebSocket\",\n\t\t);\n\t\tnodeWebSocket.injectWebSocket(server);\n\t\tconst serverEndpoint = `http://127.0.0.1:${port}`;\n\n\t\tlogger().info({ msg: \"test serer listening\", port });\n\n\t\t// Cleanup\n\t\tconst cleanup = async () => {\n\t\t\t// Stop server\n\t\t\tawait new Promise((resolve) => server.close(() => resolve(undefined)));\n\n\t\t\t// Extra cleanup\n\t\t\tawait driverCleanup?.();\n\t\t};\n\n\t\treturn {\n\t\t\tendpoint: serverEndpoint,\n\t\t\tnamespace: \"default\",\n\t\t\trunnerName: \"rivetkit\",\n\t\t\tcleanup,\n\t\t};\n\t}\n}\n","import { getLogger } from \"@/common/log\";\n\nexport function logger() {\n\treturn getLogger(\"test-suite\");\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { ActorError } from \"@/client/errors\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActionFeaturesTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Action Features\", () => {\n\t\t// TODO: These do not work with fake timers\n\t\tdescribe(\"Action Timeouts\", () => {\n\t\t\tconst usesFakeTimers = !driverTestConfig.useRealTimers;\n\n\t\t\ttest(\"should timeout actions that exceed the configured timeout\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// The quick action should complete successfully\n\t\t\t\tconst quickResult = await client.shortTimeoutActor\n\t\t\t\t\t.getOrCreate()\n\t\t\t\t\t.quickAction();\n\t\t\t\texpect(quickResult).toBe(\"quick response\");\n\n\t\t\t\t// The slow action should throw a timeout error\n\t\t\t\tawait expect(\n\t\t\t\t\tclient.shortTimeoutActor.getOrCreate().slowAction(),\n\t\t\t\t).rejects.toThrow(\"Action timed out\");\n\t\t\t});\n\n\t\t\ttest(\"should respect the default timeout\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// This action should complete within the default timeout\n\t\t\t\tconst result = await client.defaultTimeoutActor\n\t\t\t\t\t.getOrCreate()\n\t\t\t\t\t.normalAction();\n\t\t\t\texpect(result).toBe(\"normal response\");\n\t\t\t});\n\n\t\t\ttest(\"non-promise action results should not be affected by timeout\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Synchronous action should not be affected by timeout\n\t\t\t\tconst result = await client.syncTimeoutActor.getOrCreate().syncAction();\n\t\t\t\texpect(result).toBe(\"sync response\");\n\t\t\t});\n\n\t\t\ttest(\"should allow configuring different timeouts for different actors\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// The short timeout actor should fail\n\t\t\t\tawait expect(\n\t\t\t\t\tclient.shortTimeoutActor.getOrCreate().slowAction(),\n\t\t\t\t).rejects.toThrow(\"Action timed out\");\n\n\t\t\t\t// The longer timeout actor should succeed\n\t\t\t\tconst result = await client.longTimeoutActor\n\t\t\t\t\t.getOrCreate()\n\t\t\t\t\t.delayedAction();\n\t\t\t\texpect(result).toBe(\"delayed response\");\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Action Sync & Async\", () => {\n\t\t\ttest(\"should support synchronous actions\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst instance = client.syncActionActor.getOrCreate();\n\n\t\t\t\t// Test increment action\n\t\t\t\tlet result = await instance.increment(5);\n\t\t\t\texpect(result).toBe(5);\n\n\t\t\t\tresult = await instance.increment(3);\n\t\t\t\texpect(result).toBe(8);\n\n\t\t\t\t// Test getInfo action\n\t\t\t\tconst info = await instance.getInfo();\n\t\t\t\texpect(info.currentValue).toBe(8);\n\t\t\t\texpect(typeof info.timestamp).toBe(\"number\");\n\n\t\t\t\t// Test reset action (void return)\n\t\t\t\tawait instance.reset();\n\t\t\t\tresult = await instance.increment(0);\n\t\t\t\texpect(result).toBe(0);\n\t\t\t});\n\n\t\t\ttest(\"should support asynchronous actions\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst instance = client.asyncActionActor.getOrCreate();\n\n\t\t\t\t// Test delayed increment\n\t\t\t\tconst result = await instance.delayedIncrement(5);\n\t\t\t\texpect(result).toBe(5);\n\n\t\t\t\t// Test fetch data\n\t\t\t\tconst data = await instance.fetchData(\"test-123\");\n\t\t\t\texpect(data.id).toBe(\"test-123\");\n\t\t\t\texpect(typeof data.timestamp).toBe(\"number\");\n\n\t\t\t\t// Test successful async operation\n\t\t\t\tconst success = await instance.asyncWithError(false);\n\t\t\t\texpect(success).toBe(\"Success\");\n\n\t\t\t\t// Test error in async operation\n\t\t\t\ttry {\n\t\t\t\t\tawait instance.asyncWithError(true);\n\t\t\t\t\texpect.fail(\"did not error\");\n\t\t\t\t} catch (error) {\n\t\t\t\t\texpect((error as ActorError).message).toBe(\"Intentional error\");\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttest(\"should handle promises returned from actions correctly\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst instance = client.promiseActor.getOrCreate();\n\n\t\t\t\t// Test resolved promise\n\t\t\t\tconst resolvedValue = await instance.resolvedPromise();\n\t\t\t\texpect(resolvedValue).toBe(\"resolved value\");\n\n\t\t\t\t// Test delayed promise\n\t\t\t\tconst delayedValue = await instance.delayedPromise();\n\t\t\t\texpect(delayedValue).toBe(\"delayed value\");\n\n\t\t\t\t// Test rejected promise\n\t\t\t\tawait expect(instance.rejectedPromise()).rejects.toThrow(\n\t\t\t\t\t\"promised rejection\",\n\t\t\t\t);\n\n\t\t\t\t// Check state was updated by the delayed promise\n\t\t\t\tconst results = await instance.getResults();\n\t\t\t\texpect(results).toContain(\"delayed\");\n\t\t\t});\n\t\t});\n\t});\n}\n","import invariant from \"invariant\";\nimport { type TestContext, vi } from \"vitest\";\nimport { assertUnreachable } from \"@/actor/utils\";\nimport { type Client, createClient } from \"@/client/mod\";\nimport { RunConfigSchema } from \"@/driver-helpers/mod\";\nimport { createClientWithDriver } from \"@/mod\";\nimport type { registry } from \"../../fixtures/driver-test-suite/registry\";\nimport type { DriverTestConfig } from \"./mod\";\nimport { createTestInlineClientDriver } from \"./test-inline-client-driver\";\n\nexport const FAKE_TIME = new Date(\"2024-01-01T00:00:00.000Z\");\n\n// Must use `TestContext` since global hooks do not work when running concurrently\nexport async function setupDriverTest(\n\tc: TestContext,\n\tdriverTestConfig: DriverTestConfig,\n): Promise<{\n\tclient: Client<typeof registry>;\n\tendpoint: string;\n}> {\n\tif (!driverTestConfig.useRealTimers) {\n\t\tvi.useFakeTimers();\n\t\tvi.setSystemTime(FAKE_TIME);\n\t}\n\n\t// Build drivers\n\tconst { endpoint, namespace, runnerName, cleanup } =\n\t\tawait driverTestConfig.start();\n\tc.onTestFinished(cleanup);\n\n\tlet client: Client<typeof registry>;\n\tif (driverTestConfig.clientType === \"http\") {\n\t\t// Create client\n\t\tclient = createClient<typeof registry>({\n\t\t\tendpoint,\n\t\t\tnamespace,\n\t\t\trunnerName,\n\t\t\ttransport: driverTestConfig.transport,\n\t\t\tencoding: driverTestConfig.encoding,\n\t\t});\n\t} else if (driverTestConfig.clientType === \"inline\") {\n\t\t// Use inline client from driver\n\t\tconst transport = driverTestConfig.transport ?? \"websocket\";\n\t\tconst encoding = driverTestConfig.encoding ?? \"bare\";\n\t\tconst managerDriver = createTestInlineClientDriver(\n\t\t\tendpoint,\n\t\t\tencoding,\n\t\t\ttransport,\n\t\t);\n\t\tconst runConfig = RunConfigSchema.parse({\n\t\t\ttransport: transport,\n\t\t\tencoding: encoding,\n\t\t});\n\t\tclient = createClientWithDriver(managerDriver, runConfig);\n\t} else {\n\t\tassertUnreachable(driverTestConfig.clientType);\n\t}\n\n\t// Cleanup client\n\tif (!driverTestConfig.HACK_skipCleanupNet) {\n\t\tc.onTestFinished(async () => await client.dispose());\n\t}\n\n\treturn {\n\t\tclient,\n\t\tendpoint,\n\t};\n}\n\nexport async function waitFor(\n\tdriverTestConfig: DriverTestConfig,\n\tms: number,\n): Promise<void> {\n\tif (driverTestConfig.useRealTimers) {\n\t\treturn new Promise((resolve) => setTimeout(resolve, ms));\n\t} else {\n\t\tvi.advanceTimersByTime(ms);\n\t\treturn Promise.resolve();\n\t}\n}\n","import * as cbor from \"cbor-x\";\nimport type { Context as HonoContext } from \"hono\";\nimport invariant from \"invariant\";\nimport type { WebSocket } from \"ws\";\nimport type { Encoding } from \"@/actor/protocol/serde\";\nimport { assertUnreachable } from \"@/actor/utils\";\nimport { ActorError as ClientActorError } from \"@/client/errors\";\nimport type { Transport } from \"@/client/mod\";\nimport {\n\tHEADER_ACTOR_QUERY,\n\tHEADER_CONN_PARAMS,\n\tHEADER_ENCODING,\n\tWS_PROTOCOL_ACTOR,\n\tWS_PROTOCOL_CONN_PARAMS,\n\tWS_PROTOCOL_ENCODING,\n\tWS_PROTOCOL_PATH,\n\tWS_PROTOCOL_TARGET,\n\tWS_PROTOCOL_TRANSPORT,\n} from \"@/common/actor-router-consts\";\nimport type { UniversalEventSource } from \"@/common/eventsource-interface\";\nimport type { DeconstructedError } from \"@/common/utils\";\nimport { importWebSocket } from \"@/common/websocket\";\nimport {\n\ttype ActorOutput,\n\ttype CreateInput,\n\ttype GetForIdInput,\n\ttype GetOrCreateWithKeyInput,\n\ttype GetWithKeyInput,\n\tHEADER_ACTOR_ID,\n\ttype ManagerDisplayInformation,\n\ttype ManagerDriver,\n} from \"@/driver-helpers/mod\";\nimport type { ActorQuery } from \"@/manager/protocol/query\";\nimport type { UniversalWebSocket } from \"@/mod\";\nimport type * as protocol from \"@/schemas/client-protocol/mod\";\nimport { logger } from \"./log\";\n\nexport interface TestInlineDriverCallRequest {\n\tencoding: Encoding;\n\ttransport: Transport;\n\tmethod: string;\n\targs: unknown[];\n}\n\nexport type TestInlineDriverCallResponse<T> =\n\t| {\n\t\t\tok: T;\n\t  }\n\t| {\n\t\t\terr: DeconstructedError;\n\t  };\n\n/**\n * Creates a client driver used for testing the inline client driver. This will send a request to the HTTP server which will then internally call the internal client and return the response.\n */\nexport function createTestInlineClientDriver(\n\tendpoint: string,\n\tencoding: Encoding,\n\ttransport: Transport,\n): ManagerDriver {\n\treturn {\n\t\tgetForId(input: GetForIdInput): Promise<ActorOutput | undefined> {\n\t\t\treturn makeInlineRequest(endpoint, encoding, transport, \"getForId\", [\n\t\t\t\tinput,\n\t\t\t]);\n\t\t},\n\t\tgetWithKey(input: GetWithKeyInput): Promise<ActorOutput | undefined> {\n\t\t\treturn makeInlineRequest(endpoint, encoding, transport, \"getWithKey\", [\n\t\t\t\tinput,\n\t\t\t]);\n\t\t},\n\t\tgetOrCreateWithKey(input: GetOrCreateWithKeyInput): Promise<ActorOutput> {\n\t\t\treturn makeInlineRequest(\n\t\t\t\tendpoint,\n\t\t\t\tencoding,\n\t\t\t\ttransport,\n\t\t\t\t\"getOrCreateWithKey\",\n\t\t\t\t[input],\n\t\t\t);\n\t\t},\n\t\tcreateActor(input: CreateInput): Promise<ActorOutput> {\n\t\t\treturn makeInlineRequest(endpoint, encoding, transport, \"createActor\", [\n\t\t\t\tinput,\n\t\t\t]);\n\t\t},\n\t\tasync sendRequest(\n\t\t\tactorId: string,\n\t\t\tactorRequest: Request,\n\t\t): Promise<Response> {\n\t\t\t// Normalize path to match other drivers\n\t\t\tconst oldUrl = new URL(actorRequest.url);\n\t\t\tconst normalizedPath = oldUrl.pathname.startsWith(\"/\")\n\t\t\t\t? oldUrl.pathname.slice(1)\n\t\t\t\t: oldUrl.pathname;\n\t\t\tconst pathWithQuery = normalizedPath + oldUrl.search;\n\n\t\t\tlogger().debug({\n\t\t\t\tmsg: \"sending raw http request via test inline driver\",\n\t\t\t\tactorId,\n\t\t\t\tencoding,\n\t\t\t\tpath: pathWithQuery,\n\t\t\t});\n\n\t\t\t// Use the dedicated raw HTTP endpoint\n\t\t\tconst url = `${endpoint}/.test/inline-driver/send-request/${pathWithQuery}`;\n\n\t\t\tlogger().debug({ msg: \"rewriting http url\", from: oldUrl, to: url });\n\n\t\t\t// Merge headers with our metadata\n\t\t\tconst headers = new Headers(actorRequest.headers);\n\t\t\theaders.set(HEADER_ACTOR_ID, actorId);\n\n\t\t\t// Forward the request directly\n\t\t\tconst response = await fetch(\n\t\t\t\tnew Request(url, {\n\t\t\t\t\tmethod: actorRequest.method,\n\t\t\t\t\theaders,\n\t\t\t\t\tbody: actorRequest.body,\n\t\t\t\t\tsignal: actorRequest.signal,\n\t\t\t\t\tduplex: \"half\",\n\t\t\t\t} as RequestInit),\n\t\t\t);\n\n\t\t\t// Check if it's an error response from our handler\n\t\t\tif (\n\t\t\t\t!response.ok &&\n\t\t\t\tresponse.headers.get(\"content-type\")?.includes(\"application/json\")\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\t// Clone the response to avoid consuming the body\n\t\t\t\t\tconst clonedResponse = response.clone();\n\t\t\t\t\tconst errorData = (await clonedResponse.json()) as any;\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\t// Handle both error formats:\n\t\t\t\t\t\t// 1. { error: { code, message, metadata } } - structured format\n\t\t\t\t\t\t// 2. { error: \"message\" } - simple string format (from custom onFetch handlers)\n\t\t\t\t\t\tif (typeof errorData.error === \"object\") {\n\t\t\t\t\t\t\tthrow new ClientActorError(\n\t\t\t\t\t\t\t\terrorData.error.code,\n\t\t\t\t\t\t\t\terrorData.error.message,\n\t\t\t\t\t\t\t\terrorData.error.metadata,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// For simple string errors, just return the response as-is\n\t\t\t\t\t\t// This allows custom onFetch handlers to return their own error formats\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// If it's not our error format, just return the response as-is\n\t\t\t\t\tif (!(e instanceof ClientActorError)) {\n\t\t\t\t\t\treturn response;\n\t\t\t\t\t}\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn response;\n\t\t},\n\t\tasync openWebSocket(\n\t\t\tpath: string,\n\t\t\tactorId: string,\n\t\t\tencoding: Encoding,\n\t\t\tparams: unknown,\n\t\t\tconnId?: string,\n\t\t\tconnToken?: string,\n\t\t): Promise<UniversalWebSocket> {\n\t\t\tconst WebSocket = await importWebSocket();\n\n\t\t\t// Normalize path to match other drivers\n\t\t\tconst normalizedPath = path.startsWith(\"/\") ? path.slice(1) : path;\n\n\t\t\t// Create WebSocket connection to the test endpoint\n\t\t\tconst wsUrl = new URL(\n\t\t\t\t`${endpoint}/.test/inline-driver/connect-websocket/ws`,\n\t\t\t);\n\n\t\t\tlogger().debug({\n\t\t\t\tmsg: \"creating websocket connection via test inline driver\",\n\t\t\t\turl: wsUrl.toString(),\n\t\t\t});\n\n\t\t\t// Convert http/https to ws/wss\n\t\t\tconst wsProtocol = wsUrl.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\t\t\tconst finalWsUrl = `${wsProtocol}//${wsUrl.host}${wsUrl.pathname}`;\n\n\t\t\tlogger().debug({ msg: \"connecting to websocket\", url: finalWsUrl });\n\n\t\t\t// Build protocols for the connection\n\t\t\tconst protocols: string[] = [];\n\t\t\tprotocols.push(`${WS_PROTOCOL_TARGET}actor`);\n\t\t\tprotocols.push(`${WS_PROTOCOL_ACTOR}${actorId}`);\n\t\t\tprotocols.push(`${WS_PROTOCOL_ENCODING}${encoding}`);\n\t\t\tprotocols.push(`${WS_PROTOCOL_TRANSPORT}${transport}`);\n\t\t\tprotocols.push(\n\t\t\t\t`${WS_PROTOCOL_PATH}${encodeURIComponent(normalizedPath)}`,\n\t\t\t);\n\t\t\tif (params !== undefined) {\n\t\t\t\tprotocols.push(\n\t\t\t\t\t`${WS_PROTOCOL_CONN_PARAMS}${encodeURIComponent(JSON.stringify(params))}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Create and return the WebSocket\n\t\t\t// Node & browser WebSocket types are incompatible\n\t\t\tconst ws = new WebSocket(finalWsUrl, protocols) as any;\n\n\t\t\treturn ws;\n\t\t},\n\t\tasync proxyRequest(\n\t\t\tc: HonoContext,\n\t\t\tactorRequest: Request,\n\t\t\tactorId: string,\n\t\t): Promise<Response> {\n\t\t\treturn await this.sendRequest(actorId, actorRequest);\n\t\t},\n\t\tproxyWebSocket(\n\t\t\t_c: HonoContext,\n\t\t\t_path: string,\n\t\t\t_actorId: string,\n\t\t\t_encoding: Encoding,\n\t\t\t_params: unknown,\n\t\t): Promise<Response> {\n\t\t\tthrow \"UNIMPLEMENTED\";\n\t\t\t// const upgradeWebSocket = this.#runConfig.getUpgradeWebSocket?.();\n\t\t\t// invariant(upgradeWebSocket, \"missing getUpgradeWebSocket\");\n\t\t\t//\n\t\t\t// const wsHandler = this.openWebSocket(path, actorId, encoding, connParams);\n\t\t\t// return upgradeWebSocket(() => wsHandler)(c, noopNext());\n\t\t},\n\t\tdisplayInformation(): ManagerDisplayInformation {\n\t\t\treturn { name: \"Test Inline\", properties: {} };\n\t\t},\n\t\t// TODO:\n\t\tgetOrCreateInspectorAccessToken: () => \"\",\n\n\t\t// action: async <Args extends Array<unknown> = unknown[], Response = unknown>(\n\t\t// \t_c: HonoContext | undefined,\n\t\t// \tactorQuery: ActorQuery,\n\t\t// \tencoding: Encoding,\n\t\t// \tparams: unknown,\n\t\t// \tname: string,\n\t\t// \targs: Args,\n\t\t// ): Promise<Response> => {\n\t\t// \treturn makeInlineRequest<Response>(\n\t\t// \t\tendpoint,\n\t\t// \t\tencoding,\n\t\t// \t\ttransport,\n\t\t// \t\t\"action\",\n\t\t// \t\t[undefined, actorQuery, encoding, params, name, args],\n\t\t// \t);\n\t\t// },\n\t\t//\n\t\t// resolveActorId: async (\n\t\t// \t_c: HonoContext | undefined,\n\t\t// \tactorQuery: ActorQuery,\n\t\t// \tencodingKind: Encoding,\n\t\t// \tparams: unknown,\n\t\t// ): Promise<string> => {\n\t\t// \treturn makeInlineRequest<string>(\n\t\t// \t\tendpoint,\n\t\t// \t\tencodingKind,\n\t\t// \t\ttransport,\n\t\t// \t\t\"resolveActorId\",\n\t\t// \t\t[undefined, actorQuery, encodingKind, params],\n\t\t// \t);\n\t\t// },\n\t\t//\n\t\t// connectWebSocket: async (\n\t\t// \t_c: HonoContext | undefined,\n\t\t// \tactorQuery: ActorQuery,\n\t\t// \tencodingKind: Encoding,\n\t\t// \tparams: unknown,\n\t\t// ): Promise<WebSocket> => {\n\t\t// \tconst WebSocket = await importWebSocket();\n\t\t//\n\t\t// \tlogger().debug({\n\t\t// \t\tmsg: \"creating websocket connection via test inline driver\",\n\t\t// \t\tactorQuery,\n\t\t// \t\tencodingKind,\n\t\t// \t});\n\t\t//\n\t\t// \t// Create WebSocket connection to the test endpoint\n\t\t// \tconst wsUrl = new URL(\n\t\t// \t\t`${endpoint}/registry/.test/inline-driver/connect-websocket`,\n\t\t// \t);\n\t\t// \twsUrl.searchParams.set(\"actorQuery\", JSON.stringify(actorQuery));\n\t\t// \tif (params !== undefined)\n\t\t// \t\twsUrl.searchParams.set(\"params\", JSON.stringify(params));\n\t\t// \twsUrl.searchParams.set(\"encodingKind\", encodingKind);\n\t\t//\n\t\t// \t// Convert http/https to ws/wss\n\t\t// \tconst wsProtocol = wsUrl.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\t\t// \tconst finalWsUrl = `${wsProtocol}//${wsUrl.host}${wsUrl.pathname}${wsUrl.search}`;\n\t\t//\n\t\t// \tlogger().debug({ msg: \"connecting to websocket\", url: finalWsUrl });\n\t\t//\n\t\t// \t// Create and return the WebSocket\n\t\t// \t// Node & browser WebSocket types are incompatible\n\t\t// \tconst ws = new WebSocket(finalWsUrl, [\n\t\t// \t\t// HACK: See packages/drivers/cloudflare-workers/src/websocket.ts\n\t\t// \t\t\"rivetkit\",\n\t\t// \t]) as any;\n\t\t//\n\t\t// \treturn ws;\n\t\t// },\n\t\t//\n\t\t// connectSse: async (\n\t\t// \t_c: HonoContext | undefined,\n\t\t// \tactorQuery: ActorQuery,\n\t\t// \tencodingKind: Encoding,\n\t\t// \tparams: unknown,\n\t\t// ): Promise<UniversalEventSource> => {\n\t\t// \tlogger().debug({\n\t\t// \t\tmsg: \"creating sse connection via test inline driver\",\n\t\t// \t\tactorQuery,\n\t\t// \t\tencodingKind,\n\t\t// \t\tparams,\n\t\t// \t});\n\t\t//\n\t\t// \t// Dynamically import EventSource if needed\n\t\t// \tconst EventSourceImport = await import(\"eventsource\");\n\t\t// \t// Handle both ES modules (default) and CommonJS export patterns\n\t\t// \tconst EventSourceConstructor =\n\t\t// \t\t(EventSourceImport as any).default || EventSourceImport;\n\t\t//\n\t\t// \t// Encode parameters for the URL\n\t\t// \tconst actorQueryParam = encodeURIComponent(JSON.stringify(actorQuery));\n\t\t// \tconst encodingParam = encodeURIComponent(encodingKind);\n\t\t// \tconst paramsParam = params\n\t\t// \t\t? encodeURIComponent(JSON.stringify(params))\n\t\t// \t\t: null;\n\t\t//\n\t\t// \t// Create SSE connection URL\n\t\t// \tconst sseUrl = new URL(\n\t\t// \t\t`${endpoint}/registry/.test/inline-driver/connect-sse`,\n\t\t// \t);\n\t\t// \tsseUrl.searchParams.set(\"actorQueryRaw\", actorQueryParam);\n\t\t// \tsseUrl.searchParams.set(\"encodingKind\", encodingParam);\n\t\t// \tif (paramsParam) {\n\t\t// \t\tsseUrl.searchParams.set(\"params\", paramsParam);\n\t\t// \t}\n\t\t//\n\t\t// \tlogger().debug({ msg: \"connecting to sse\", url: sseUrl.toString() });\n\t\t//\n\t\t// \t// Create and return the EventSource\n\t\t// \tconst eventSource = new EventSourceConstructor(sseUrl.toString());\n\t\t//\n\t\t// \t// Wait for the connection to be established before returning\n\t\t// \tawait new Promise<void>((resolve, reject) => {\n\t\t// \t\teventSource.onopen = () => {\n\t\t// \t\t\tlogger().debug({ msg: \"sse connection established\" });\n\t\t// \t\t\tresolve();\n\t\t// \t\t};\n\t\t//\n\t\t// \t\teventSource.onerror = (event: Event) => {\n\t\t// \t\t\tlogger().error({ msg: \"sse connection failed\", event });\n\t\t// \t\t\treject(new Error(\"Failed to establish SSE connection\"));\n\t\t// \t\t};\n\t\t//\n\t\t// \t\t// Set a timeout in case the connection never establishes\n\t\t// \t\tsetTimeout(() => {\n\t\t// \t\t\tif (eventSource.readyState !== EventSourceConstructor.OPEN) {\n\t\t// \t\t\t\treject(new Error(\"SSE connection timed out\"));\n\t\t// \t\t\t}\n\t\t// \t\t}, 10000); // 10 second timeout\n\t\t// \t});\n\t\t//\n\t\t// \treturn eventSource as UniversalEventSource;\n\t\t// },\n\t\t//\n\t\t// sendHttpMessage: async (\n\t\t// \t_c: HonoContext | undefined,\n\t\t// \tactorId: string,\n\t\t// \tencoding: Encoding,\n\t\t// \tconnectionId: string,\n\t\t// \tconnectionToken: string,\n\t\t// \tmessage: protocol.ToServer,\n\t\t// ): Promise<void> => {\n\t\t// \tlogger().debug({\n\t\t// \t\tmsg: \"sending http message via test inline driver\",\n\t\t// \t\tactorId,\n\t\t// \t\tencoding,\n\t\t// \t\tconnectionId,\n\t\t// \t\ttransport,\n\t\t// \t});\n\t\t//\n\t\t// \tconst result = await fetch(\n\t\t// \t\t`${endpoint}/registry/.test/inline-driver/call`,\n\t\t// \t\t{\n\t\t// \t\t\tmethod: \"POST\",\n\t\t// \t\t\theaders: {\n\t\t// \t\t\t\t\"Content-Type\": \"application/json\",\n\t\t// \t\t\t},\n\t\t// \t\t\tbody: JSON.stringify({\n\t\t// \t\t\t\tencoding,\n\t\t// \t\t\t\ttransport,\n\t\t// \t\t\t\tmethod: \"sendHttpMessage\",\n\t\t// \t\t\t\targs: [\n\t\t// \t\t\t\t\tundefined,\n\t\t// \t\t\t\t\tactorId,\n\t\t// \t\t\t\t\tencoding,\n\t\t// \t\t\t\t\tconnectionId,\n\t\t// \t\t\t\t\tconnectionToken,\n\t\t// \t\t\t\t\tmessage,\n\t\t// \t\t\t\t],\n\t\t// \t\t\t} satisfies TestInlineDriverCallRequest),\n\t\t// \t\t},\n\t\t// \t);\n\t\t//\n\t\t// \tif (!result.ok) {\n\t\t// \t\tthrow new Error(`Failed to send HTTP message: ${result.statusText}`);\n\t\t// \t}\n\t\t//\n\t\t// \t// Discard response\n\t\t// \tawait result.body?.cancel();\n\t\t// },\n\t\t//\n\t\t// rawHttpRequest: async (\n\t\t// \t_c: HonoContext | undefined,\n\t\t// \tactorQuery: ActorQuery,\n\t\t// \tencoding: Encoding,\n\t\t// \tparams: unknown,\n\t\t// \tpath: string,\n\t\t// \tinit: RequestInit,\n\t\t// ): Promise<Response> => {\n\t\t// \t// Normalize path to match other drivers\n\t\t// \tconst normalizedPath = path.startsWith(\"/\") ? path.slice(1) : path;\n\t\t//\n\t\t// \tlogger().debug({\n\t\t// \t\tmsg: \"sending raw http request via test inline driver\",\n\t\t// \t\tactorQuery,\n\t\t// \t\tencoding,\n\t\t// \t\tpath: normalizedPath,\n\t\t// \t});\n\t\t//\n\t\t// \t// Use the dedicated raw HTTP endpoint\n\t\t// \tconst url = `${endpoint}/registry/.test/inline-driver/raw-http/${normalizedPath}`;\n\t\t//\n\t\t// \tlogger().debug({ msg: \"rewriting http url\", from: path, to: url });\n\t\t//\n\t\t// \t// Merge headers with our metadata\n\t\t// \tconst headers = new Headers(init.headers);\n\t\t// \theaders.set(HEADER_ACTOR_QUERY, JSON.stringify(actorQuery));\n\t\t// \theaders.set(HEADER_ENCODING, encoding);\n\t\t// \tif (params !== undefined) {\n\t\t// \t\theaders.set(HEADER_CONN_PARAMS, JSON.stringify(params));\n\t\t// \t}\n\t\t//\n\t\t// \t// Forward the request directly\n\t\t// \tconst response = await fetch(url, {\n\t\t// \t\t...init,\n\t\t// \t\theaders,\n\t\t// \t});\n\t\t//\n\t\t// \t// Check if it's an error response from our handler\n\t\t// \tif (\n\t\t// \t\t!response.ok &&\n\t\t// \t\tresponse.headers.get(\"content-type\")?.includes(\"application/json\")\n\t\t// \t) {\n\t\t// \t\ttry {\n\t\t// \t\t\t// Clone the response to avoid consuming the body\n\t\t// \t\t\tconst clonedResponse = response.clone();\n\t\t// \t\t\tconst errorData = (await clonedResponse.json()) as any;\n\t\t// \t\t\tif (errorData.error) {\n\t\t// \t\t\t\t// Handle both error formats:\n\t\t// \t\t\t\t// 1. { error: { code, message, metadata } } - structured format\n\t\t// \t\t\t\t// 2. { error: \"message\" } - simple string format (from custom onFetch handlers)\n\t\t// \t\t\t\tif (typeof errorData.error === \"object\") {\n\t\t// \t\t\t\t\tthrow new ClientActorError(\n\t\t// \t\t\t\t\t\terrorData.error.code,\n\t\t// \t\t\t\t\t\terrorData.error.message,\n\t\t// \t\t\t\t\t\terrorData.error.metadata,\n\t\t// \t\t\t\t\t);\n\t\t// \t\t\t\t}\n\t\t// \t\t\t\t// For simple string errors, just return the response as-is\n\t\t// \t\t\t\t// This allows custom onFetch handlers to return their own error formats\n\t\t// \t\t\t}\n\t\t// \t\t} catch (e) {\n\t\t// \t\t\t// If it's not our error format, just return the response as-is\n\t\t// \t\t\tif (!(e instanceof ClientActorError)) {\n\t\t// \t\t\t\treturn response;\n\t\t// \t\t\t}\n\t\t// \t\t\tthrow e;\n\t\t// \t\t}\n\t\t// \t}\n\t\t//\n\t\t// \treturn response;\n\t\t// },\n\t\t//\n\t\t// rawWebSocket: async (\n\t\t// \t_c: HonoContext | undefined,\n\t\t// \tactorQuery: ActorQuery,\n\t\t// \tencoding: Encoding,\n\t\t// \tparams: unknown,\n\t\t// \tpath: string,\n\t\t// \tprotocols: string | string[] | undefined,\n\t\t// ): Promise<WebSocket> => {\n\t\t// \tlogger().debug({ msg: \"test inline driver rawWebSocket called\" });\n\t\t// \tconst WebSocket = await importWebSocket();\n\t\t//\n\t\t// \t// Normalize path to match other drivers\n\t\t// \tconst normalizedPath = path.startsWith(\"/\") ? path.slice(1) : path;\n\t\t//\n\t\t// \tlogger().debug({\n\t\t// \t\tmsg: \"creating raw websocket connection via test inline driver\",\n\t\t// \t\tactorQuery,\n\t\t// \t\tencoding,\n\t\t// \t\tpath: normalizedPath,\n\t\t// \t\tprotocols,\n\t\t// \t});\n\t\t//\n\t\t// \t// Create WebSocket connection to the test endpoint\n\t\t// \tconst wsUrl = new URL(\n\t\t// \t\t`${endpoint}/registry/.test/inline-driver/raw-websocket`,\n\t\t// \t);\n\t\t// \twsUrl.searchParams.set(\"actorQuery\", JSON.stringify(actorQuery));\n\t\t// \tif (params !== undefined)\n\t\t// \t\twsUrl.searchParams.set(\"params\", JSON.stringify(params));\n\t\t// \twsUrl.searchParams.set(\"encodingKind\", encoding);\n\t\t// \twsUrl.searchParams.set(\"path\", normalizedPath);\n\t\t// \tif (protocols !== undefined)\n\t\t// \t\twsUrl.searchParams.set(\"protocols\", JSON.stringify(protocols));\n\t\t//\n\t\t// \t// Convert http/https to ws/wss\n\t\t// \tconst wsProtocol = wsUrl.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\t\t// \tconst finalWsUrl = `${wsProtocol}//${wsUrl.host}${wsUrl.pathname}${wsUrl.search}`;\n\t\t//\n\t\t// \tlogger().debug({ msg: \"connecting to raw websocket\", url: finalWsUrl });\n\t\t//\n\t\t// \tlogger().debug({\n\t\t// \t\tmsg: \"rewriting websocket url\",\n\t\t// \t\tfrom: path,\n\t\t// \t\tto: finalWsUrl,\n\t\t// \t});\n\t\t//\n\t\t// \t// Create and return the WebSocket\n\t\t// \t// Node & browser WebSocket types are incompatible\n\t\t// \tconst ws = new WebSocket(finalWsUrl, [\n\t\t// \t\t// HACK: See packages/drivers/cloudflare-workers/src/websocket.ts\n\t\t// \t\t\"rivetkit\",\n\t\t// \t]) as any;\n\t\t//\n\t\t// \tlogger().debug({\n\t\t// \t\tmsg: \"test inline driver created websocket\",\n\t\t// \t\treadyState: ws.readyState,\n\t\t// \t\turl: ws.url,\n\t\t// \t});\n\t\t//\n\t\t// \treturn ws;\n\t\t// },\n\t} satisfies ManagerDriver;\n}\n\nasync function makeInlineRequest<T>(\n\tendpoint: string,\n\tencoding: Encoding,\n\ttransport: Transport,\n\tmethod: string,\n\targs: unknown[],\n): Promise<T> {\n\tlogger().debug({\n\t\tmsg: \"sending inline request\",\n\t\tencoding,\n\t\ttransport,\n\t\tmethod,\n\t\targs,\n\t});\n\n\t// Call driver\n\tconst response = await fetch(`${endpoint}/.test/inline-driver/call`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: cbor.encode({\n\t\t\tencoding,\n\t\t\ttransport,\n\t\t\tmethod,\n\t\t\targs,\n\t\t} satisfies TestInlineDriverCallRequest),\n\t\tduplex: \"half\",\n\t} as RequestInit);\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to call inline ${method}: ${response.statusText}`);\n\t}\n\n\t// Parse response\n\tconst buffer = await response.arrayBuffer();\n\tconst callResponse: TestInlineDriverCallResponse<T> = cbor.decode(\n\t\tnew Uint8Array(buffer),\n\t);\n\n\t// Throw or OK\n\tif (\"ok\" in callResponse) {\n\t\treturn callResponse.ok;\n\t} else if (\"err\" in callResponse) {\n\t\tthrow new ClientActorError(\n\t\t\tcallResponse.err.group,\n\t\t\tcallResponse.err.code,\n\t\t\tcallResponse.err.message,\n\t\t\tcallResponse.err.metadata,\n\t\t);\n\t} else {\n\t\tassertUnreachable(callResponse);\n\t}\n}\n","import { describe, expect, test, vi } from \"vitest\";\nimport { SSE_PING_INTERVAL } from \"@/actor/router-endpoints\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { FAKE_TIME, setupDriverTest, waitFor } from \"../utils\";\n\nexport function runActorConnTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Connection Tests\", () => {\n\t\tdescribe(\"Connection Methods\", () => {\n\t\t\ttest(\"should connect using .get().connect()\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor\n\t\t\t\tawait client.counter.create([\"test-get\"]);\n\n\t\t\t\t// Get a handle and connect\n\t\t\t\tconst handle = client.counter.get([\"test-get\"]);\n\t\t\t\tconst connection = handle.connect();\n\n\t\t\t\t// Verify connection by performing an action\n\t\t\t\tconst count = await connection.increment(5);\n\t\t\t\texpect(count).toBe(5);\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should connect using .getForId().connect()\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a actor first to get its ID\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-get-for-id\"]);\n\t\t\t\tawait handle.increment(3);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\t// Get a new handle using the actor ID and connect\n\t\t\t\tconst idHandle = client.counter.getForId(actorId);\n\t\t\t\tconst connection = idHandle.connect();\n\n\t\t\t\t// Verify connection works and state is preserved\n\t\t\t\tconst count = await connection.getCount();\n\t\t\t\texpect(count).toBe(3);\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should connect using .getOrCreate().connect()\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Get or create actor and connect\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-get-or-create\"]);\n\t\t\t\tconst connection = handle.connect();\n\n\t\t\t\t// Verify connection works\n\t\t\t\tconst count = await connection.increment(7);\n\t\t\t\texpect(count).toBe(7);\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should connect using (await create()).connect()\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and connect\n\t\t\t\tconst handle = await client.counter.create([\"test-create\"]);\n\t\t\t\tconst connection = handle.connect();\n\n\t\t\t\t// Verify connection works\n\t\t\t\tconst count = await connection.increment(9);\n\t\t\t\texpect(count).toBe(9);\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Event Communication\", () => {\n\t\t\ttest(\"should mix RPC calls and WebSocket events\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-mixed-rpc-ws\"]);\n\t\t\t\tconst connection = handle.connect();\n\n\t\t\t\t// Set up event listener\n\t\t\t\tconst receivedEvents: number[] = [];\n\t\t\t\tconnection.on(\"newCount\", (count: number) => {\n\t\t\t\t\treceivedEvents.push(count);\n\t\t\t\t});\n\n\t\t\t\t// TODO: There is a race condition with opening subscription and sending events on SSE, so we need to wait for a successful round trip on the event\n\t\t\t\tawait vi.waitFor(async () => {\n\t\t\t\t\t// Send one RPC call over the connection to ensure it's open\n\t\t\t\t\tawait connection.setCount(1);\n\t\t\t\t\texpect(receivedEvents).includes(1);\n\t\t\t\t});\n\n\t\t\t\t// Now use stateless RPC calls through the handle (no connection)\n\t\t\t\t// These should still trigger events that the connection receives\n\t\t\t\tawait handle.setCount(2);\n\t\t\t\tawait handle.setCount(3);\n\n\t\t\t\t// Wait for all events to be received\n\t\t\t\tawait vi.waitFor(() => {\n\t\t\t\t\texpect(receivedEvents).includes(2);\n\t\t\t\t\texpect(receivedEvents).includes(3);\n\t\t\t\t});\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should receive events via broadcast\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and connect\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-broadcast\"]);\n\t\t\t\tconst connection = handle.connect();\n\n\t\t\t\t// Set up event listener\n\t\t\t\tconst receivedEvents: number[] = [];\n\t\t\t\tconnection.on(\"newCount\", (count: number) => {\n\t\t\t\t\treceivedEvents.push(count);\n\t\t\t\t});\n\n\t\t\t\t// HACK: Race condition between subscribing & sending events in SSE\n\t\t\t\t// Verify events were received\n\t\t\t\tawait vi.waitFor(\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tawait connection.setCount(5);\n\t\t\t\t\t\tawait connection.setCount(8);\n\t\t\t\t\t\texpect(receivedEvents).toContain(5);\n\t\t\t\t\t\texpect(receivedEvents).toContain(8);\n\t\t\t\t\t},\n\t\t\t\t\t{ timeout: 10_000 },\n\t\t\t\t);\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should handle one-time events with once()\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and connect\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-once\"]);\n\t\t\t\tconst connection = handle.connect();\n\n\t\t\t\t// Set up one-time event listener\n\t\t\t\tconst receivedEvents: number[] = [];\n\t\t\t\tconnection.once(\"newCount\", (count: number) => {\n\t\t\t\t\treceivedEvents.push(count);\n\t\t\t\t});\n\n\t\t\t\t// Trigger multiple events, but should only receive the first one\n\t\t\t\tawait connection.increment(5);\n\t\t\t\tawait connection.increment(3);\n\n\t\t\t\t// Verify only the first event was received\n\t\t\t\tawait vi.waitFor(() => {\n\t\t\t\t\texpect(receivedEvents).toEqual([5]);\n\t\t\t\t\texpect(receivedEvents).not.toContain(8);\n\t\t\t\t});\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should unsubscribe from events\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and connect\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-unsubscribe\"]);\n\t\t\t\tconst connection = handle.connect();\n\n\t\t\t\t// Set up event listener with unsubscribe\n\t\t\t\tconst receivedEvents: number[] = [];\n\t\t\t\tconst unsubscribe = connection.on(\"newCount\", (count: number) => {\n\t\t\t\t\treceivedEvents.push(count);\n\t\t\t\t});\n\n\t\t\t\t// TODO: SSE has race condition with subscriptions & publishing messages\n\t\t\t\t// Trigger first event\n\t\t\t\tawait vi.waitFor(async () => {\n\t\t\t\t\tawait connection.setCount(5);\n\t\t\t\t\texpect(receivedEvents).toEqual([5]);\n\t\t\t\t});\n\n\t\t\t\t// Unsubscribe\n\t\t\t\tunsubscribe();\n\n\t\t\t\t// Trigger second event, should not be received\n\t\t\t\tawait connection.setCount(8);\n\n\t\t\t\t// Verify only the first event was received\n\t\t\t\texpect(receivedEvents).not.toContain(8);\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Connection Parameters\", () => {\n\t\t\ttest(\"should pass connection parameters\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create two connections with different params\n\t\t\t\tconst handle1 = client.counterWithParams.getOrCreate([\"test-params\"], {\n\t\t\t\t\tparams: { name: \"user1\" },\n\t\t\t\t});\n\t\t\t\tconst handle2 = client.counterWithParams.getOrCreate([\"test-params\"], {\n\t\t\t\t\tparams: { name: \"user2\" },\n\t\t\t\t});\n\n\t\t\t\tconst conn1 = handle1.connect();\n\t\t\t\tconst conn2 = handle2.connect();\n\n\t\t\t\t// HACK: Call an action to wait for the connections to be established\n\t\t\t\tawait conn1.getInitializers();\n\t\t\t\tawait conn2.getInitializers();\n\n\t\t\t\t// Get initializers to verify connection params were used\n\t\t\t\tconst initializers = await conn1.getInitializers();\n\n\t\t\t\t// Verify both connection names were recorded\n\t\t\t\texpect(initializers).toContain(\"user1\");\n\t\t\t\texpect(initializers).toContain(\"user2\");\n\n\t\t\t\t// Clean up\n\t\t\t\tawait conn1.dispose();\n\t\t\t\tawait conn2.dispose();\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Lifecycle Hooks\", () => {\n\t\t\ttest.skipIf(\n\t\t\t\tdriverTestConfig.transport === \"sse\" &&\n\t\t\t\t\tdriverTestConfig.clientType === \"inline\",\n\t\t\t)(\"should trigger lifecycle hooks\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create and connect\n\t\t\t\tconst connHandle = client.counterWithLifecycle.getOrCreate(\n\t\t\t\t\t[\"test-lifecycle\"],\n\t\t\t\t\t{\n\t\t\t\t\t\tparams: { trackLifecycle: true },\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tconst connection = connHandle.connect();\n\n\t\t\t\t// Verify lifecycle events were triggered\n\t\t\t\tconst events = await connection.getEvents();\n\t\t\t\texpect(events).toEqual([\"onStart\", \"onBeforeConnect\", \"onConnect\"]);\n\n\t\t\t\t// Disconnect should trigger onDisconnect\n\t\t\t\tawait connection.dispose();\n\n\t\t\t\tawait vi.waitFor(\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\t// Reconnect to check if onDisconnect was called\n\t\t\t\t\t\tconst handle = client.counterWithLifecycle.getOrCreate([\n\t\t\t\t\t\t\t\"test-lifecycle\",\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tconst finalEvents = await handle.getEvents();\n\t\t\t\t\t\texpect(finalEvents).toBeOneOf([\n\t\t\t\t\t\t\t// Still active\n\t\t\t\t\t\t\t[\"onStart\", \"onBeforeConnect\", \"onConnect\", \"onDisconnect\"],\n\t\t\t\t\t\t\t// Went to sleep and woke back up\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\"onStart\",\n\t\t\t\t\t\t\t\t\"onBeforeConnect\",\n\t\t\t\t\t\t\t\t\"onConnect\",\n\t\t\t\t\t\t\t\t\"onDisconnect\",\n\t\t\t\t\t\t\t\t\"onStart\",\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t]);\n\t\t\t\t\t},\n\t\t\t\t\t// NOTE: High timeout required for Cloudflare Workers\n\t\t\t\t\t{\n\t\t\t\t\t\ttimeout: 10_000,\n\t\t\t\t\t\tinterval: 100,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test, vi } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorConnStateTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Connection State Tests\", () => {\n\t\tdescribe(\"Connection State Initialization\", () => {\n\t\t\ttest(\"should retrieve connection state\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Connect to the actor\n\t\t\t\tconst connection = client.connStateActor.getOrCreate().connect();\n\n\t\t\t\t// Get the connection state\n\t\t\t\tconst connState = await connection.getConnectionState();\n\n\t\t\t\t// Verify the connection state structure\n\t\t\t\texpect(connState.id).toBeDefined();\n\t\t\t\texpect(connState.username).toBeDefined();\n\t\t\t\texpect(connState.role).toBeDefined();\n\t\t\t\texpect(connState.counter).toBeDefined();\n\t\t\t\texpect(connState.createdAt).toBeDefined();\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should initialize connection state with custom parameters\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Connect with custom parameters\n\t\t\t\tconst connection = client.connStateActor\n\t\t\t\t\t.getOrCreate([], {\n\t\t\t\t\t\tparams: {\n\t\t\t\t\t\t\tusername: \"testuser\",\n\t\t\t\t\t\t\trole: \"admin\",\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\t.connect();\n\n\t\t\t\t// Get the connection state\n\t\t\t\tconst connState = await connection.getConnectionState();\n\n\t\t\t\t// Verify the connection state was initialized with custom values\n\t\t\t\texpect(connState.username).toBe(\"testuser\");\n\t\t\t\texpect(connState.role).toBe(\"admin\");\n\n\t\t\t\t// Clean up\n\t\t\t\tawait connection.dispose();\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Connection State Management\", () => {\n\t\t\ttest(\"should maintain unique state for each connection\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create multiple connections\n\t\t\t\tconst conn1 = client.connStateActor\n\t\t\t\t\t.getOrCreate([], {\n\t\t\t\t\t\tparams: { username: \"user1\" },\n\t\t\t\t\t})\n\t\t\t\t\t.connect();\n\n\t\t\t\tconst conn2 = client.connStateActor\n\t\t\t\t\t.getOrCreate([], {\n\t\t\t\t\t\tparams: { username: \"user2\" },\n\t\t\t\t\t})\n\t\t\t\t\t.connect();\n\n\t\t\t\t// Update connection state for each connection\n\t\t\t\tawait conn1.incrementConnCounter(5);\n\t\t\t\tawait conn2.incrementConnCounter(10);\n\n\t\t\t\t// Get state for each connection\n\t\t\t\tconst state1 = await conn1.getConnectionState();\n\t\t\t\tconst state2 = await conn2.getConnectionState();\n\n\t\t\t\t// Verify states are separate\n\t\t\t\texpect(state1.counter).toBe(5);\n\t\t\t\texpect(state2.counter).toBe(10);\n\t\t\t\texpect(state1.username).toBe(\"user1\");\n\t\t\t\texpect(state2.username).toBe(\"user2\");\n\n\t\t\t\t// Clean up\n\t\t\t\tawait conn1.dispose();\n\t\t\t\tawait conn2.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should track connections in shared state\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create two connections\n\t\t\t\tconst handle = client.connStateActor.getOrCreate();\n\t\t\t\tconst conn1 = handle.connect();\n\t\t\t\tconst conn2 = handle.connect();\n\n\t\t\t\t// HACK: Wait for both connections to successfully connect by waiting for a round trip RPC\n\t\t\t\tawait conn1.getConnectionState();\n\t\t\t\tawait conn2.getConnectionState();\n\n\t\t\t\t// Get state1 for reference\n\t\t\t\tconst state1 = await conn1.getConnectionState();\n\n\t\t\t\t// Get connection IDs tracked by the actor\n\t\t\t\tconst connectionIds = await conn1.getConnectionIds();\n\n\t\t\t\t// There should be at least 2 connections tracked\n\t\t\t\texpect(connectionIds.length).toBeGreaterThanOrEqual(2);\n\n\t\t\t\t// Should include the ID of the first connection\n\t\t\t\texpect(connectionIds).toContain(state1.id);\n\n\t\t\t\t// Clean up\n\t\t\t\tawait conn1.dispose();\n\t\t\t\tawait conn2.dispose();\n\t\t\t});\n\n\t\t\ttest(\"should identify different connections in the same actor\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create two connections to the same actor\n\t\t\t\tconst handle = client.connStateActor.getOrCreate();\n\t\t\t\tconst conn1 = handle.connect();\n\t\t\t\tconst conn2 = handle.connect();\n\n\t\t\t\t// HACK: Wait for both connections to successfully connect by waiting for a round trip RPC\n\t\t\t\tawait conn1.getConnectionState();\n\t\t\t\tawait conn2.getConnectionState();\n\n\t\t\t\t// Get all connection states\n\t\t\t\tconst allStates = await conn1.getAllConnectionStates();\n\n\t\t\t\t// Should have at least 2 states\n\t\t\t\texpect(allStates.length).toBeGreaterThanOrEqual(2);\n\n\t\t\t\t// IDs should be unique\n\t\t\t\tconst ids = allStates.map((state) => state.id);\n\t\t\t\tconst uniqueIds = [...new Set(ids)];\n\t\t\t\texpect(uniqueIds.length).toBe(ids.length);\n\n\t\t\t\t// Clean up\n\t\t\t\tawait conn1.dispose();\n\t\t\t\tawait conn2.dispose();\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Connection Lifecycle\", () => {\n\t\t\ttest.skipIf(\n\t\t\t\tdriverTestConfig.transport === \"sse\" &&\n\t\t\t\t\tdriverTestConfig.clientType === \"inline\",\n\t\t\t)(\"should track connection and disconnection events\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst debugHandle = client.connStateActor.getOrCreate(undefined, {\n\t\t\t\t\tparams: { noCount: true },\n\t\t\t\t});\n\n\t\t\t\t// Create a connection\n\t\t\t\tconst conn = client.connStateActor.getOrCreate().connect();\n\n\t\t\t\t// Get the connection state\n\t\t\t\tconst connState = await conn.getConnectionState();\n\n\t\t\t\t// Verify the connection is tracked\n\t\t\t\tawait vi.waitFor(async () => {\n\t\t\t\t\tconst connectionIds = await debugHandle.getConnectionIds();\n\t\t\t\t\texpect(connectionIds).toContain(connState.id);\n\t\t\t\t});\n\n\t\t\t\t// Initial disconnection count\n\t\t\t\tawait vi.waitFor(async () => {\n\t\t\t\t\tconst disconnects = await debugHandle.getDisconnectionCount();\n\t\t\t\t\texpect(disconnects).toBe(0);\n\t\t\t\t});\n\n\t\t\t\t// Dispose the connection\n\t\t\t\tawait conn.dispose();\n\n\t\t\t\t// Validate conn count\n\t\t\t\tawait vi.waitFor(\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tconsole.log(\"disconnects before\");\n\t\t\t\t\t\tconst disconnects = await debugHandle.getDisconnectionCount();\n\t\t\t\t\t\tconsole.log(\"disconnects\", disconnects);\n\t\t\t\t\t\texpect(disconnects).toBe(1);\n\t\t\t\t\t},\n\t\t\t\t\t// SSE takes a long time to disconnect on CF Workers\n\t\t\t\t\t{\n\t\t\t\t\t\ttimeout: 10_000,\n\t\t\t\t\t\tinterval: 100,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\t// Create a new connection to check the disconnection count\n\t\t\t\tconst newConn = client.connStateActor.getOrCreate().connect();\n\n\t\t\t\t// Verify the connection is tracked\n\t\t\t\tawait vi.waitFor(async () => {\n\t\t\t\t\tconst connectionIds = await debugHandle.getConnectionIds();\n\t\t\t\t\tconsole.log(\"conn ids\", connectionIds);\n\t\t\t\t\texpect(connectionIds.length).toBe(1);\n\t\t\t\t});\n\n\t\t\t\t// Clean up\n\t\t\t\tawait newConn.dispose();\n\n\t\t\t\t// Verify disconnection was tracked\n\t\t\t\tawait vi.waitFor(\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tconsole.log(\"A\");\n\t\t\t\t\t\tconst disconnects = await debugHandle.getDisconnectionCount();\n\t\t\t\t\t\tconsole.log(`B ${disconnects}`);\n\t\t\t\t\t\texpect(disconnects).toBe(2);\n\t\t\t\t\t},\n\t\t\t\t\t// SSE takes a long time to disconnect on CF Workers\n\t\t\t\t\t{\n\t\t\t\t\t\ttimeout: 10_000,\n\t\t\t\t\t\tinterval: 100,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\n\t\t\ttest(\"should update connection state\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a connection\n\t\t\t\tconst conn = client.connStateActor.getOrCreate().connect();\n\n\t\t\t\t// Get the initial state\n\t\t\t\tconst initialState = await conn.getConnectionState();\n\t\t\t\texpect(initialState.username).toBe(\"anonymous\");\n\n\t\t\t\t// Update the connection state\n\t\t\t\tconst updatedState = await conn.updateConnection({\n\t\t\t\t\tusername: \"newname\",\n\t\t\t\t\trole: \"moderator\",\n\t\t\t\t});\n\n\t\t\t\t// Verify the state was updated\n\t\t\t\texpect(updatedState.username).toBe(\"newname\");\n\t\t\t\texpect(updatedState.role).toBe(\"moderator\");\n\n\t\t\t\t// Get the state again to verify persistence\n\t\t\t\tconst latestState = await conn.getConnectionState();\n\t\t\t\texpect(latestState.username).toBe(\"newname\");\n\t\t\t\texpect(latestState.role).toBe(\"moderator\");\n\n\t\t\t\t// Clean up\n\t\t\t\tawait conn.dispose();\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Connection Communication\", () => {\n\t\t\ttest(\"should send messages to specific connections\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create two connections\n\t\t\t\tconst handle = client.connStateActor.getOrCreate();\n\t\t\t\tconst conn1 = handle.connect();\n\t\t\t\tconst conn2 = handle.connect();\n\n\t\t\t\t// Get connection states\n\t\t\t\tconst state1 = await conn1.getConnectionState();\n\t\t\t\tconst state2 = await conn2.getConnectionState();\n\n\t\t\t\t// Set up event listener on second connection\n\t\t\t\tconst receivedMessages: any[] = [];\n\t\t\t\tconn2.on(\"directMessage\", (data) => {\n\t\t\t\t\treceivedMessages.push(data);\n\t\t\t\t});\n\n\t\t\t\t// TODO: SSE has race condition between subscrib eand publish message\n\t\t\t\tawait vi.waitFor(async () => {\n\t\t\t\t\t// Send message from first connection to second\n\t\t\t\t\tconst success = await conn1.sendToConnection(\n\t\t\t\t\t\tstate2.id,\n\t\t\t\t\t\t\"Hello from conn1\",\n\t\t\t\t\t);\n\t\t\t\t\texpect(success).toBe(true);\n\n\t\t\t\t\t// Verify message was received\n\t\t\t\t\texpect(receivedMessages.length).toBe(1);\n\t\t\t\t\texpect(receivedMessages[0].from).toBe(state1.id);\n\t\t\t\t\texpect(receivedMessages[0].message).toBe(\"Hello from conn1\");\n\t\t\t\t});\n\n\t\t\t\t// Clean up\n\t\t\t\tawait conn1.dispose();\n\t\t\t\tawait conn2.dispose();\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { runActorScheduleTests } from \"./actor-schedule\";\nimport { runActorSleepTests } from \"./actor-sleep\";\nimport { runActorStateTests } from \"./actor-state\";\n\nexport function runActorDriverTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Driver Tests\", () => {\n\t\t// Run state persistence tests\n\t\trunActorStateTests(driverTestConfig);\n\n\t\t// Run scheduled alarms tests\n\t\trunActorScheduleTests(driverTestConfig);\n\t});\n}\n\n/** Actor driver tests that need to be tested for all transport mechanisms. */\nexport function runActorDriverTestsWithTransport(\n\tdriverTestConfig: DriverTestConfig,\n) {\n\tdescribe(\"Actor Driver Tests\", () => {\n\t\t// Run actor sleep tests\n\t\trunActorSleepTests(driverTestConfig);\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest, waitFor } from \"../utils\";\n\nexport function runActorScheduleTests(driverTestConfig: DriverTestConfig) {\n\tdescribe.skipIf(driverTestConfig.skip?.schedule)(\n\t\t\"Actor Schedule Tests\",\n\t\t() => {\n\t\t\t// See alarm + actor sleeping test in actor-sleep.ts\n\n\t\t\tdescribe(\"Scheduled Alarms\", () => {\n\t\t\t\ttest(\"executes c.schedule.at() with specific timestamp\", async (c) => {\n\t\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t\t// Create instance\n\t\t\t\t\tconst scheduled = client.scheduled.getOrCreate();\n\n\t\t\t\t\t// Schedule a task to run using timestamp\n\t\t\t\t\tconst timestamp = Date.now() + 250;\n\t\t\t\t\tawait scheduled.scheduleTaskAt(timestamp);\n\n\t\t\t\t\t// Wait for longer than the scheduled time\n\t\t\t\t\tawait waitFor(driverTestConfig, 500);\n\n\t\t\t\t\t// Verify the scheduled task ran\n\t\t\t\t\tconst lastRun = await scheduled.getLastRun();\n\t\t\t\t\tconst scheduledCount = await scheduled.getScheduledCount();\n\n\t\t\t\t\texpect(lastRun).toBeGreaterThan(0);\n\t\t\t\t\texpect(scheduledCount).toBe(1);\n\t\t\t\t});\n\n\t\t\t\ttest(\"executes c.schedule.after() with delay\", async (c) => {\n\t\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t\t// Create instance\n\t\t\t\t\tconst scheduled = client.scheduled.getOrCreate();\n\n\t\t\t\t\t// Schedule a task to run using delay\n\t\t\t\t\tawait scheduled.scheduleTaskAfter(250);\n\n\t\t\t\t\t// Wait for longer than the scheduled time\n\t\t\t\t\tawait waitFor(driverTestConfig, 500);\n\n\t\t\t\t\t// Verify the scheduled task ran\n\t\t\t\t\tconst lastRun = await scheduled.getLastRun();\n\t\t\t\t\tconst scheduledCount = await scheduled.getScheduledCount();\n\n\t\t\t\t\texpect(lastRun).toBeGreaterThan(0);\n\t\t\t\t\texpect(scheduledCount).toBe(1);\n\t\t\t\t});\n\n\t\t\t\ttest(\"multiple scheduled tasks execute in order\", async (c) => {\n\t\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t\t// Create instance\n\t\t\t\t\tconst scheduled = client.scheduled.getOrCreate();\n\n\t\t\t\t\t// Reset history to start fresh\n\t\t\t\t\tawait scheduled.clearHistory();\n\n\t\t\t\t\t// Schedule multiple tasks with different delays\n\t\t\t\t\tawait scheduled.scheduleTaskAfterWithId(\"first\", 250);\n\t\t\t\t\tawait scheduled.scheduleTaskAfterWithId(\"second\", 750);\n\t\t\t\t\tawait scheduled.scheduleTaskAfterWithId(\"third\", 1250);\n\n\t\t\t\t\t// Wait for first task only\n\t\t\t\t\tawait waitFor(driverTestConfig, 500);\n\t\t\t\t\tconst history1 = await scheduled.getTaskHistory();\n\t\t\t\t\texpect(history1).toEqual([\"first\"]);\n\n\t\t\t\t\t// Wait for second task\n\t\t\t\t\tawait waitFor(driverTestConfig, 500);\n\t\t\t\t\tconst history2 = await scheduled.getTaskHistory();\n\t\t\t\t\texpect(history2).toEqual([\"first\", \"second\"]);\n\n\t\t\t\t\t// Wait for third task\n\t\t\t\t\tawait waitFor(driverTestConfig, 500);\n\t\t\t\t\tconst history3 = await scheduled.getTaskHistory();\n\t\t\t\t\texpect(history3).toEqual([\"first\", \"second\", \"third\"]);\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t);\n}\n","import { describe, expect, test } from \"vitest\";\nimport { SLEEP_TIMEOUT } from \"../../../fixtures/driver-test-suite/sleep\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest, waitFor } from \"../utils\";\n\n// TODO: These tests are broken with fake timers because `_sleep` requires\n// background async promises that have a race condition with calling\n// `getCounts`\n//\n// To fix this, we need to imeplment some event system to be able to check for\n// when an actor has slept. OR we can expose an HTTP endpoint on the manager\n// for `.test` that checks if na actor is sleeping that we can poll.\nexport function runActorSleepTests(driverTestConfig: DriverTestConfig) {\n\tdescribe.skipIf(driverTestConfig.skip?.sleep)(\"Actor Sleep Tests\", () => {\n\t\ttest(\"actor sleep persists state\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor\n\t\t\tconst sleepActor = client.sleep.getOrCreate();\n\n\t\t\t// Verify initial sleep count\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Trigger sleep\n\t\t\tawait sleepActor.triggerSleep();\n\n\t\t\t// HACK: Wait for sleep to finish in background\n\t\t\tawait waitFor(driverTestConfig, 250);\n\n\t\t\t// Get sleep count after restore\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1);\n\t\t\t\texpect(startCount).toBe(2);\n\t\t\t}\n\t\t});\n\n\t\ttest(\"actor sleep persists state with connect\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor with persistent connection\n\t\t\tconst sleepActor = client.sleep.getOrCreate().connect();\n\n\t\t\t// Verify initial sleep count\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Trigger sleep\n\t\t\tawait sleepActor.triggerSleep();\n\n\t\t\t// Disconnect to allow reconnection\n\t\t\tawait sleepActor.dispose();\n\n\t\t\t// HACK: Wait for sleep to finish in background\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Reconnect to get sleep count after restore\n\t\t\tconst sleepActor2 = client.sleep.getOrCreate();\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor2.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1);\n\t\t\t\texpect(startCount).toBe(2);\n\t\t\t}\n\t\t});\n\n\t\ttest(\"actor automatically sleeps after timeout\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor\n\t\t\tconst sleepActor = client.sleep.getOrCreate();\n\n\t\t\t// Verify initial sleep count\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Wait for sleep\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Get sleep count after restore\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1);\n\t\t\t\texpect(startCount).toBe(2);\n\t\t\t}\n\t\t});\n\n\t\ttest(\"actor automatically sleeps after timeout with connect\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor with persistent connection\n\t\t\tconst sleepActor = client.sleep.getOrCreate().connect();\n\n\t\t\t// Verify initial sleep count\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Disconnect to allow actor to sleep\n\t\t\tawait sleepActor.dispose();\n\n\t\t\t// Wait for sleep\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Reconnect to get sleep count after restore\n\t\t\tconst sleepActor2 = client.sleep.getOrCreate();\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor2.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1);\n\t\t\t\texpect(startCount).toBe(2);\n\t\t\t}\n\t\t});\n\n\t\ttest(\"rpc calls keep actor awake\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor\n\t\t\tconst sleepActor = client.sleep.getOrCreate();\n\n\t\t\t// Verify initial state\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Wait almost until sleep timeout, then make RPC call\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT - 250);\n\n\t\t\t// RPC call should reset the sleep timer\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0); // Haven't slept yet\n\t\t\t\texpect(startCount).toBe(1); // Still the same instance\n\t\t\t}\n\n\t\t\t// Wait another partial timeout period - actor should still be awake\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT - 250);\n\n\t\t\t// Actor should still be awake\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0); // Still haven't slept\n\t\t\t\texpect(startCount).toBe(1); // Still the same instance\n\t\t\t}\n\n\t\t\t// Now wait for full timeout without any RPC calls\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Actor should have slept and restarted\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1); // Slept once\n\t\t\t\texpect(startCount).toBe(2); // New instance after sleep\n\t\t\t}\n\t\t});\n\n\t\ttest(\"alarms keep actor awake\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor\n\t\t\tconst sleepActor = client.sleep.getOrCreate();\n\n\t\t\t// Verify initial state\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Set an alarm to keep the actor awake\n\t\t\tawait sleepActor.setAlarm(SLEEP_TIMEOUT - 250);\n\n\t\t\t// Wait until after SLEEPT_IMEOUT to validate the actor did not sleep\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Actor should not have slept\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\t\t});\n\n\t\ttest(\"alarms wake actors\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor\n\t\t\tconst sleepActor = client.sleep.getOrCreate();\n\n\t\t\t// Verify initial state\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Set an alarm to keep the actor awake\n\t\t\tawait sleepActor.setAlarm(SLEEP_TIMEOUT + 250);\n\n\t\t\t// Wait until after SLEEPT_IMEOUT to validate the actor did not sleep\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 200);\n\n\t\t\t// Actor should not have slept\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1);\n\t\t\t\texpect(startCount).toBe(2);\n\t\t\t}\n\t\t});\n\n\t\ttest(\"long running rpcs keep actor awake\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor\n\t\t\tconst sleepActor = client.sleepWithLongRpc.getOrCreate().connect();\n\n\t\t\t// Verify initial state\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Start a long-running RPC that takes longer than the sleep timeout\n\t\t\tconst waitPromise = new Promise((resolve) =>\n\t\t\t\tsleepActor.once(\"waiting\", resolve),\n\t\t\t);\n\t\t\tconst longRunningPromise = sleepActor.longRunningRpc();\n\t\t\tawait waitPromise;\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\t\t\tawait sleepActor.finishLongRunningRpc();\n\t\t\tawait longRunningPromise;\n\n\t\t\t// Actor should still be the same instance (didn't sleep during RPC)\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0); // Hasn't slept\n\t\t\t\texpect(startCount).toBe(1); // Same instance\n\t\t\t}\n\t\t\tawait sleepActor.dispose();\n\n\t\t\t// Now wait for the sleep timeout\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Actor should have slept after the timeout\n\t\t\tconst sleepActor2 = client.sleepWithLongRpc.getOrCreate();\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor2.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1); // Slept once\n\t\t\t\texpect(startCount).toBe(2); // New instance after sleep\n\t\t\t}\n\t\t});\n\n\t\ttest(\"active raw websockets keep actor awake\", async (c) => {\n\t\t\tconst { client, endpoint: baseUrl } = await setupDriverTest(\n\t\t\t\tc,\n\t\t\t\tdriverTestConfig,\n\t\t\t);\n\n\t\t\t// Create actor\n\t\t\tconst sleepActor = client.sleepWithRawWebSocket.getOrCreate();\n\n\t\t\t// Verify initial state\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Connect WebSocket\n\t\t\tconst ws = await sleepActor.websocket();\n\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tws.onopen = () => resolve();\n\t\t\t\tws.onerror = reject;\n\t\t\t});\n\n\t\t\t// Wait for connection message\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tws.onmessage = (event) => {\n\t\t\t\t\tconst data = JSON.parse(event.data);\n\t\t\t\t\tif (data.type === \"connected\") {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t});\n\n\t\t\t// Wait longer than sleep timeout while keeping WebSocket connected\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Send a message to check if actor is still alive\n\t\t\tws.send(JSON.stringify({ type: \"getCounts\" }));\n\n\t\t\tconst counts = await new Promise<any>((resolve) => {\n\t\t\t\tws.onmessage = (event) => {\n\t\t\t\t\tconst data = JSON.parse(event.data);\n\t\t\t\t\tif (data.type === \"counts\") {\n\t\t\t\t\t\tresolve(data);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t});\n\n\t\t\t// Actor should still be the same instance (didn't sleep while WebSocket connected)\n\t\t\texpect(counts.sleepCount).toBe(0);\n\t\t\texpect(counts.startCount).toBe(1);\n\n\t\t\t// Close WebSocket\n\t\t\tws.close();\n\n\t\t\t// Wait for sleep timeout after WebSocket closed\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Actor should have slept after WebSocket closed\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1); // Slept once\n\t\t\t\texpect(startCount).toBe(2); // New instance after sleep\n\t\t\t}\n\t\t});\n\n\t\ttest(\"active raw fetch requests keep actor awake\", async (c) => {\n\t\t\tconst { client, endpoint: baseUrl } = await setupDriverTest(\n\t\t\t\tc,\n\t\t\t\tdriverTestConfig,\n\t\t\t);\n\n\t\t\t// Create actor\n\t\t\tconst sleepActor = client.sleepWithRawHttp.getOrCreate();\n\n\t\t\t// Verify initial state\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Start a long-running fetch request\n\t\t\tconst fetchDuration = SLEEP_TIMEOUT + 250;\n\t\t\tconst fetchPromise = sleepActor.fetch(\n\t\t\t\t`long-request?duration=${fetchDuration}`,\n\t\t\t);\n\n\t\t\t// Wait for the fetch to complete\n\t\t\tconst response = await fetchPromise;\n\t\t\tconst result = (await response.json()) as { completed: boolean };\n\t\t\texpect(result.completed).toBe(true);\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount, requestCount } =\n\t\t\t\t\tawait sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t\texpect(requestCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Wait for sleep timeout\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Actor should have slept after timeout\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(1); // Slept once\n\t\t\t\texpect(startCount).toBe(2); // New instance after sleep\n\t\t\t}\n\t\t});\n\n\t\ttest(\"noSleep option disables sleeping\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor with noSleep option\n\t\t\tconst sleepActor = client.sleepWithNoSleepOption.getOrCreate();\n\n\t\t\t// Verify initial state\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0);\n\t\t\t\texpect(startCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Wait longer than sleep timeout\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Actor should NOT have slept due to noSleep option\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0); // Never slept\n\t\t\t\texpect(startCount).toBe(1); // Still the same instance\n\t\t\t}\n\n\t\t\t// Wait even longer to be sure\n\t\t\tawait waitFor(driverTestConfig, SLEEP_TIMEOUT + 250);\n\n\t\t\t// Actor should still not have slept\n\t\t\t{\n\t\t\t\tconst { startCount, sleepCount } = await sleepActor.getCounts();\n\t\t\t\texpect(sleepCount).toBe(0); // Never slept\n\t\t\t\texpect(startCount).toBe(1); // Still the same instance\n\t\t\t}\n\t\t});\n\t});\n}\n","import { actor, type UniversalWebSocket } from \"rivetkit\";\nimport { promiseWithResolvers } from \"rivetkit/utils\";\n\nexport const SLEEP_TIMEOUT = 1000;\n\nexport const sleep = actor({\n\tstate: { startCount: 0, sleepCount: 0 },\n\tonStart: (c) => {\n\t\tc.state.startCount += 1;\n\t},\n\tonStop: (c) => {\n\t\tc.state.sleepCount += 1;\n\t},\n\tactions: {\n\t\ttriggerSleep: (c) => {\n\t\t\tc.sleep();\n\t\t},\n\t\tgetCounts: (c) => {\n\t\t\treturn { startCount: c.state.startCount, sleepCount: c.state.sleepCount };\n\t\t},\n\t\tsetAlarm: async (c, duration: number) => {\n\t\t\tawait c.schedule.after(duration, \"onAlarm\");\n\t\t},\n\t\tonAlarm: (c) => {\n\t\t\tc.log.info(\"alarm called\");\n\t\t},\n\t},\n\toptions: {\n\t\tsleepTimeout: SLEEP_TIMEOUT,\n\t},\n});\n\nexport const sleepWithLongRpc = actor({\n\tstate: { startCount: 0, sleepCount: 0 },\n\tcreateVars: () => ({}) as { longRunningResolve: PromiseWithResolvers<void> },\n\tonStart: (c) => {\n\t\tc.state.startCount += 1;\n\t},\n\tonStop: (c) => {\n\t\tc.state.sleepCount += 1;\n\t},\n\tactions: {\n\t\tgetCounts: (c) => {\n\t\t\treturn { startCount: c.state.startCount, sleepCount: c.state.sleepCount };\n\t\t},\n\t\tlongRunningRpc: async (c) => {\n\t\t\tc.log.info(\"starting long running rpc\");\n\t\t\tc.vars.longRunningResolve = promiseWithResolvers();\n\t\t\tc.broadcast(\"waiting\");\n\t\t\tawait c.vars.longRunningResolve.promise;\n\t\t\tc.log.info(\"finished long running rpc\");\n\t\t},\n\t\tfinishLongRunningRpc: (c) => c.vars.longRunningResolve?.resolve(),\n\t},\n\toptions: {\n\t\tsleepTimeout: SLEEP_TIMEOUT,\n\t},\n});\n\nexport const sleepWithRawHttp = actor({\n\tstate: { startCount: 0, sleepCount: 0, requestCount: 0 },\n\tonStart: (c) => {\n\t\tc.state.startCount += 1;\n\t},\n\tonStop: (c) => {\n\t\tc.state.sleepCount += 1;\n\t},\n\tonFetch: async (c, request) => {\n\t\tc.state.requestCount += 1;\n\t\tconst url = new URL(request.url);\n\n\t\tif (url.pathname === \"/long-request\") {\n\t\t\tconst duration = parseInt(url.searchParams.get(\"duration\") || \"1000\");\n\t\t\tc.log.info({ msg: \"starting long fetch request\", duration });\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, duration));\n\t\t\tc.log.info(\"finished long fetch request\");\n\t\t\treturn new Response(JSON.stringify({ completed: true }), {\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t});\n\t\t}\n\n\t\treturn new Response(\"Not Found\", { status: 404 });\n\t},\n\tactions: {\n\t\tgetCounts: (c) => {\n\t\t\treturn {\n\t\t\t\tstartCount: c.state.startCount,\n\t\t\t\tsleepCount: c.state.sleepCount,\n\t\t\t\trequestCount: c.state.requestCount,\n\t\t\t};\n\t\t},\n\t},\n\toptions: {\n\t\tsleepTimeout: SLEEP_TIMEOUT,\n\t},\n});\n\nexport const sleepWithRawWebSocket = actor({\n\tstate: { startCount: 0, sleepCount: 0, connectionCount: 0 },\n\tonStart: (c) => {\n\t\tc.state.startCount += 1;\n\t},\n\tonStop: (c) => {\n\t\tc.state.sleepCount += 1;\n\t},\n\tonWebSocket: (c, websocket: UniversalWebSocket, opts) => {\n\t\tc.state.connectionCount += 1;\n\t\tc.log.info({\n\t\t\tmsg: \"websocket connected\",\n\t\t\tconnectionCount: c.state.connectionCount,\n\t\t});\n\n\t\twebsocket.send(\n\t\t\tJSON.stringify({\n\t\t\t\ttype: \"connected\",\n\t\t\t\tconnectionCount: c.state.connectionCount,\n\t\t\t}),\n\t\t);\n\n\t\twebsocket.addEventListener(\"message\", (event: any) => {\n\t\t\tconst data = event.data;\n\t\t\tif (typeof data === \"string\") {\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(data);\n\t\t\t\t\tif (parsed.type === \"getCounts\") {\n\t\t\t\t\t\twebsocket.send(\n\t\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\t\ttype: \"counts\",\n\t\t\t\t\t\t\t\tstartCount: c.state.startCount,\n\t\t\t\t\t\t\t\tsleepCount: c.state.sleepCount,\n\t\t\t\t\t\t\t\tconnectionCount: c.state.connectionCount,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (parsed.type === \"keepAlive\") {\n\t\t\t\t\t\t// Just acknowledge to keep connection alive\n\t\t\t\t\t\twebsocket.send(JSON.stringify({ type: \"ack\" }));\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Echo non-JSON messages\n\t\t\t\t\twebsocket.send(data);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\twebsocket.addEventListener(\"close\", () => {\n\t\t\tc.state.connectionCount -= 1;\n\t\t\tc.log.info({\n\t\t\t\tmsg: \"websocket disconnected\",\n\t\t\t\tconnectionCount: c.state.connectionCount,\n\t\t\t});\n\t\t});\n\t},\n\tactions: {\n\t\tgetCounts: (c) => {\n\t\t\treturn {\n\t\t\t\tstartCount: c.state.startCount,\n\t\t\t\tsleepCount: c.state.sleepCount,\n\t\t\t\tconnectionCount: c.state.connectionCount,\n\t\t\t};\n\t\t},\n\t},\n\toptions: {\n\t\tsleepTimeout: SLEEP_TIMEOUT,\n\t},\n});\n\nexport const sleepWithNoSleepOption = actor({\n\tstate: { startCount: 0, sleepCount: 0 },\n\tonStart: (c) => {\n\t\tc.state.startCount += 1;\n\t},\n\tonStop: (c) => {\n\t\tc.state.sleepCount += 1;\n\t},\n\tactions: {\n\t\tgetCounts: (c) => {\n\t\t\treturn { startCount: c.state.startCount, sleepCount: c.state.sleepCount };\n\t\t},\n\t},\n\toptions: {\n\t\tsleepTimeout: SLEEP_TIMEOUT,\n\t\tnoSleep: true,\n\t},\n});\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorStateTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor State Tests\", () => {\n\t\tdescribe(\"State Persistence\", () => {\n\t\t\ttest(\"persists state between actor instances\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create instance and increment\n\t\t\t\tconst counterInstance = client.counter.getOrCreate();\n\t\t\t\tconst initialCount = await counterInstance.increment(5);\n\t\t\t\texpect(initialCount).toBe(5);\n\n\t\t\t\t// Get a fresh reference to the same actor and verify state persisted\n\t\t\t\tconst sameInstance = client.counter.getOrCreate();\n\t\t\t\tconst persistedCount = await sameInstance.increment(3);\n\t\t\t\texpect(persistedCount).toBe(8);\n\t\t\t});\n\n\t\t\ttest(\"restores state after actor disconnect/reconnect\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and set initial state\n\t\t\t\tconst counterInstance = client.counter.getOrCreate();\n\t\t\t\tawait counterInstance.increment(5);\n\n\t\t\t\t// Reconnect to the same actor\n\t\t\t\tconst reconnectedInstance = client.counter.getOrCreate();\n\t\t\t\tconst persistedCount = await reconnectedInstance.increment(0);\n\t\t\t\texpect(persistedCount).toBe(5);\n\t\t\t});\n\n\t\t\ttest(\"maintains separate state for different actors\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create first counter with specific key\n\t\t\t\tconst counterA = client.counter.getOrCreate([\"counter-a\"]);\n\t\t\t\tawait counterA.increment(5);\n\n\t\t\t\t// Create second counter with different key\n\t\t\t\tconst counterB = client.counter.getOrCreate([\"counter-b\"]);\n\t\t\t\tawait counterB.increment(10);\n\n\t\t\t\t// Verify state is separate\n\t\t\t\tconst countA = await counterA.increment(0);\n\t\t\t\tconst countB = await counterB.increment(0);\n\t\t\t\texpect(countA).toBe(5);\n\t\t\t\texpect(countB).toBe(10);\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport {\n\tINTERNAL_ERROR_CODE,\n\tINTERNAL_ERROR_DESCRIPTION,\n} from \"@/actor/errors\";\nimport { assertUnreachable } from \"@/actor/utils\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorErrorHandlingTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Error Handling Tests\", () => {\n\t\tdescribe(\"UserError Handling\", () => {\n\t\t\ttest(\"should handle simple UserError with message\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Try to call an action that throws a simple UserError\n\t\t\t\tconst handle = client.errorHandlingActor.getOrCreate();\n\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.throwSimpleError();\n\t\t\t\t\t// If we get here, the test should fail\n\t\t\t\t\texpect(true).toBe(false); // This should not be reached\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\t// Verify the error properties\n\t\t\t\t\texpect(error.message).toBe(\"Simple error message\");\n\t\t\t\t\t// Default code is \"user_error\" when not specified\n\t\t\t\t\texpect(error.code).toBe(\"user_error\");\n\t\t\t\t\t// No metadata by default\n\t\t\t\t\texpect(error.metadata).toBeUndefined();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttest(\"should handle detailed UserError with code and metadata\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Try to call an action that throws a detailed UserError\n\t\t\t\tconst handle = client.errorHandlingActor.getOrCreate();\n\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.throwDetailedError();\n\t\t\t\t\t// If we get here, the test should fail\n\t\t\t\t\texpect(true).toBe(false); // This should not be reached\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\t// Verify the error properties\n\t\t\t\t\texpect(error.message).toBe(\"Detailed error message\");\n\t\t\t\t\texpect(error.code).toBe(\"detailed_error\");\n\t\t\t\t\texpect(error.metadata).toBeDefined();\n\t\t\t\t\texpect(error.metadata.reason).toBe(\"test\");\n\t\t\t\t\texpect(error.metadata.timestamp).toBeDefined();\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Internal Error Handling\", () => {\n\t\t\ttest(\"should convert internal errors to safe format\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Try to call an action that throws an internal error\n\t\t\t\tconst handle = client.errorHandlingActor.getOrCreate();\n\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.throwInternalError();\n\t\t\t\t\t// If we get here, the test should fail\n\t\t\t\t\texpect(true).toBe(false); // This should not be reached\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\t// Verify the error is converted to a safe format\n\t\t\t\t\texpect(error.code).toBe(INTERNAL_ERROR_CODE);\n\t\t\t\t\t// Original error details should not be exposed\n\t\t\t\t\texpect(error.message).toBe(INTERNAL_ERROR_DESCRIPTION);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\t// TODO: Does not work with fake timers\n\t\tdescribe.skip(\"Action Timeout\", () => {\n\t\t\ttest(\"should handle action timeouts with custom duration\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Call an action that should time out\n\t\t\t\tconst handle = client.errorHandlingActor.getOrCreate();\n\n\t\t\t\t// This should throw a timeout error because errorHandlingActor has\n\t\t\t\t// a 500ms timeout and this action tries to run for much longer\n\t\t\t\tconst timeoutPromise = handle.timeoutAction();\n\n\t\t\t\ttry {\n\t\t\t\t\tawait timeoutPromise;\n\t\t\t\t\t// If we get here, the test failed - timeout didn't occur\n\t\t\t\t\texpect(true).toBe(false); // This should not be reached\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\t// Verify it's a timeout error\n\t\t\t\t\texpect(error.message).toMatch(/timed out/i);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttest(\"should successfully run actions within timeout\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Call an action with a delay shorter than the timeout\n\t\t\t\tconst handle = client.errorHandlingActor.getOrCreate();\n\n\t\t\t\t// This should succeed because 200ms < 500ms timeout\n\t\t\t\tconst result = await handle.delayedAction(200);\n\t\t\t\texpect(result).toBe(\"Completed after 200ms\");\n\t\t\t});\n\n\t\t\ttest(\"should respect different timeouts for different actors\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// The following actors have different timeout settings:\n\t\t\t\t// customTimeoutActor: 200ms timeout\n\t\t\t\t// standardTimeoutActor: default timeout (much longer)\n\n\t\t\t\t// This should fail - 300ms delay with 200ms timeout\n\t\t\t\ttry {\n\t\t\t\t\tawait client.customTimeoutActor.getOrCreate().slowAction();\n\t\t\t\t\t// Should not reach here\n\t\t\t\t\texpect(true).toBe(false);\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\texpect(error.message).toMatch(/timed out/i);\n\t\t\t\t}\n\n\t\t\t\t// This should succeed - 50ms delay with 200ms timeout\n\t\t\t\tconst quickResult = await client.customTimeoutActor\n\t\t\t\t\t.getOrCreate()\n\t\t\t\t\t.quickAction();\n\t\t\t\texpect(quickResult).toBe(\"Quick action completed\");\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Error Recovery\", () => {\n\t\t\ttest(\"should continue working after errors\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = client.errorHandlingActor.getOrCreate();\n\n\t\t\t\t// Trigger an error\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.throwSimpleError();\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// Ignore error\n\t\t\t\t}\n\n\t\t\t\t// Actor should still work after error\n\t\t\t\tconst result = await handle.successfulAction();\n\t\t\t\texpect(result).toBe(\"success\");\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { ActorError } from \"@/client/mod\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorHandleTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Handle Tests\", () => {\n\t\tdescribe(\"Access Methods\", () => {\n\t\t\ttest(\"should use .get() to access a actor\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor first\n\t\t\t\tawait client.counter.create([\"test-get-handle\"]);\n\n\t\t\t\t// Access using get\n\t\t\t\tconst handle = client.counter.get([\"test-get-handle\"]);\n\n\t\t\t\t// Verify Action works\n\t\t\t\tconst count = await handle.increment(5);\n\t\t\t\texpect(count).toBe(5);\n\n\t\t\t\tconst retrievedCount = await handle.getCount();\n\t\t\t\texpect(retrievedCount).toBe(5);\n\t\t\t});\n\n\t\t\ttest(\"should use .getForId() to access a actor by ID\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a actor first to get its ID\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-get-for-id-handle\"]);\n\t\t\t\tawait handle.increment(3);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\t// Access using getForId\n\t\t\t\tconst idHandle = client.counter.getForId(actorId);\n\n\t\t\t\t// Verify Action works and state is preserved\n\t\t\t\tconst count = await idHandle.getCount();\n\t\t\t\texpect(count).toBe(3);\n\n\t\t\t\tconst newCount = await idHandle.increment(4);\n\t\t\t\texpect(newCount).toBe(7);\n\t\t\t});\n\n\t\t\ttest(\"should use .getOrCreate() to access or create a actor\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Access using getOrCreate - should create the actor\n\t\t\t\tconst handle = client.counter.getOrCreate([\n\t\t\t\t\t\"test-get-or-create-handle\",\n\t\t\t\t]);\n\n\t\t\t\t// Verify Action works\n\t\t\t\tconst count = await handle.increment(7);\n\t\t\t\texpect(count).toBe(7);\n\n\t\t\t\t// Get the same actor again - should retrieve existing actor\n\t\t\t\tconst sameHandle = client.counter.getOrCreate([\n\t\t\t\t\t\"test-get-or-create-handle\",\n\t\t\t\t]);\n\t\t\t\tconst retrievedCount = await sameHandle.getCount();\n\t\t\t\texpect(retrievedCount).toBe(7);\n\t\t\t});\n\n\t\t\ttest(\"should use (await create()) to create and return a handle\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and get handle\n\t\t\t\tconst handle = await client.counter.create([\"test-create-handle\"]);\n\n\t\t\t\t// Verify Action works\n\t\t\t\tconst count = await handle.increment(9);\n\t\t\t\texpect(count).toBe(9);\n\n\t\t\t\tconst retrievedCount = await handle.getCount();\n\t\t\t\texpect(retrievedCount).toBe(9);\n\t\t\t});\n\n\t\t\ttest(\"errors when calling create twice with the same key\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst key = [\"duplicate-create-handle\", crypto.randomUUID()];\n\n\t\t\t\t// First create should succeed\n\t\t\t\tawait client.counter.create(key);\n\n\t\t\t\t// Second create with same key should throw ActorAlreadyExists\n\t\t\t\ttry {\n\t\t\t\t\tawait client.counter.create(key);\n\t\t\t\t\texpect.fail(\"did not error on duplicate create\");\n\t\t\t\t} catch (err) {\n\t\t\t\t\texpect((err as ActorError).group).toBe(\"actor\");\n\t\t\t\t\texpect((err as ActorError).code).toBe(\"already_exists\");\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttest(\".get().resolve() errors for non-existent actor\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst missingId = `nonexistent-${crypto.randomUUID()}`;\n\n\t\t\t\ttry {\n\t\t\t\t\tawait client.counter.get([missingId]).resolve();\n\t\t\t\t\texpect.fail(\"did not error for get().resolve() on missing actor\");\n\t\t\t\t} catch (err) {\n\t\t\t\t\texpect((err as ActorError).group).toBe(\"actor\");\n\t\t\t\t\texpect((err as ActorError).code).toBe(\"not_found\");\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Action Functionality\", () => {\n\t\t\ttest(\"should call actions directly on the handle\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-action-handle\"]);\n\n\t\t\t\t// Call multiple actions in sequence\n\t\t\t\tconst count1 = await handle.increment(3);\n\t\t\t\texpect(count1).toBe(3);\n\n\t\t\t\tconst count2 = await handle.increment(5);\n\t\t\t\texpect(count2).toBe(8);\n\n\t\t\t\tconst retrievedCount = await handle.getCount();\n\t\t\t\texpect(retrievedCount).toBe(8);\n\t\t\t});\n\n\t\t\ttest(\"should handle independent handles to the same actor\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create two handles to the same actor\n\t\t\t\tconst handle1 = client.counter.getOrCreate([\"test-multiple-handles\"]);\n\t\t\t\tconst handle2 = client.counter.get([\"test-multiple-handles\"]);\n\n\t\t\t\t// Call actions on both handles\n\t\t\t\tawait handle1.increment(3);\n\t\t\t\tconst count = await handle2.getCount();\n\n\t\t\t\t// Verify both handles access the same state\n\t\t\t\texpect(count).toBe(3);\n\n\t\t\t\tconst finalCount = await handle2.increment(4);\n\t\t\t\texpect(finalCount).toBe(7);\n\n\t\t\t\tconst checkCount = await handle1.getCount();\n\t\t\t\texpect(checkCount).toBe(7);\n\t\t\t});\n\n\t\t\ttest(\"should resolve a actor's ID\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = client.counter.getOrCreate([\"test-resolve-id\"]);\n\n\t\t\t\t// Call an action to ensure actor exists\n\t\t\t\tawait handle.increment(1);\n\n\t\t\t\t// Resolve the ID\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\t// Verify we got a valid ID (string)\n\t\t\t\texpect(typeof actorId).toBe(\"string\");\n\t\t\t\texpect(actorId).not.toBe(\"\");\n\n\t\t\t\t// Verify we can use this ID to get the actor\n\t\t\t\tconst idHandle = client.counter.getForId(actorId);\n\t\t\t\tconst count = await idHandle.getCount();\n\t\t\t\texpect(count).toBe(1);\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Lifecycle Hooks\", () => {\n\t\t\ttest(\"should trigger lifecycle hooks on actor creation\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Get or create a new actor - this should trigger onStart\n\t\t\t\tconst handle = client.counterWithLifecycle.getOrCreate([\n\t\t\t\t\t\"test-lifecycle-handle\",\n\t\t\t\t]);\n\n\t\t\t\t// Verify onStart was triggered\n\t\t\t\tconst initialEvents = await handle.getEvents();\n\t\t\t\texpect(initialEvents).toContain(\"onStart\");\n\n\t\t\t\t// Create a separate handle to the same actor\n\t\t\t\tconst sameHandle = client.counterWithLifecycle.getOrCreate([\n\t\t\t\t\t\"test-lifecycle-handle\",\n\t\t\t\t]);\n\n\t\t\t\t// Verify events still include onStart but don't duplicate it\n\t\t\t\t// (onStart should only be called once when the actor is first created)\n\t\t\t\tconst events = await sameHandle.getEvents();\n\t\t\t\texpect(events).toContain(\"onStart\");\n\t\t\t\texpect(events.filter((e) => e === \"onStart\").length).toBe(1);\n\t\t\t});\n\n\t\t\ttest(\"should trigger lifecycle hooks for each Action call\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a normal handle to view events\n\t\t\t\tconst viewHandle = client.counterWithLifecycle.getOrCreate([\n\t\t\t\t\t\"test-lifecycle-action\",\n\t\t\t\t]);\n\n\t\t\t\t// Initial state should only have onStart\n\t\t\t\tconst initialEvents = await viewHandle.getEvents();\n\t\t\t\texpect(initialEvents).toContain(\"onStart\");\n\t\t\t\texpect(initialEvents).not.toContain(\"onBeforeConnect\");\n\t\t\t\texpect(initialEvents).not.toContain(\"onConnect\");\n\t\t\t\texpect(initialEvents).not.toContain(\"onDisconnect\");\n\n\t\t\t\t// Create a handle with trackLifecycle enabled for testing Action calls\n\t\t\t\tconst trackingHandle = client.counterWithLifecycle.getOrCreate(\n\t\t\t\t\t[\"test-lifecycle-action\"],\n\t\t\t\t\t{ params: { trackLifecycle: true } },\n\t\t\t\t);\n\n\t\t\t\t// Make an Action call\n\t\t\t\tawait trackingHandle.increment(5);\n\n\t\t\t\t// Check that it triggered the lifecycle hooks\n\t\t\t\tconst eventsAfterAction = await viewHandle.getEvents();\n\n\t\t\t\t// Should have onBeforeConnect, onConnect, and onDisconnect for the Action call\n\t\t\t\texpect(eventsAfterAction).toContain(\"onBeforeConnect\");\n\t\t\t\texpect(eventsAfterAction).toContain(\"onConnect\");\n\t\t\t\texpect(eventsAfterAction).toContain(\"onDisconnect\");\n\n\t\t\t\t// Each should have count 1\n\t\t\t\texpect(\n\t\t\t\t\teventsAfterAction.filter((e) => e === \"onBeforeConnect\").length,\n\t\t\t\t).toBe(1);\n\t\t\t\texpect(eventsAfterAction.filter((e) => e === \"onConnect\").length).toBe(\n\t\t\t\t\t1,\n\t\t\t\t);\n\t\t\t\texpect(\n\t\t\t\t\teventsAfterAction.filter((e) => e === \"onDisconnect\").length,\n\t\t\t\t).toBe(1);\n\n\t\t\t\t// Make another Action call\n\t\t\t\tawait trackingHandle.increment(10);\n\n\t\t\t\t// Check that it triggered another set of lifecycle hooks\n\t\t\t\tconst eventsAfterSecondAction = await viewHandle.getEvents();\n\n\t\t\t\t// Each hook should now have count 2\n\t\t\t\texpect(\n\t\t\t\t\teventsAfterSecondAction.filter((e) => e === \"onBeforeConnect\").length,\n\t\t\t\t).toBe(2);\n\t\t\t\texpect(\n\t\t\t\t\teventsAfterSecondAction.filter((e) => e === \"onConnect\").length,\n\t\t\t\t).toBe(2);\n\t\t\t\texpect(\n\t\t\t\t\teventsAfterSecondAction.filter((e) => e === \"onDisconnect\").length,\n\t\t\t\t).toBe(2);\n\t\t\t});\n\n\t\t\ttest(\"should trigger lifecycle hooks for each Action call across multiple handles\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a normal handle to view events\n\t\t\t\tconst viewHandle = client.counterWithLifecycle.getOrCreate([\n\t\t\t\t\t\"test-lifecycle-multi-handle\",\n\t\t\t\t]);\n\n\t\t\t\t// Create two tracking handles to the same actor\n\t\t\t\tconst trackingHandle1 = client.counterWithLifecycle.getOrCreate(\n\t\t\t\t\t[\"test-lifecycle-multi-handle\"],\n\t\t\t\t\t{ params: { trackLifecycle: true } },\n\t\t\t\t);\n\n\t\t\t\tconst trackingHandle2 = client.counterWithLifecycle.getOrCreate(\n\t\t\t\t\t[\"test-lifecycle-multi-handle\"],\n\t\t\t\t\t{ params: { trackLifecycle: true } },\n\t\t\t\t);\n\n\t\t\t\t// Make Action calls on both handles\n\t\t\t\tawait trackingHandle1.increment(5);\n\t\t\t\tawait trackingHandle2.increment(10);\n\n\t\t\t\t// Check lifecycle hooks\n\t\t\t\tconst events = await viewHandle.getEvents();\n\n\t\t\t\t// Should have 1 onStart, 2 each of onBeforeConnect, onConnect, and onDisconnect\n\t\t\t\texpect(events.filter((e) => e === \"onStart\").length).toBe(1);\n\t\t\t\texpect(events.filter((e) => e === \"onBeforeConnect\").length).toBe(2);\n\t\t\t\texpect(events.filter((e) => e === \"onConnect\").length).toBe(2);\n\t\t\t\texpect(events.filter((e) => e === \"onDisconnect\").length).toBe(2);\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorInlineClientTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Inline Client Tests\", () => {\n\t\tdescribe(\"Stateless Client Calls\", () => {\n\t\t\ttest(\"should make stateless calls to other actors\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create the inline client actor\n\t\t\t\tconst inlineClientHandle = client.inlineClientActor.getOrCreate([\n\t\t\t\t\t\"inline-client-test\",\n\t\t\t\t]);\n\n\t\t\t\t// Test calling counter.increment via inline client\n\t\t\t\tconst result = await inlineClientHandle.callCounterIncrement(5);\n\t\t\t\texpect(result).toBe(5);\n\n\t\t\t\t// Verify the counter state was actually updated\n\t\t\t\tconst counterState = await inlineClientHandle.getCounterState();\n\t\t\t\texpect(counterState).toBe(5);\n\n\t\t\t\t// Check that messages were logged\n\t\t\t\tconst messages = await inlineClientHandle.getMessages();\n\t\t\t\texpect(messages).toHaveLength(2);\n\t\t\t\texpect(messages[0]).toContain(\"Called counter.increment(5), result: 5\");\n\t\t\t\texpect(messages[1]).toContain(\"Got counter state: 5\");\n\t\t\t});\n\n\t\t\ttest(\"should handle multiple stateless calls\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create the inline client actor\n\t\t\t\tconst inlineClientHandle = client.inlineClientActor.getOrCreate([\n\t\t\t\t\t\"inline-client-multi\",\n\t\t\t\t]);\n\n\t\t\t\t// Clear any existing messages\n\t\t\t\tawait inlineClientHandle.clearMessages();\n\n\t\t\t\t// Make multiple calls\n\t\t\t\tconst result1 = await inlineClientHandle.callCounterIncrement(3);\n\t\t\t\tconst result2 = await inlineClientHandle.callCounterIncrement(7);\n\t\t\t\tconst finalState = await inlineClientHandle.getCounterState();\n\n\t\t\t\texpect(result1).toBe(3);\n\t\t\t\texpect(result2).toBe(10); // 3 + 7\n\t\t\t\texpect(finalState).toBe(10);\n\n\t\t\t\t// Check messages\n\t\t\t\tconst messages = await inlineClientHandle.getMessages();\n\t\t\t\texpect(messages).toHaveLength(3);\n\t\t\t\texpect(messages[0]).toContain(\"Called counter.increment(3), result: 3\");\n\t\t\t\texpect(messages[1]).toContain(\n\t\t\t\t\t\"Called counter.increment(7), result: 10\",\n\t\t\t\t);\n\t\t\t\texpect(messages[2]).toContain(\"Got counter state: 10\");\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Stateful Client Calls\", () => {\n\t\t\ttest(\"should connect to other actors and receive events\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create the inline client actor\n\t\t\t\tconst inlineClientHandle = client.inlineClientActor.getOrCreate([\n\t\t\t\t\t\"inline-client-stateful\",\n\t\t\t\t]);\n\n\t\t\t\t// Clear any existing messages\n\t\t\t\tawait inlineClientHandle.clearMessages();\n\n\t\t\t\t// Test stateful connection with events\n\t\t\t\tconst result = await inlineClientHandle.connectToCounterAndIncrement(4);\n\n\t\t\t\texpect(result.result1).toBe(4);\n\t\t\t\texpect(result.result2).toBe(12); // 4 + 8\n\t\t\t\texpect(result.events).toEqual([4, 12]); // Should have received both events\n\n\t\t\t\t// Check that message was logged\n\t\t\t\tconst messages = await inlineClientHandle.getMessages();\n\t\t\t\texpect(messages).toHaveLength(1);\n\t\t\t\texpect(messages[0]).toContain(\n\t\t\t\t\t\"Connected to counter, incremented by 4 and 8\",\n\t\t\t\t);\n\t\t\t\texpect(messages[0]).toContain(\"results: 4, 12\");\n\t\t\t\texpect(messages[0]).toContain(\"events: [4,12]\");\n\t\t\t});\n\n\t\t\ttest(\"should handle stateful connection independently\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create the inline client actor\n\t\t\t\tconst inlineClientHandle = client.inlineClientActor.getOrCreate([\n\t\t\t\t\t\"inline-client-independent\",\n\t\t\t\t]);\n\n\t\t\t\t// Clear any existing messages\n\t\t\t\tawait inlineClientHandle.clearMessages();\n\n\t\t\t\t// Test with different increment values\n\t\t\t\tconst result = await inlineClientHandle.connectToCounterAndIncrement(2);\n\n\t\t\t\texpect(result.result1).toBe(2);\n\t\t\t\texpect(result.result2).toBe(6); // 2 + 4\n\t\t\t\texpect(result.events).toEqual([2, 6]);\n\n\t\t\t\t// Verify the state is independent from previous tests\n\t\t\t\tconst messages = await inlineClientHandle.getMessages();\n\t\t\t\texpect(messages).toHaveLength(1);\n\t\t\t\texpect(messages[0]).toContain(\n\t\t\t\t\t\"Connected to counter, incremented by 2 and 4\",\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Mixed Client Usage\", () => {\n\t\t\ttest(\"should handle both stateless and stateful calls\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create the inline client actor\n\t\t\t\tconst inlineClientHandle = client.inlineClientActor.getOrCreate([\n\t\t\t\t\t\"inline-client-mixed\",\n\t\t\t\t]);\n\n\t\t\t\t// Clear any existing messages\n\t\t\t\tawait inlineClientHandle.clearMessages();\n\n\t\t\t\t// Start with stateless calls\n\t\t\t\tawait inlineClientHandle.callCounterIncrement(1);\n\t\t\t\tconst statelessResult = await inlineClientHandle.getCounterState();\n\t\t\t\texpect(statelessResult).toBe(1);\n\n\t\t\t\t// Then do stateful call\n\t\t\t\tconst statefulResult =\n\t\t\t\t\tawait inlineClientHandle.connectToCounterAndIncrement(3);\n\t\t\t\texpect(statefulResult.result1).toBe(3);\n\t\t\t\texpect(statefulResult.result2).toBe(9); // 3 + 6\n\n\t\t\t\t// Check all messages were logged\n\t\t\t\tconst messages = await inlineClientHandle.getMessages();\n\t\t\t\texpect(messages).toHaveLength(3);\n\t\t\t\texpect(messages[0]).toContain(\"Called counter.increment(1), result: 1\");\n\t\t\t\texpect(messages[1]).toContain(\"Got counter state: 1\");\n\t\t\t\texpect(messages[2]).toContain(\n\t\t\t\t\t\"Connected to counter, incremented by 3 and 6\",\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport { HEADER_ACTOR_QUERY } from \"@/driver-helpers/mod\";\nimport {\n\tcreateActorInspectorClient,\n\tcreateManagerInspectorClient,\n} from \"@/inspector/mod\";\nimport type { ActorQuery } from \"@/mod\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorInspectorTests(driverTestConfig: DriverTestConfig) {\n\t// TODO: Add back\n\tdescribe.skip(\"Actor Inspector Tests\", () => {\n\t\tdescribe(\"Manager Inspector\", () => {\n\t\t\ttest(\"should respond to ping\", async (c) => {\n\t\t\t\tconst { endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.ping.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual({ message: \"pong\" });\n\t\t\t});\n\n\t\t\ttest(\"should get actors with pagination\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create some actors first\n\t\t\t\tawait client.counter.create([\"test-actor-1\"]);\n\t\t\t\tawait client.counter.create([\"test-actor-2\"]);\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.actors.$get({\n\t\t\t\t\tquery: { limit: \"1\" },\n\t\t\t\t});\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual(\n\t\t\t\t\texpect.arrayContaining([\n\t\t\t\t\t\texpect.objectContaining({ key: [\"test-actor-1\"] }),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t\texpect(data.length).toBe(1);\n\t\t\t});\n\n\t\t\ttest(\"should get all actors with pagination\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst actorKey1 = [\"test-cursor-1\"];\n\t\t\t\tconst actorKey2 = [\"test-cursor-2\"];\n\n\t\t\t\t// Create some actors first\n\t\t\t\tawait client.counter.create(actorKey1);\n\t\t\t\tawait client.counter.create(actorKey2);\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.actors.$get({\n\t\t\t\t\tquery: { limit: \"5\" },\n\t\t\t\t});\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual(\n\t\t\t\t\texpect.arrayContaining([\n\t\t\t\t\t\texpect.objectContaining({\n\t\t\t\t\t\t\tid: expect.any(String),\n\t\t\t\t\t\t\tkey: actorKey1,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\texpect.objectContaining({\n\t\t\t\t\t\t\tid: expect.any(String),\n\t\t\t\t\t\t\tkey: actorKey2,\n\t\t\t\t\t\t}),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\ttest(\"should handle invalid limit parameter\", async (c) => {\n\t\t\t\tconst { endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.actors.$get({\n\t\t\t\t\tquery: { limit: \"0\" },\n\t\t\t\t});\n\t\t\t\texpect(response.status).toBe(400);\n\t\t\t});\n\n\t\t\ttest(\"should create a new actor\", async (c) => {\n\t\t\t\tconst { endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.actors.$post({\n\t\t\t\t\tjson: {\n\t\t\t\t\t\tname: \"default\",\n\t\t\t\t\t\tkey: [\"test-create-actor\"],\n\t\t\t\t\t\tinput: {},\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\texpect(response.status).toBe(201);\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual(\n\t\t\t\t\texpect.objectContaining({\n\t\t\t\t\t\tid: expect.any(String),\n\t\t\t\t\t\tname: \"default\",\n\t\t\t\t\t\tkey: [\"test-create-actor\"],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\ttest(\"should get builds\", async (c) => {\n\t\t\t\tconst { endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.builds.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual(\n\t\t\t\t\texpect.arrayContaining([\n\t\t\t\t\t\texpect.objectContaining({ name: expect.any(String) }),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\ttest(\"should get actor by id\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create an actor and get its ID\n\t\t\t\tconst handle = await client.counter.create([\"test-get-by-id\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.actor[\":id\"].$get({\n\t\t\t\t\tparam: { id: actorId },\n\t\t\t\t});\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toHaveProperty(\"id\", actorId);\n\t\t\t});\n\n\t\t\ttest(\"should return 404 for non-existent actor\", async (c) => {\n\t\t\t\tconst { endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.actor[\":id\"].$get({\n\t\t\t\t\tparam: { id: \"non-existent-id\" },\n\t\t\t\t});\n\t\t\t\texpect(response.status).toBe(404);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual({ error: \"Actor not found\" });\n\t\t\t});\n\n\t\t\ttest(\"should get bootstrap data\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create at least one actor to ensure bootstrap has data\n\t\t\t\t// Create an actor and get its ID\n\t\t\t\tconst handle = await client.counter.create([\"test-bootstrap\"]);\n\t\t\t\tawait handle.resolve();\n\n\t\t\t\tconst http = createManagerInspectorClient(`${endpoint}/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.bootstrap.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data.actors).toEqual(\n\t\t\t\t\texpect.arrayContaining([\n\t\t\t\t\t\texpect.objectContaining({\n\t\t\t\t\t\t\tkey: [\"test-bootstrap\"],\n\t\t\t\t\t\t\tname: \"counter\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Actor Inspector\", () => {\n\t\t\ttest(\"should handle actor not found\", async (c) => {\n\t\t\t\tconst { endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst actorId = \"non-existing\";\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.ping.$get();\n\t\t\t\texpect(response.ok).toBe(false);\n\t\t\t});\n\t\t\ttest(\"should respond to ping\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-ping\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.ping.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual({ message: \"pong\" });\n\t\t\t});\n\n\t\t\ttest(\"should get actor state\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-state\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\t// Increment the counter to set some state\n\t\t\t\tawait handle.increment(5);\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.state.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual({\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tstate: expect.objectContaining({\n\t\t\t\t\t\tcount: 5,\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t});\n\n\t\t\ttest(\"should update actor state with replace\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-state-replace\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\t// Replace the entire state\n\t\t\t\tconst response = await http.state.$patch({\n\t\t\t\t\tjson: {\n\t\t\t\t\t\treplace: { count: 10 },\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual({\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tstate: { count: 10 },\n\t\t\t\t});\n\t\t\t});\n\n\t\t\ttest(\"should update actor state with patch\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-state-patch\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\t// Set initial state\n\t\t\t\tawait handle.increment(3);\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\t// Patch the state\n\t\t\t\tconst response = await http.state.$patch({\n\t\t\t\t\tjson: {\n\t\t\t\t\t\tpatch: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\top: \"replace\",\n\t\t\t\t\t\t\t\tpath: \"/count\",\n\t\t\t\t\t\t\t\tvalue: 7,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual({\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tstate: expect.objectContaining({\n\t\t\t\t\t\tcount: 7,\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t});\n\n\t\t\ttest(\"should get actor connections\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-connections\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\t\t\t\thandle.connect();\n\t\t\t\tawait handle.increment(10);\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.connections.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data.connections).toEqual(\n\t\t\t\t\texpect.arrayContaining([\n\t\t\t\t\t\texpect.objectContaining({\n\t\t\t\t\t\t\tid: expect.any(String),\n\t\t\t\t\t\t}),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\ttest(\"should get actor events\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-events\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\thandle.connect();\n\t\t\t\tawait handle.increment(10);\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.events.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data.events).toEqual(\n\t\t\t\t\texpect.arrayContaining([\n\t\t\t\t\t\texpect.objectContaining({\n\t\t\t\t\t\t\ttype: \"broadcast\",\n\t\t\t\t\t\t\tid: expect.any(String),\n\t\t\t\t\t\t}),\n\t\t\t\t\t]),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\ttest(\"should clear actor events\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-events-clear\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\thandle.connect();\n\t\t\t\tawait handle.increment(10);\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\t{\n\t\t\t\t\tconst response = await http.events.$get();\n\t\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\t\tconst data = await response.json();\n\t\t\t\t\texpect(data.events).toEqual(\n\t\t\t\t\t\texpect.arrayContaining([\n\t\t\t\t\t\t\texpect.objectContaining({\n\t\t\t\t\t\t\t\ttype: \"broadcast\",\n\t\t\t\t\t\t\t\tid: expect.any(String),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t]),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst response = await http.events.clear.$post();\n\t\t\t\texpect(response.status).toBe(200);\n\t\t\t});\n\n\t\t\ttest(\"should get actor rpcs\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-rpcs\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.rpcs.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\texpect(data).toEqual(\n\t\t\t\t\texpect.objectContaining({\n\t\t\t\t\t\trpcs: expect.arrayContaining([\"increment\", \"getCount\"]),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t});\n\n\t\t\t// database is not officially supported yet\n\t\t\ttest.skip(\"should get actor database info\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-db\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst response = await http.db.$get();\n\t\t\t\texpect(response.status).toBe(200);\n\n\t\t\t\tconst data = await response.json();\n\t\t\t\t// Database might be enabled or disabled depending on actor configuration\n\t\t\t\texpect(data).toHaveProperty(\"enabled\");\n\t\t\t\texpect(typeof data.enabled).toBe(\"boolean\");\n\n\t\t\t\tif (data.enabled) {\n\t\t\t\t\texpect(data).toHaveProperty(\"db\");\n\t\t\t\t\texpect(Array.isArray(data.db)).toBe(true);\n\t\t\t\t} else {\n\t\t\t\t\texpect(data.db).toBe(null);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttest.skip(\"should execute database query when database is enabled\", async (c) => {\n\t\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst handle = await client.counter.create([\"test-db-query\"]);\n\t\t\t\tconst actorId = await handle.resolve();\n\n\t\t\t\tconst http = createActorInspectorClient(`${endpoint}/actors/inspect`, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer token`,\n\t\t\t\t\t\t[HEADER_ACTOR_QUERY]: JSON.stringify({\n\t\t\t\t\t\t\tgetForId: { name: \"counter\", actorId },\n\t\t\t\t\t\t} satisfies ActorQuery),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\t// First check if database is enabled\n\t\t\t\tconst dbInfoResponse = await http.db.$get();\n\t\t\t\tconst dbInfo = await dbInfoResponse.json();\n\n\t\t\t\tif (dbInfo.enabled) {\n\t\t\t\t\t// Execute a simple query\n\t\t\t\t\tconst queryResponse = await http.db.$post({\n\t\t\t\t\t\tjson: {\n\t\t\t\t\t\t\tquery: \"SELECT 1 as test\",\n\t\t\t\t\t\t\tparams: [],\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\texpect(queryResponse.status).toBe(200);\n\n\t\t\t\t\tconst queryData = await queryResponse.json();\n\t\t\t\t\texpect(queryData).toHaveProperty(\"result\");\n\t\t\t\t} else {\n\t\t\t\t\t// If database is not enabled, the POST should return enabled: false\n\t\t\t\t\tconst queryResponse = await http.db.$post({\n\t\t\t\t\t\tjson: {\n\t\t\t\t\t\t\tquery: \"SELECT 1 as test\",\n\t\t\t\t\t\t\tparams: [],\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\texpect(queryResponse.status).toBe(200);\n\n\t\t\t\t\tconst queryData = await queryResponse.json();\n\t\t\t\t\texpect(queryData).toEqual({ enabled: false });\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorMetadataTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Metadata Tests\", () => {\n\t\tdescribe(\"Actor Name\", () => {\n\t\t\ttest(\"should provide access to actor name\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Get the actor name\n\t\t\t\tconst handle = client.metadataActor.getOrCreate();\n\t\t\t\tconst actorName = await handle.getActorName();\n\n\t\t\t\t// Verify it matches the expected name\n\t\t\t\texpect(actorName).toBe(\"metadataActor\");\n\t\t\t});\n\n\t\t\ttest(\"should preserve actor name in state during onStart\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Get the stored actor name\n\t\t\t\tconst handle = client.metadataActor.getOrCreate();\n\t\t\t\tconst storedName = await handle.getStoredActorName();\n\n\t\t\t\t// Verify it was stored correctly\n\t\t\t\texpect(storedName).toBe(\"metadataActor\");\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Actor Tags\", () => {\n\t\t\ttest(\"should provide access to tags\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and set up test tags\n\t\t\t\tconst handle = client.metadataActor.getOrCreate();\n\t\t\t\tawait handle.setupTestTags({\n\t\t\t\t\tenv: \"test\",\n\t\t\t\t\tpurpose: \"metadata-test\",\n\t\t\t\t});\n\n\t\t\t\t// Get the tags\n\t\t\t\tconst tags = await handle.getTags();\n\n\t\t\t\t// Verify the tags are accessible\n\t\t\t\texpect(tags).toHaveProperty(\"env\");\n\t\t\t\texpect(tags.env).toBe(\"test\");\n\t\t\t\texpect(tags).toHaveProperty(\"purpose\");\n\t\t\t\texpect(tags.purpose).toBe(\"metadata-test\");\n\t\t\t});\n\n\t\t\ttest(\"should allow accessing individual tags\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and set up test tags\n\t\t\t\tconst handle = client.metadataActor.getOrCreate();\n\t\t\t\tawait handle.setupTestTags({\n\t\t\t\t\tcategory: \"test-actor\",\n\t\t\t\t\tversion: \"1.0\",\n\t\t\t\t});\n\n\t\t\t\t// Get individual tags\n\t\t\t\tconst category = await handle.getTag(\"category\");\n\t\t\t\tconst version = await handle.getTag(\"version\");\n\t\t\t\tconst nonexistent = await handle.getTag(\"nonexistent\");\n\n\t\t\t\t// Verify the tag values\n\t\t\t\texpect(category).toBe(\"test-actor\");\n\t\t\t\texpect(version).toBe(\"1.0\");\n\t\t\t\texpect(nonexistent).toBeNull();\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Metadata Structure\", () => {\n\t\t\ttest(\"should provide complete metadata object\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and set up test metadata\n\t\t\t\tconst handle = client.metadataActor.getOrCreate();\n\t\t\t\tawait handle.setupTestTags({ type: \"metadata-test\" });\n\t\t\t\tawait handle.setupTestRegion(\"us-west-1\");\n\n\t\t\t\t// Get all metadata\n\t\t\t\tconst metadata = await handle.getMetadata();\n\n\t\t\t\t// Verify structure of metadata\n\t\t\t\texpect(metadata).toHaveProperty(\"name\");\n\t\t\t\texpect(metadata.name).toBe(\"metadataActor\");\n\n\t\t\t\texpect(metadata).toHaveProperty(\"tags\");\n\t\t\t\texpect(metadata.tags).toHaveProperty(\"type\");\n\t\t\t\texpect(metadata.tags.type).toBe(\"metadata-test\");\n\n\t\t\t\t// Region should be set to our test value\n\t\t\t\texpect(metadata).toHaveProperty(\"region\");\n\t\t\t\texpect(metadata.region).toBe(\"us-west-1\");\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Region Information\", () => {\n\t\t\ttest(\"should retrieve region information\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor and set up test region\n\t\t\t\tconst handle = client.metadataActor.getOrCreate();\n\t\t\t\tawait handle.setupTestRegion(\"eu-central-1\");\n\n\t\t\t\t// Get the region\n\t\t\t\tconst region = await handle.getRegion();\n\n\t\t\t\t// Verify the region is set correctly\n\t\t\t\texpect(region).toBe(\"eu-central-1\");\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"@/driver-test-suite/mod\";\nimport { setupDriverTest } from \"@/driver-test-suite/utils\";\n\nexport function runActorOnStateChangeTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor onStateChange Tests\", () => {\n\t\ttest(\"triggers onStateChange when state is modified\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\tconst actor = client.onStateChangeActor.getOrCreate();\n\n\t\t\t// Modify state - should trigger onChange\n\t\t\tawait actor.setValue(10);\n\n\t\t\t// Check that onChange was called\n\t\t\tconst changeCount = await actor.getChangeCount();\n\t\t\texpect(changeCount).toBe(1);\n\t\t});\n\n\t\ttest(\"triggers onChange multiple times for multiple state changes\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\tconst actor = client.onStateChangeActor.getOrCreate();\n\n\t\t\t// Modify state multiple times\n\t\t\tawait actor.incrementMultiple(3);\n\n\t\t\t// Check that onChange was called for each modification\n\t\t\tconst changeCount = await actor.getChangeCount();\n\t\t\texpect(changeCount).toBe(3);\n\t\t});\n\n\t\ttest(\"does NOT trigger onChange for read-only actions\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\tconst actor = client.onStateChangeActor.getOrCreate();\n\n\t\t\t// Set initial value\n\t\t\tawait actor.setValue(5);\n\n\t\t\t// Read value without modifying - should NOT trigger onChange\n\t\t\tconst value = await actor.getValue();\n\t\t\texpect(value).toBe(5);\n\n\t\t\t// Check that onChange was NOT called\n\t\t\tconst changeCount = await actor.getChangeCount();\n\t\t\texpect(changeCount).toBe(1);\n\t\t});\n\n\t\ttest(\"does NOT trigger onChange for computed values\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\tconst actor = client.onStateChangeActor.getOrCreate();\n\n\t\t\t// Set initial value\n\t\t\tawait actor.setValue(3);\n\n\t\t\t// Check that onChange was called\n\t\t\t{\n\t\t\t\tconst changeCount = await actor.getChangeCount();\n\t\t\t\texpect(changeCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Compute value without modifying state - should NOT trigger onChange\n\t\t\tconst doubled = await actor.getDoubled();\n\t\t\texpect(doubled).toBe(6);\n\n\t\t\t// Check that onChange was NOT called\n\t\t\t{\n\t\t\t\tconst changeCount = await actor.getChangeCount();\n\t\t\t\texpect(changeCount).toBe(1);\n\t\t\t}\n\t\t});\n\n\t\ttest(\"simple: connect, call action, dispose does NOT trigger onChange\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\tconst actor = client.onStateChangeActor.getOrCreate();\n\n\t\t\t// Connect to the actor\n\t\t\tconst connection = await actor.connect();\n\n\t\t\t// Call an action that doesn't modify state\n\t\t\tconst value = await connection.getValue();\n\t\t\texpect(value).toBe(0);\n\n\t\t\t// Dispose the connection\n\t\t\tawait connection.dispose();\n\n\t\t\t// Verify that onChange was NOT triggered\n\t\t\tconst changeCount = await actor.getChangeCount();\n\t\t\texpect(changeCount).toBe(0);\n\t\t});\n\t});\n}\n","import { describe, expect, test, vi } from \"vitest\";\nimport type { ActorConnRaw } from \"@/client/actor-conn\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorReconnectTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Reconnection Tests\", () => {\n\t\ttest(\"should reconnect and preserve connection state after non-clean disconnect\", async (c) => {\n\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor and connect\n\t\t\tconst handle = client.counterConn.getOrCreate([\"test-reconnect\"]);\n\t\t\tconst connection = handle.connect();\n\n\t\t\t// Set an initial count on the connection\n\t\t\tawait connection.increment(5);\n\n\t\t\t// Verify connection count is 1\n\t\t\tconst connCount1 = await connection.getConnectionCount();\n\t\t\texpect(connCount1).toBe(1);\n\n\t\t\t// Force disconnect (non-clean) - simulates network failure\n\t\t\tconst connRaw = connection as unknown as ActorConnRaw;\n\t\t\tawait forceUncleanDisconnect(\n\t\t\t\tendpoint,\n\t\t\t\tconnRaw.actorId!,\n\t\t\t\tconnRaw.connectionId!,\n\t\t\t);\n\n\t\t\t// Wait a bit for the disconnection to be processed\n\t\t\tawait vi.waitFor(\n\t\t\t\tasync () => {\n\t\t\t\t\tconst countAfterReconnect = await connection.getCount();\n\t\t\t\t\texpect(countAfterReconnect).toBe(5); // Should preserve the count\n\t\t\t\t},\n\t\t\t\t{ timeout: 5000, interval: 100 },\n\t\t\t);\n\n\t\t\t// Verify connection count is still 1 (same connection reconnected)\n\t\t\tconst connCount2 = await connection.getConnectionCount();\n\t\t\texpect(connCount2).toBe(1);\n\n\t\t\t// Verify we can still increment the counter\n\t\t\tconst newCount = await connection.getCount();\n\t\t\texpect(newCount).toBe(5);\n\n\t\t\t// Clean up\n\t\t\tawait connection.dispose();\n\t\t});\n\n\t\ttest(\"should not preserve connection state after clean disconnect\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor and connect\n\t\t\tconst handle = client.counterConn.getOrCreate([\"test-clean-disconnect\"]);\n\t\t\tconst connection = handle.connect();\n\n\t\t\t// Set an initial count on the connection\n\t\t\tawait connection.increment(10);\n\n\t\t\t// Verify connection count is 1\n\t\t\tconst connCount1 = await connection.getConnectionCount();\n\t\t\texpect(connCount1).toBe(1);\n\n\t\t\t// Clean disconnect\n\t\t\tawait connection.dispose();\n\n\t\t\t// Wait a bit to ensure disconnection is processed\n\t\t\tawait vi.waitFor(\n\t\t\t\tasync () => {\n\t\t\t\t\t// Check that connection count is now 0\n\t\t\t\t\tconst handle2 = client.counterConn.get([\"test-clean-disconnect\"]);\n\t\t\t\t\tconst connCount = await handle2.getConnectionCount();\n\t\t\t\t\t// This counts the current action caller\n\t\t\t\t\texpect(connCount).toBe(1);\n\t\t\t\t},\n\t\t\t\t{ timeout: 5000 },\n\t\t\t);\n\n\t\t\t// Create a new connection\n\t\t\tconst connection2 = handle.connect();\n\n\t\t\t// The count should be reset since it's a new connection\n\t\t\tconst countNewConnection = await connection2.getCount();\n\t\t\texpect(countNewConnection).toBe(0); // Should be reset\n\n\t\t\t// Verify connection count is 1 again (new connection)\n\t\t\tconst connCount3 = await connection2.getConnectionCount();\n\t\t\texpect(connCount3).toBe(1);\n\n\t\t\t// Clean up\n\t\t\tawait connection2.dispose();\n\t\t});\n\n\t\ttest(\"should handle multiple non-clean disconnects and reconnects\", async (c) => {\n\t\t\tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor and connect\n\t\t\tconst handle = client.counterConn.getOrCreate([\n\t\t\t\t\"test-multiple-reconnect\",\n\t\t\t]);\n\t\t\tconst connection = handle.connect();\n\n\t\t\t// Set an initial count\n\t\t\tawait connection.setCount(100);\n\n\t\t\t// Perform multiple disconnect-reconnect cycles\n\t\t\tfor (let i = 0; i < 3; i++) {\n\t\t\t\t// Increment before disconnect\n\t\t\t\tawait connection.increment(1);\n\n\t\t\t\t// Force disconnect\n\t\t\t\tconst connRaw = connection as unknown as ActorConnRaw;\n\t\t\t\tawait forceUncleanDisconnect(\n\t\t\t\t\tendpoint,\n\t\t\t\t\tconnRaw.actorId!,\n\t\t\t\t\tconnRaw.connectionId!,\n\t\t\t\t);\n\n\t\t\t\t// Wait for reconnection and verify state is preserved\n\t\t\t\tawait vi.waitFor(\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tconst countAfter = await connection.getCount();\n\t\t\t\t\t\texpect(countAfter).toBe(101 + i);\n\t\t\t\t\t},\n\t\t\t\t\t{ timeout: 5000 },\n\t\t\t\t);\n\n\t\t\t\t// Verify connection count remains 1\n\t\t\t\tconst connCount = await connection.getConnectionCount();\n\t\t\t\texpect(connCount).toBe(1);\n\t\t\t}\n\n\t\t\t// Final verification\n\t\t\tconst finalCount = await connection.getCount();\n\t\t\texpect(finalCount).toBe(103);\n\n\t\t\t// Clean up\n\t\t\tawait connection.dispose();\n\t\t});\n\t});\n}\n\nasync function forceUncleanDisconnect(\n\tendpoint: string,\n\tactorId: string,\n\tconnId: string,\n): Promise<void> {\n\tconst response = await fetch(\n\t\t`${endpoint}/.test/force-disconnect?actor=${actorId}&conn=${connId}`,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t},\n\t);\n\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`Failed to force disconnect: ${text}`);\n\t}\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runActorVarsTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Actor Variables\", () => {\n\t\tdescribe(\"Static vars\", () => {\n\t\t\ttest(\"should provide access to static vars\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\tconst instance = client.staticVarActor.getOrCreate();\n\n\t\t\t\t// Test accessing vars\n\t\t\t\tconst result = await instance.getVars();\n\t\t\t\texpect(result).toEqual({ counter: 42, name: \"test-actor\" });\n\n\t\t\t\t// Test accessing specific var property\n\t\t\t\tconst name = await instance.getName();\n\t\t\t\texpect(name).toBe(\"test-actor\");\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Deep cloning of static vars\", () => {\n\t\t\ttest(\"should deep clone static vars between actor instances\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create two separate instances\n\t\t\t\tconst instance1 = client.nestedVarActor.getOrCreate([\"instance1\"]);\n\t\t\t\tconst instance2 = client.nestedVarActor.getOrCreate([\"instance2\"]);\n\n\t\t\t\t// Modify vars in the first instance\n\t\t\t\tconst modifiedVars = await instance1.modifyNested();\n\t\t\t\texpect(modifiedVars.nested.value).toBe(\"modified\");\n\t\t\t\texpect(modifiedVars.nested.array).toContain(4);\n\t\t\t\texpect(modifiedVars.nested.obj.key).toBe(\"new-value\");\n\n\t\t\t\t// Check that the second instance still has the original values\n\t\t\t\tconst instance2Vars = await instance2.getVars();\n\t\t\t\texpect(instance2Vars.nested.value).toBe(\"original\");\n\t\t\t\texpect(instance2Vars.nested.array).toEqual([1, 2, 3]);\n\t\t\t\texpect(instance2Vars.nested.obj.key).toBe(\"value\");\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"createVars\", () => {\n\t\t\ttest(\"should support dynamic vars creation\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create an instance\n\t\t\t\tconst instance = client.dynamicVarActor.getOrCreate();\n\n\t\t\t\t// Test accessing dynamically created vars\n\t\t\t\tconst vars = await instance.getVars();\n\t\t\t\texpect(vars).toHaveProperty(\"random\");\n\t\t\t\texpect(vars).toHaveProperty(\"computed\");\n\t\t\t\texpect(typeof vars.random).toBe(\"number\");\n\t\t\t\texpect(typeof vars.computed).toBe(\"string\");\n\t\t\t\texpect(vars.computed).toMatch(/^Actor-\\d+$/);\n\t\t\t});\n\n\t\t\ttest(\"should create different vars for different instances\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create two separate instances\n\t\t\t\tconst instance1 = client.uniqueVarActor.getOrCreate([\"test1\"]);\n\t\t\t\tconst instance2 = client.uniqueVarActor.getOrCreate([\"test2\"]);\n\n\t\t\t\t// Get vars from both instances\n\t\t\t\tconst vars1 = await instance1.getVars();\n\t\t\t\tconst vars2 = await instance2.getVars();\n\n\t\t\t\t// Verify they have different values\n\t\t\t\texpect(vars1.id).not.toBe(vars2.id);\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Driver Context\", () => {\n\t\t\ttest(\"should provide access to driver context\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create an instance\n\t\t\t\tconst instance = client.driverCtxActor.getOrCreate();\n\n\t\t\t\t// Test accessing driver context through vars\n\t\t\t\tconst vars = await instance.getVars();\n\n\t\t\t\t// Driver context might or might not be available depending on the driver\n\t\t\t\t// But the test should run without errors\n\t\t\t\texpect(vars).toHaveProperty(\"hasDriverCtx\");\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { ActorError } from \"@/client/mod\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runManagerDriverTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Manager Driver Tests\", () => {\n\t\tdescribe(\"Client Connection Methods\", () => {\n\t\t\ttest(\"connect() - finds or creates a actor\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Basic connect() with no parameters creates a default actor\n\t\t\t\tconst counterA = client.counter.getOrCreate();\n\t\t\t\tawait counterA.increment(5);\n\n\t\t\t\t// Get the same actor again to verify state persisted\n\t\t\t\tconst counterAAgain = client.counter.getOrCreate();\n\t\t\t\tconst count = await counterAAgain.increment(0);\n\t\t\t\texpect(count).toBe(5);\n\n\t\t\t\t// Connect with key creates a new actor with specific parameters\n\t\t\t\tconst counterB = client.counter.getOrCreate([\"counter-b\", \"testing\"]);\n\n\t\t\t\tawait counterB.increment(10);\n\t\t\t\tconst countB = await counterB.increment(0);\n\t\t\t\texpect(countB).toBe(10);\n\t\t\t});\n\n\t\t\ttest(\"throws ActorAlreadyExists when creating duplicate actors\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a unique actor with specific key\n\t\t\t\tconst uniqueKey = [\"duplicate-actor-test\", crypto.randomUUID()];\n\t\t\t\tconst counter = client.counter.getOrCreate(uniqueKey);\n\t\t\t\tawait counter.increment(5);\n\n\t\t\t\t// Expect duplicate actor\n\t\t\t\ttry {\n\t\t\t\t\tawait client.counter.create(uniqueKey);\n\t\t\t\t\texpect.fail(\"did not error on duplicate create\");\n\t\t\t\t} catch (err) {\n\t\t\t\t\texpect((err as ActorError).group).toBe(\"actor\");\n\t\t\t\t\texpect((err as ActorError).code).toBe(\"already_exists\");\n\t\t\t\t}\n\n\t\t\t\t// Verify the original actor still works and has its state\n\t\t\t\tconst count = await counter.increment(0);\n\t\t\t\texpect(count).toBe(5);\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Connection Options\", () => {\n\t\t\ttest(\"get without create prevents actor creation\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Try to get a nonexistent actor with no create\n\t\t\t\tconst nonexistentId = `nonexistent-${crypto.randomUUID()}`;\n\n\t\t\t\t// Should fail when actor doesn't exist\n\t\t\t\ttry {\n\t\t\t\t\tawait client.counter.get([nonexistentId]).resolve();\n\t\t\t\t\texpect.fail(\"did not error for get\");\n\t\t\t\t} catch (err) {\n\t\t\t\t\texpect((err as ActorError).group).toBe(\"actor\");\n\t\t\t\t\texpect((err as ActorError).code).toBe(\"not_found\");\n\t\t\t\t}\n\n\t\t\t\t// Create the actor\n\t\t\t\tconst createdCounter = client.counter.getOrCreate(nonexistentId);\n\t\t\t\tawait createdCounter.increment(3);\n\n\t\t\t\t// Now no create should work since the actor exists\n\t\t\t\tconst retrievedCounter = client.counter.get(nonexistentId);\n\n\t\t\t\tconst count = await retrievedCounter.increment(0);\n\t\t\t\texpect(count).toBe(3);\n\t\t\t});\n\n\t\t\ttest(\"connection params are passed to actors\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a actor with connection params\n\t\t\t\t// Note: In a real test we'd verify these are received by the actor,\n\t\t\t\t// but our simple counter actor doesn't use connection params.\n\t\t\t\t// This test just ensures the params are accepted by the driver.\n\t\t\t\tconst counter = client.counter.getOrCreate(undefined, {\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tuserId: \"user-123\",\n\t\t\t\t\t\tauthToken: \"token-abc\",\n\t\t\t\t\t\tsettings: { increment: 5 },\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tawait counter.increment(1);\n\t\t\t\tconst count = await counter.increment(0);\n\t\t\t\texpect(count).toBe(1);\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Actor Creation & Retrieval\", () => {\n\t\t\ttest(\"creates and retrieves actors by ID\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a unique ID for this test\n\t\t\t\tconst uniqueId = `test-counter-${crypto.randomUUID()}`;\n\n\t\t\t\t// Create actor with specific ID\n\t\t\t\tconst counter = client.counter.getOrCreate([uniqueId]);\n\t\t\t\tawait counter.increment(10);\n\n\t\t\t\t// Retrieve the same actor by ID and verify state\n\t\t\t\tconst retrievedCounter = client.counter.getOrCreate([uniqueId]);\n\t\t\t\tconst count = await retrievedCounter.increment(0); // Get current value\n\t\t\t\texpect(count).toBe(10);\n\t\t\t});\n\n\t\t\ttest(\"passes input to actor during creation\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Test data to pass as input\n\t\t\t\tconst testInput = {\n\t\t\t\t\tname: \"test-actor\",\n\t\t\t\t\tvalue: 42,\n\t\t\t\t\tnested: { foo: \"bar\" },\n\t\t\t\t};\n\n\t\t\t\t// Create actor with input\n\t\t\t\tconst actor = await client.inputActor.create(undefined, {\n\t\t\t\t\tinput: testInput,\n\t\t\t\t});\n\n\t\t\t\t// Verify both createState and onCreate received the input\n\t\t\t\tconst inputs = await actor.getInputs();\n\n\t\t\t\t// Input should be available in createState\n\t\t\t\texpect(inputs.initialInput).toEqual(testInput);\n\n\t\t\t\t// Input should also be available in onCreate lifecycle hook\n\t\t\t\texpect(inputs.onCreateInput).toEqual(testInput);\n\t\t\t});\n\n\t\t\ttest(\"input is undefined when not provided\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor without providing input\n\t\t\t\tconst actor = await client.inputActor.create();\n\n\t\t\t\t// Get inputs and verify they're undefined\n\t\t\t\tconst inputs = await actor.getInputs();\n\n\t\t\t\t// Should be undefined in createState\n\t\t\t\texpect(inputs.initialInput).toBeUndefined();\n\n\t\t\t\t// Should be undefined in onCreate lifecycle hook too\n\t\t\t\texpect(inputs.onCreateInput).toBeUndefined();\n\t\t\t});\n\n\t\t\ttest(\"getOrCreate passes input to actor during creation\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a unique key for this test\n\t\t\t\tconst uniqueKey = [`input-test-${crypto.randomUUID()}`];\n\n\t\t\t\t// Test data to pass as input\n\t\t\t\tconst testInput = {\n\t\t\t\t\tname: \"getorcreate-test\",\n\t\t\t\t\tvalue: 100,\n\t\t\t\t\tnested: { baz: \"qux\" },\n\t\t\t\t};\n\n\t\t\t\t// Use getOrCreate with input\n\t\t\t\tconst actor = client.inputActor.getOrCreate(uniqueKey, {\n\t\t\t\t\tcreateWithInput: testInput,\n\t\t\t\t});\n\n\t\t\t\t// Verify both createState and onCreate received the input\n\t\t\t\tconst inputs = await actor.getInputs();\n\n\t\t\t\t// Input should be available in createState\n\t\t\t\texpect(inputs.initialInput).toEqual(testInput);\n\n\t\t\t\t// Input should also be available in onCreate lifecycle hook\n\t\t\t\texpect(inputs.onCreateInput).toEqual(testInput);\n\n\t\t\t\t// Verify that calling getOrCreate again with the same key\n\t\t\t\t// returns the existing actor and doesn't create a new one\n\t\t\t\tconst existingActor = client.inputActor.getOrCreate(uniqueKey);\n\t\t\t\tconst existingInputs = await existingActor.getInputs();\n\n\t\t\t\t// Should still have the original inputs\n\t\t\t\texpect(existingInputs.initialInput).toEqual(testInput);\n\t\t\t\texpect(existingInputs.onCreateInput).toEqual(testInput);\n\t\t\t});\n\n\t\t\t// TODO: Correctly test region for each provider\n\t\t\t//test(\"creates and retrieves actors with region\", async (c) => {\n\t\t\t//\tconst { client } = await setupDriverTest(c,\n\t\t\t//\t\tdriverTestConfig,\n\t\t\t//\t\tCOUNTER_APP_PATH\n\t\t\t//\t);\n\t\t\t//\n\t\t\t//\t// Create actor with a specific region\n\t\t\t//\tconst counter = client.counter.getOrCreate({\n\t\t\t//\t\tcreate: {\n\t\t\t//\t\t\tkey: [\"metadata-test\", \"testing\"],\n\t\t\t//\t\t\tregion: \"test-region\",\n\t\t\t//\t\t},\n\t\t\t//\t});\n\t\t\t//\n\t\t\t//\t// Set state to identify this specific instance\n\t\t\t//\tawait counter.increment(42);\n\t\t\t//\n\t\t\t//\t// Retrieve by ID (since metadata is not used for retrieval)\n\t\t\t//\tconst retrievedCounter = client.counter.getOrCreate([\"metadata-test\"]);\n\t\t\t//\n\t\t\t//\t// Verify it's the same instance\n\t\t\t//\tconst count = await retrievedCounter.increment(0);\n\t\t\t//\texpect(count).toBe(42);\n\t\t\t//});\n\t\t});\n\n\t\tdescribe(\"Key Matching\", () => {\n\t\t\ttest(\"matches actors only with exactly the same keys\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor with multiple keys\n\t\t\t\tconst originalCounter = client.counter.getOrCreate([\n\t\t\t\t\t\"counter-match\",\n\t\t\t\t\t\"test\",\n\t\t\t\t\t\"us-east\",\n\t\t\t\t]);\n\t\t\t\tawait originalCounter.increment(10);\n\n\t\t\t\t// Should match with exact same keys\n\t\t\t\tconst exactMatchCounter = client.counter.getOrCreate([\n\t\t\t\t\t\"counter-match\",\n\t\t\t\t\t\"test\",\n\t\t\t\t\t\"us-east\",\n\t\t\t\t]);\n\t\t\t\tconst exactMatchCount = await exactMatchCounter.increment(0);\n\t\t\t\texpect(exactMatchCount).toBe(10);\n\n\t\t\t\t// Should NOT match with subset of keys - should create new actor\n\t\t\t\tconst subsetMatchCounter = client.counter.getOrCreate([\n\t\t\t\t\t\"counter-match\",\n\t\t\t\t\t\"test\",\n\t\t\t\t]);\n\t\t\t\tconst subsetMatchCount = await subsetMatchCounter.increment(0);\n\t\t\t\texpect(subsetMatchCount).toBe(0); // Should be a new counter with 0\n\n\t\t\t\t// Should NOT match with just one key - should create new actor\n\t\t\t\tconst singleKeyCounter = client.counter.getOrCreate([\"counter-match\"]);\n\t\t\t\tconst singleKeyCount = await singleKeyCounter.increment(0);\n\t\t\t\texpect(singleKeyCount).toBe(0); // Should be a new counter with 0\n\t\t\t});\n\n\t\t\ttest(\"string key matches array with single string key\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor with string key\n\t\t\t\tconst stringKeyCounter = client.counter.getOrCreate(\"string-key-test\");\n\t\t\t\tawait stringKeyCounter.increment(7);\n\n\t\t\t\t// Should match with equivalent array key\n\t\t\t\tconst arrayKeyCounter = client.counter.getOrCreate([\"string-key-test\"]);\n\t\t\t\tconst count = await arrayKeyCounter.increment(0);\n\t\t\t\texpect(count).toBe(7);\n\t\t\t});\n\n\t\t\ttest(\"undefined key matches empty array key and no key\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create actor with undefined key\n\t\t\t\tconst undefinedKeyCounter = client.counter.getOrCreate(undefined);\n\t\t\t\tawait undefinedKeyCounter.increment(12);\n\n\t\t\t\t// Should match with empty array key\n\t\t\t\tconst emptyArrayKeyCounter = client.counter.getOrCreate([]);\n\t\t\t\tconst emptyArrayCount = await emptyArrayKeyCounter.increment(0);\n\t\t\t\texpect(emptyArrayCount).toBe(12);\n\n\t\t\t\t// Should match with no key\n\t\t\t\tconst noKeyCounter = client.counter.getOrCreate();\n\t\t\t\tconst noKeyCount = await noKeyCounter.increment(0);\n\t\t\t\texpect(noKeyCount).toBe(12);\n\t\t\t});\n\n\t\t\ttest(\"no keys does not match actors with keys\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create counter with keys\n\t\t\t\tconst keyedCounter = client.counter.getOrCreate([\n\t\t\t\t\t\"counter-with-keys\",\n\t\t\t\t\t\"special\",\n\t\t\t\t]);\n\t\t\t\tawait keyedCounter.increment(15);\n\n\t\t\t\t// Should not match when searching with no keys\n\t\t\t\tconst noKeysCounter = client.counter.getOrCreate();\n\t\t\t\tconst count = await noKeysCounter.increment(10);\n\t\t\t\texpect(count).toBe(10);\n\t\t\t});\n\n\t\t\ttest(\"actors with keys match actors with no keys\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create a counter with no keys\n\t\t\t\tconst noKeysCounter = client.counter.getOrCreate();\n\t\t\t\tawait noKeysCounter.increment(25);\n\n\t\t\t\t// Get counter with keys - should create a new one\n\t\t\t\tconst keyedCounter = client.counter.getOrCreate([\n\t\t\t\t\t\"new-counter\",\n\t\t\t\t\t\"prod\",\n\t\t\t\t]);\n\t\t\t\tconst keyedCount = await keyedCounter.increment(0);\n\n\t\t\t\t// Should be a new counter, not the one created above\n\t\t\t\texpect(keyedCount).toBe(0);\n\t\t\t});\n\t\t});\n\n\t\tdescribe(\"Multiple Actor Instances\", () => {\n\t\t\t// TODO: This test is flakey https://github.com/rivet-dev/rivetkit/issues/873\n\t\t\ttest(\"creates multiple actor instances of the same type\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Create multiple instances with different IDs\n\t\t\t\tconst instance1 = client.counter.getOrCreate([\"multi-1\"]);\n\t\t\t\tconst instance2 = client.counter.getOrCreate([\"multi-2\"]);\n\t\t\t\tconst instance3 = client.counter.getOrCreate([\"multi-3\"]);\n\n\t\t\t\t// Set different states\n\t\t\t\tawait instance1.increment(1);\n\t\t\t\tawait instance2.increment(2);\n\t\t\t\tawait instance3.increment(3);\n\n\t\t\t\t// Retrieve all instances again\n\t\t\t\tconst retrieved1 = client.counter.getOrCreate([\"multi-1\"]);\n\t\t\t\tconst retrieved2 = client.counter.getOrCreate([\"multi-2\"]);\n\t\t\t\tconst retrieved3 = client.counter.getOrCreate([\"multi-3\"]);\n\n\t\t\t\t// Verify separate state\n\t\t\t\texpect(await retrieved1.increment(0)).toBe(1);\n\t\t\t\texpect(await retrieved2.increment(0)).toBe(2);\n\t\t\t\texpect(await retrieved3.increment(0)).toBe(3);\n\t\t\t});\n\n\t\t\ttest(\"handles default instance with no explicit ID\", async (c) => {\n\t\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t\t// Get default instance (no ID specified)\n\t\t\t\tconst defaultCounter = client.counter.getOrCreate();\n\n\t\t\t\t// Set state\n\t\t\t\tawait defaultCounter.increment(5);\n\n\t\t\t\t// Get default instance again\n\t\t\t\tconst sameDefaultCounter = client.counter.getOrCreate();\n\n\t\t\t\t// Verify state is maintained\n\t\t\t\tconst count = await sameDefaultCounter.increment(0);\n\t\t\t\texpect(count).toBe(5);\n\t\t\t});\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runRawHttpTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"raw http\", () => {\n\t\ttest(\"should handle raw HTTP GET requests\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"test\"]);\n\n\t\t\t// Test the hello endpoint\n\t\t\tconst helloResponse = await actor.fetch(\"api/hello\");\n\t\t\texpect(helloResponse.ok).toBe(true);\n\t\t\tconst helloData = await helloResponse.json();\n\t\t\texpect(helloData).toEqual({ message: \"Hello from actor!\" });\n\t\t});\n\n\t\ttest(\"should handle raw HTTP POST requests with echo\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"test\"]);\n\n\t\t\tconst testData = { test: \"data\", number: 123 };\n\t\t\tconst echoResponse = await actor.fetch(\"api/echo\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(testData),\n\t\t\t});\n\n\t\t\texpect(echoResponse.ok).toBe(true);\n\t\t\tconst echoData = await echoResponse.json();\n\t\t\texpect(echoData).toEqual(testData);\n\t\t});\n\n\t\ttest(\"should track state across raw HTTP requests\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"state-test\"]);\n\n\t\t\t// Make a few requests\n\t\t\tawait actor.fetch(\"api/hello\");\n\t\t\tawait actor.fetch(\"api/hello\");\n\t\t\tawait actor.fetch(\"api/state\");\n\n\t\t\t// Check the state endpoint\n\t\t\tconst stateResponse = await actor.fetch(\"api/state\");\n\t\t\texpect(stateResponse.ok).toBe(true);\n\t\t\tconst stateData = (await stateResponse.json()) as {\n\t\t\t\trequestCount: number;\n\t\t\t};\n\t\t\texpect(stateData.requestCount).toBe(4); // 4 total requests\n\t\t});\n\n\t\ttest(\"should pass headers correctly\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"headers-test\"]);\n\n\t\t\tconst customHeaders = {\n\t\t\t\t\"X-Custom-Header\": \"test-value\",\n\t\t\t\t\"X-Another-Header\": \"another-value\",\n\t\t\t};\n\n\t\t\tconst response = await actor.fetch(\"api/headers\", {\n\t\t\t\theaders: customHeaders,\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst headers = (await response.json()) as Record<string, string>;\n\t\t\texpect(headers[\"x-custom-header\"]).toBe(\"test-value\");\n\t\t\texpect(headers[\"x-another-header\"]).toBe(\"another-value\");\n\t\t});\n\n\t\ttest(\"should return 404 for unhandled paths\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"404-test\"]);\n\n\t\t\tconst response = await actor.fetch(\"api/nonexistent\");\n\t\t\texpect(response.ok).toBe(false);\n\t\t\texpect(response.status).toBe(404);\n\t\t});\n\n\t\ttest(\"should return 404 when no onFetch handler defined\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpNoHandlerActor.getOrCreate([\"no-handler\"]);\n\n\t\t\tconst response = await actor.fetch(\"api/anything\");\n\t\t\texpect(response.ok).toBe(false);\n\t\t\texpect(response.status).toBe(404);\n\n\t\t\t// No actions available without onFetch handler\n\t\t});\n\n\t\ttest(\"should return 500 error when onFetch returns void\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpVoidReturnActor.getOrCreate([\"void-return\"]);\n\n\t\t\tconst response = await actor.fetch(\"api/anything\");\n\t\t\texpect(response.ok).toBe(false);\n\t\t\texpect(response.status).toBe(500);\n\n\t\t\t// Check error message - response might be CBOR encoded\n\t\t\ttry {\n\t\t\t\tconst errorData = (await response.json()) as { message: string };\n\t\t\t\texpect(errorData.message).toContain(\n\t\t\t\t\t\"onFetch handler must return a Response\",\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// If JSON parsing fails, just check that we got a 500 error\n\t\t\t\t// The error details are already validated by the status code\n\t\t\t}\n\n\t\t\t// No actions available when onFetch returns void\n\t\t});\n\n\t\ttest(\"should handle different HTTP methods\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"methods-test\"]);\n\n\t\t\t// Test various HTTP methods\n\t\t\tconst methods = [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\"];\n\n\t\t\tfor (const method of methods) {\n\t\t\t\tconst response = await actor.fetch(\"api/echo\", {\n\t\t\t\t\tmethod,\n\t\t\t\t\tbody: [\"POST\", \"PUT\", \"PATCH\"].includes(method)\n\t\t\t\t\t\t? JSON.stringify({ method })\n\t\t\t\t\t\t: undefined,\n\t\t\t\t});\n\n\t\t\t\t// Echo endpoint only handles POST, others should fall through to 404\n\t\t\t\tif (method === \"POST\") {\n\t\t\t\t\texpect(response.ok).toBe(true);\n\t\t\t\t\tconst data = await response.json();\n\t\t\t\t\texpect(data).toEqual({ method });\n\t\t\t\t} else if (method === \"GET\") {\n\t\t\t\t\t// GET to echo should return 404\n\t\t\t\t\texpect(response.status).toBe(404);\n\t\t\t\t} else {\n\t\t\t\t\t// Other methods with body should also return 404\n\t\t\t\t\texpect(response.status).toBe(404);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\ttest(\"should handle binary data\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"binary-test\"]);\n\n\t\t\t// Send binary data\n\t\t\tconst binaryData = new Uint8Array([1, 2, 3, 4, 5]);\n\t\t\tconst response = await actor.fetch(\"api/echo\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/octet-stream\",\n\t\t\t\t},\n\t\t\t\tbody: binaryData,\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst responseBuffer = await response.arrayBuffer();\n\t\t\tconst responseArray = new Uint8Array(responseBuffer);\n\t\t\texpect(Array.from(responseArray)).toEqual([1, 2, 3, 4, 5]);\n\t\t});\n\n\t\ttest(\"should work with Hono router using createVars\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpHonoActor.getOrCreate([\"hono-test\"]);\n\n\t\t\t// Test root endpoint\n\t\t\tconst rootResponse = await actor.fetch(\"/\");\n\t\t\texpect(rootResponse.ok).toBe(true);\n\t\t\tconst rootData = await rootResponse.json();\n\t\t\texpect(rootData).toEqual({ message: \"Welcome to Hono actor!\" });\n\n\t\t\t// Test GET all users\n\t\t\tconst usersResponse = await actor.fetch(\"/users\");\n\t\t\texpect(usersResponse.ok).toBe(true);\n\t\t\tconst users = await usersResponse.json();\n\t\t\texpect(users).toEqual([\n\t\t\t\t{ id: 1, name: \"Alice\" },\n\t\t\t\t{ id: 2, name: \"Bob\" },\n\t\t\t]);\n\n\t\t\t// Test GET single user\n\t\t\tconst userResponse = await actor.fetch(\"/users/1\");\n\t\t\texpect(userResponse.ok).toBe(true);\n\t\t\tconst user = await userResponse.json();\n\t\t\texpect(user).toEqual({ id: 1, name: \"Alice\" });\n\n\t\t\t// Test POST new user\n\t\t\tconst newUser = { name: \"Charlie\" };\n\t\t\tconst createResponse = await actor.fetch(\"/users\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify(newUser),\n\t\t\t});\n\t\t\texpect(createResponse.ok).toBe(true);\n\t\t\texpect(createResponse.status).toBe(201);\n\t\t\tconst createdUser = await createResponse.json();\n\t\t\texpect(createdUser).toEqual({ id: 3, name: \"Charlie\" });\n\n\t\t\t// Test PUT update user\n\t\t\tconst updateData = { name: \"Alice Updated\" };\n\t\t\tconst updateResponse = await actor.fetch(\"/users/1\", {\n\t\t\t\tmethod: \"PUT\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify(updateData),\n\t\t\t});\n\t\t\texpect(updateResponse.ok).toBe(true);\n\t\t\tconst updatedUser = await updateResponse.json();\n\t\t\texpect(updatedUser).toEqual({ id: 1, name: \"Alice Updated\" });\n\n\t\t\t// Test DELETE user\n\t\t\tconst deleteResponse = await actor.fetch(\"/users/2\", {\n\t\t\t\tmethod: \"DELETE\",\n\t\t\t});\n\t\t\texpect(deleteResponse.ok).toBe(true);\n\t\t\tconst deleteResult = await deleteResponse.json();\n\t\t\texpect(deleteResult).toEqual({ message: \"User 2 deleted\" });\n\n\t\t\t// Test 404 for non-existent route\n\t\t\tconst notFoundResponse = await actor.fetch(\"/api/unknown\");\n\t\t\texpect(notFoundResponse.ok).toBe(false);\n\t\t\texpect(notFoundResponse.status).toBe(404);\n\n\t\t\t// No actions available on Hono actor\n\t\t});\n\n\t\ttest(\"should handle paths with and without leading slashes\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"path-test\"]);\n\n\t\t\t// Test path without leading slash\n\t\t\tconst responseWithoutSlash = await actor.fetch(\"api/hello\");\n\t\t\texpect(responseWithoutSlash.ok).toBe(true);\n\t\t\tconst dataWithoutSlash = await responseWithoutSlash.json();\n\t\t\texpect(dataWithoutSlash).toEqual({ message: \"Hello from actor!\" });\n\n\t\t\t// Test path with leading slash\n\t\t\tconst responseWithSlash = await actor.fetch(\"/api/hello\");\n\t\t\texpect(responseWithSlash.ok).toBe(true);\n\t\t\tconst dataWithSlash = await responseWithSlash.json();\n\t\t\texpect(dataWithSlash).toEqual({ message: \"Hello from actor!\" });\n\n\t\t\t// Both should work the same way\n\t\t});\n\n\t\ttest(\"should not create double slashes in request URLs\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\t// Create a special actor that logs the request URL\n\t\t\tconst actor = client.rawHttpHonoActor.getOrCreate([\"url-test\"]);\n\n\t\t\t// Test with leading slash - this was causing double slashes\n\t\t\tconst response = await actor.fetch(\"/users\");\n\t\t\texpect(response.ok).toBe(true);\n\n\t\t\t// The Hono router should receive a clean path without double slashes\n\t\t\t// If there were double slashes, Hono would not match the route correctly\n\t\t\tconst data = await response.json();\n\t\t\texpect(data).toEqual([\n\t\t\t\t{ id: 1, name: \"Alice\" },\n\t\t\t\t{ id: 2, name: \"Bob\" },\n\t\t\t]);\n\t\t});\n\n\t\ttest(\"should handle forwarded requests correctly without double slashes\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpHonoActor.getOrCreate([\"forward-test\"]);\n\n\t\t\t// Simulate what the example does - pass path as string and Request as init\n\t\t\tconst truncatedPath = \"/users\";\n\t\t\tconst url = new URL(truncatedPath, \"http://example.com\");\n\t\t\tconst newRequest = new Request(url, {\n\t\t\t\tmethod: \"GET\",\n\t\t\t});\n\n\t\t\t// This simulates calling actor.fetch(truncatedPath, newRequest)\n\t\t\t// which was causing double slashes in the example\n\t\t\tconst response = await actor.fetch(truncatedPath, newRequest as any);\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst users = await response.json();\n\t\t\texpect(users).toEqual([\n\t\t\t\t{ id: 1, name: \"Alice\" },\n\t\t\t\t{ id: 2, name: \"Bob\" },\n\t\t\t]);\n\t\t});\n\n\t\ttest(\"example fix: should properly forward requests using just Request object\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpHonoActor.getOrCreate([\"forward-fix\"]);\n\n\t\t\t// The correct way - just pass the Request object\n\t\t\tconst truncatedPath = \"/users/1\";\n\t\t\tconst url = new URL(truncatedPath, \"http://example.com\");\n\t\t\tconst newRequest = new Request(url, {\n\t\t\t\tmethod: \"GET\",\n\t\t\t});\n\n\t\t\t// Correct usage - just pass the Request\n\t\t\tconst response = await actor.fetch(newRequest);\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst user = await response.json();\n\t\t\texpect(user).toEqual({ id: 1, name: \"Alice\" });\n\t\t});\n\n\t\ttest(\"should support standard fetch API with URL and Request objects\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpActor.getOrCreate([\"fetch-api-test\"]);\n\n\t\t\t// Test with URL object\n\t\t\tconst url = new URL(\"/api/echo\", \"http://example.com\");\n\t\t\tconst urlResponse = await actor.fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ from: \"URL object\" }),\n\t\t\t});\n\t\t\texpect(urlResponse.ok).toBe(true);\n\t\t\tconst urlData = await urlResponse.json();\n\t\t\texpect(urlData).toEqual({ from: \"URL object\" });\n\n\t\t\t// Test with Request object\n\t\t\tconst request = new Request(\"http://example.com/api/echo\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ from: \"Request object\" }),\n\t\t\t});\n\t\t\tconst requestResponse = await actor.fetch(request);\n\t\t\texpect(requestResponse.ok).toBe(true);\n\t\t\tconst requestData = await requestResponse.json();\n\t\t\texpect(requestData).toEqual({ from: \"Request object\" });\n\n\t\t\t// Test with Request object and additional init params\n\t\t\tconst request2 = new Request(\"http://example.com/api/headers\", {\n\t\t\t\tmethod: \"GET\",\n\t\t\t\theaders: { \"X-Original\": \"request-header\" },\n\t\t\t});\n\t\t\tconst overrideResponse = await actor.fetch(request2, {\n\t\t\t\theaders: { \"X-Override\": \"init-header\" },\n\t\t\t});\n\t\t\texpect(overrideResponse.ok).toBe(true);\n\t\t\tconst headers = (await overrideResponse.json()) as Record<string, string>;\n\t\t\texpect(headers[\"x-override\"]).toBe(\"init-header\");\n\t\t\t// Original headers should be present too\n\t\t\texpect(headers[\"x-original\"]).toBe(\"request-header\");\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport { z } from \"zod\";\nimport { registry } from \"../../../fixtures/driver-test-suite/registry\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runRawHttpRequestPropertiesTests(\n\tdriverTestConfig: DriverTestConfig,\n) {\n\tdescribe(\"raw http request properties\", () => {\n\t\ttest(\"should pass all Request properties correctly to onFetch\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Test basic request properties\n\t\t\tconst response = await actor.fetch(\"test/path?foo=bar&baz=qux\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"X-Custom-Header\": \"custom-value\",\n\t\t\t\t\tAuthorization: \"Bearer test-token\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({ test: \"data\" }),\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\n\t\t\t// Verify URL properties\n\t\t\texpect(data.url).toContain(\"/test/path?foo=bar&baz=qux\");\n\t\t\texpect(data.pathname).toBe(\"/test/path\");\n\t\t\texpect(data.search).toBe(\"?foo=bar&baz=qux\");\n\t\t\texpect(data.searchParams).toEqual({\n\t\t\t\tfoo: \"bar\",\n\t\t\t\tbaz: \"qux\",\n\t\t\t});\n\n\t\t\t// Verify method\n\t\t\texpect(data.method).toBe(\"POST\");\n\n\t\t\t// Verify headers\n\t\t\texpect(data.headers[\"content-type\"]).toBe(\"application/json\");\n\t\t\texpect(data.headers[\"x-custom-header\"]).toBe(\"custom-value\");\n\t\t\texpect(data.headers[\"authorization\"]).toBe(\"Bearer test-token\");\n\n\t\t\t// Verify body\n\t\t\texpect(data.body).toEqual({ test: \"data\" });\n\t\t});\n\n\t\ttest(\"should handle GET requests with no body\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\tconst response = await actor.fetch(\"test/get\", {\n\t\t\t\tmethod: \"GET\",\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\n\t\t\texpect(data.method).toBe(\"GET\");\n\t\t\texpect(data.body).toBeNull();\n\t\t});\n\n\t\ttest(\"should handle different content types\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Test form data\n\t\t\tconst formData = new URLSearchParams();\n\t\t\tformData.append(\"field1\", \"value1\");\n\t\t\tformData.append(\"field2\", \"value2\");\n\n\t\t\tconst formResponse = await actor.fetch(\"test/form\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t},\n\t\t\t\tbody: formData.toString(),\n\t\t\t});\n\n\t\t\texpect(formResponse.ok).toBe(true);\n\t\t\tconst formResult = (await formResponse.json()) as any;\n\t\t\texpect(formResult.headers[\"content-type\"]).toBe(\n\t\t\t\t\"application/x-www-form-urlencoded\",\n\t\t\t);\n\t\t\texpect(formResult.bodyText).toBe(\"field1=value1&field2=value2\");\n\n\t\t\t// Test plain text\n\t\t\tconst textResponse = await actor.fetch(\"test/text\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"text/plain\",\n\t\t\t\t},\n\t\t\t\tbody: \"Hello, World!\",\n\t\t\t});\n\n\t\t\texpect(textResponse.ok).toBe(true);\n\t\t\tconst textResult = (await textResponse.json()) as any;\n\t\t\texpect(textResult.headers[\"content-type\"]).toBe(\"text/plain\");\n\t\t\texpect(textResult.bodyText).toBe(\"Hello, World!\");\n\t\t});\n\n\t\ttest(\"should preserve all header casing and values\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\tconst response = await actor.fetch(\"test/headers\", {\n\t\t\t\theaders: {\n\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\"Accept-Language\": \"en-US,en;q=0.9\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t\t\"User-Agent\": \"RivetKit-Test/1.0\",\n\t\t\t\t\t\"X-Request-ID\": \"12345\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\n\t\t\t// Headers should be normalized to lowercase\n\t\t\texpect(data.headers[\"accept\"]).toBe(\"application/json\");\n\t\t\texpect(data.headers[\"accept-language\"]).toBe(\"en-US,en;q=0.9\");\n\t\t\texpect(data.headers[\"cache-control\"]).toBe(\"no-cache\");\n\t\t\t// User-Agent might be overwritten by the HTTP client, so just check it exists\n\t\t\texpect(data.headers[\"user-agent\"]).toBeTruthy();\n\t\t\texpect(data.headers[\"x-request-id\"]).toBe(\"12345\");\n\t\t});\n\n\t\ttest(\"should handle empty and special URL paths\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Test root path\n\t\t\tconst rootResponse = await actor.fetch(\"\");\n\t\t\texpect(rootResponse.ok).toBe(true);\n\t\t\tconst rootData = (await rootResponse.json()) as any;\n\t\t\texpect(rootData.pathname).toBe(\"/\");\n\n\t\t\t// Test path with special characters\n\t\t\tconst specialResponse = await actor.fetch(\n\t\t\t\t\"test/path%20with%20spaces/and%2Fslashes\",\n\t\t\t);\n\t\t\texpect(specialResponse.ok).toBe(true);\n\t\t\tconst specialData = (await specialResponse.json()) as any;\n\t\t\t// Note: The URL path may or may not be decoded depending on the HTTP client/server\n\t\t\t// Just verify it contains the expected segments\n\t\t\texpect(specialData.pathname).toMatch(/path.*with.*spaces.*and.*slashes/);\n\n\t\t\t// Test path with fragment (should be ignored in server-side)\n\t\t\tconst fragmentResponse = await actor.fetch(\"test/path#fragment\");\n\t\t\texpect(fragmentResponse.ok).toBe(true);\n\t\t\tconst fragmentData = (await fragmentResponse.json()) as any;\n\t\t\texpect(fragmentData.pathname).toBe(\"/test/path\");\n\t\t\texpect(fragmentData.hash).toBe(\"\"); // Fragments are not sent to server\n\t\t});\n\n\t\ttest(\"should handle request properties for all HTTP methods\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\tconst methods = [\n\t\t\t\t\"GET\",\n\t\t\t\t\"POST\",\n\t\t\t\t\"PUT\",\n\t\t\t\t\"DELETE\",\n\t\t\t\t\"PATCH\",\n\t\t\t\t\"HEAD\",\n\t\t\t\t\"OPTIONS\",\n\t\t\t];\n\n\t\t\tfor (const method of methods) {\n\t\t\t\tconst response = await actor.fetch(`test/${method.toLowerCase()}`, {\n\t\t\t\t\tmethod,\n\t\t\t\t\t// Only include body for methods that support it\n\t\t\t\t\tbody: [\"POST\", \"PUT\", \"PATCH\"].includes(method)\n\t\t\t\t\t\t? JSON.stringify({ method })\n\t\t\t\t\t\t: undefined,\n\t\t\t\t});\n\n\t\t\t\t// HEAD responses have no body\n\t\t\t\tif (method === \"HEAD\") {\n\t\t\t\t\texpect(response.status).toBe(200);\n\t\t\t\t\tconst text = await response.text();\n\t\t\t\t\texpect(text).toBe(\"\");\n\t\t\t\t} else if (method === \"OPTIONS\") {\n\t\t\t\t\texpect(response.status).toBe(204);\n\t\t\t\t\tconst text = await response.text();\n\t\t\t\t\texpect(text).toBe(\"\");\n\t\t\t\t} else {\n\t\t\t\t\texpect(response.ok).toBe(true);\n\t\t\t\t\tconst data = (await response.json()) as any;\n\t\t\t\t\texpect(data.method).toBe(method);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\ttest(\"should handle complex query parameters\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Test multiple values for same key\n\t\t\tconst response = await actor.fetch(\n\t\t\t\t\"test?key=value1&key=value2&array[]=1&array[]=2&nested[prop]=val\",\n\t\t\t);\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\n\t\t\t// Note: URLSearchParams only keeps the last value for duplicate keys\n\t\t\texpect(data.searchParams.key).toBe(\"value2\");\n\t\t\texpect(data.searchParams[\"array[]\"]).toBe(\"2\");\n\t\t\texpect(data.searchParams[\"nested[prop]\"]).toBe(\"val\");\n\t\t});\n\n\t\ttest(\"should handle multipart form data\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Create multipart boundary\n\t\t\tconst boundary = \"----RivetKitBoundary\";\n\t\t\tconst body = [\n\t\t\t\t`------${boundary}`,\n\t\t\t\t'Content-Disposition: form-data; name=\"field1\"',\n\t\t\t\t\"\",\n\t\t\t\t\"value1\",\n\t\t\t\t`------${boundary}`,\n\t\t\t\t'Content-Disposition: form-data; name=\"field2\"',\n\t\t\t\t\"\",\n\t\t\t\t\"value2\",\n\t\t\t\t`------${boundary}--`,\n\t\t\t].join(\"\\r\\n\");\n\n\t\t\tconst response = await actor.fetch(\"test/multipart\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": `multipart/form-data; boundary=----${boundary}`,\n\t\t\t\t},\n\t\t\t\tbody: body,\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\t\t\texpect(data.headers[\"content-type\"]).toContain(\"multipart/form-data\");\n\t\t\texpect(data.bodyText).toContain(\"field1\");\n\t\t\texpect(data.bodyText).toContain(\"value1\");\n\t\t});\n\n\t\ttest(\"should handle very long URLs\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Create a very long query string\n\t\t\tconst longValue = \"x\".repeat(1000);\n\t\t\tconst response = await actor.fetch(`test/long?param=${longValue}`);\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\t\t\texpect(data.searchParams.param).toBe(longValue);\n\t\t\texpect(data.search.length).toBeGreaterThan(1000);\n\t\t});\n\n\t\ttest(\"should handle large request bodies\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Create a large JSON body (1MB+)\n\t\t\tconst largeArray = new Array(10000).fill({\n\t\t\t\tid: 1,\n\t\t\t\tname: \"Test\",\n\t\t\t\tdescription: \"This is a test object with some data\",\n\t\t\t});\n\n\t\t\tconst response = await actor.fetch(\"test/large\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(largeArray),\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\t\t\texpect(data.body).toHaveLength(10000);\n\t\t});\n\n\t\ttest(\"should handle missing content-type header\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\tconst response = await actor.fetch(\"test/no-content-type\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tbody: \"plain text without content-type\",\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\t\t\texpect(data.bodyText).toBe(\"plain text without content-type\");\n\t\t});\n\n\t\ttest(\"should handle empty request body\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\tconst response = await actor.fetch(\"test/empty\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: \"\",\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\t\t\texpect(data.body).toBeNull();\n\t\t\texpect(data.bodyText).toBe(\"\");\n\t\t});\n\n\t\ttest(\"should handle custom HTTP methods\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Test a custom method (though most HTTP clients may not support this)\n\t\t\ttry {\n\t\t\t\tconst response = await actor.fetch(\"test/custom\", {\n\t\t\t\t\tmethod: \"CUSTOM\",\n\t\t\t\t});\n\n\t\t\t\t// If the request succeeds, verify the method\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = (await response.json()) as any;\n\t\t\t\t\texpect(data.method).toBe(\"CUSTOM\");\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Some HTTP clients may reject custom methods\n\t\t\t\t// This is expected behavior\n\t\t\t}\n\t\t});\n\n\t\ttest(\"should handle cookies in headers\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\tconst response = await actor.fetch(\"test/cookies\", {\n\t\t\t\theaders: {\n\t\t\t\t\tCookie: \"session=abc123; user=test; preferences=dark_mode\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\t\t\texpect(data.headers.cookie).toBe(\n\t\t\t\t\"session=abc123; user=test; preferences=dark_mode\",\n\t\t\t);\n\t\t});\n\n\t\ttest(\"should handle URL encoding properly\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Test various encoded characters\n\t\t\tconst response = await actor.fetch(\n\t\t\t\t\"test/encoded?special=%20%21%40%23%24%25%5E%26&unicode=%E2%9C%93&email=test%40example.com\",\n\t\t\t);\n\n\t\t\texpect(response.ok).toBe(true);\n\t\t\tconst data = (await response.json()) as any;\n\n\t\t\t// Verify URL decoding\n\t\t\texpect(data.searchParams.special).toBe(\" !@#$%^&\");\n\t\t\texpect(data.searchParams.unicode).toBe(\"✓\");\n\t\t\texpect(data.searchParams.email).toBe(\"test@example.com\");\n\t\t});\n\n\t\ttest(\"should handle concurrent requests maintaining separate contexts\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawHttpRequestPropertiesActor.getOrCreate([\"test\"]);\n\n\t\t\t// Send multiple concurrent requests with different data\n\t\t\tconst requests = [\n\t\t\t\tactor.fetch(\"test/concurrent?id=1\", {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\t\tbody: JSON.stringify({ request: 1 }),\n\t\t\t\t}),\n\t\t\t\tactor.fetch(\"test/concurrent?id=2\", {\n\t\t\t\t\tmethod: \"PUT\",\n\t\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\t\tbody: JSON.stringify({ request: 2 }),\n\t\t\t\t}),\n\t\t\t\tactor.fetch(\"test/concurrent?id=3\", {\n\t\t\t\t\tmethod: \"DELETE\",\n\t\t\t\t}),\n\t\t\t];\n\n\t\t\tconst responses = await Promise.all(requests);\n\t\t\tconst results = (await Promise.all(\n\t\t\t\tresponses.map((r) => r.json()),\n\t\t\t)) as any[];\n\n\t\t\t// Verify each request maintained its own context\n\t\t\texpect(results[0].searchParams.id).toBe(\"1\");\n\t\t\texpect(results[0].method).toBe(\"POST\");\n\t\t\texpect(results[0].body).toEqual({ request: 1 });\n\n\t\t\texpect(results[1].searchParams.id).toBe(\"2\");\n\t\t\texpect(results[1].method).toBe(\"PUT\");\n\t\t\texpect(results[1].body).toEqual({ request: 2 });\n\n\t\t\texpect(results[2].searchParams.id).toBe(\"3\");\n\t\t\texpect(results[2].method).toBe(\"DELETE\");\n\t\t\texpect(results[2].body).toBeNull();\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runRawWebSocketTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"raw websocket\", () => {\n\t\ttest(\"should establish raw WebSocket connection\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawWebSocketActor.getOrCreate([\"basic\"]);\n\n\t\t\tconst ws = await actor.websocket();\n\n\t\t\t// The WebSocket should already be open since openWebSocket waits for openPromise\n\t\t\t// But we still need to ensure any buffered events are processed\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\t// If already open, resolve immediately\n\t\t\t\tif (ws.readyState === WebSocket.OPEN) {\n\t\t\t\t\tresolve();\n\t\t\t\t} else {\n\t\t\t\t\t// Otherwise wait for open event\n\t\t\t\t\tws.addEventListener(\n\t\t\t\t\t\t\"open\",\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ once: true },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Should receive welcome message\n\t\t\tconst welcomeMessage = await new Promise<any>((resolve, reject) => {\n\t\t\t\tws.addEventListener(\n\t\t\t\t\t\"message\",\n\t\t\t\t\t(event: any) => {\n\t\t\t\t\t\tresolve(JSON.parse(event.data as string));\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true },\n\t\t\t\t);\n\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\texpect(welcomeMessage.type).toBe(\"welcome\");\n\t\t\texpect(welcomeMessage.connectionCount).toBe(1);\n\n\t\t\tws.close();\n\t\t});\n\n\t\ttest(\"should echo messages\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawWebSocketActor.getOrCreate([\"echo\"]);\n\n\t\t\tconst ws = await actor.websocket();\n\n\t\t\t// Check if WebSocket is already open\n\t\t\tif (ws.readyState !== WebSocket.OPEN) {\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tws.addEventListener(\"open\", () => resolve(), { once: true });\n\t\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Skip welcome message\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tws.addEventListener(\"message\", () => resolve(), { once: true });\n\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\t// Send and receive echo\n\t\t\tconst testMessage = { test: \"data\", timestamp: Date.now() };\n\t\t\tws.send(JSON.stringify(testMessage));\n\n\t\t\tconst echoMessage = await new Promise<any>((resolve, reject) => {\n\t\t\t\tws.addEventListener(\n\t\t\t\t\t\"message\",\n\t\t\t\t\t(event: any) => {\n\t\t\t\t\t\tresolve(JSON.parse(event.data as string));\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true },\n\t\t\t\t);\n\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\texpect(echoMessage).toEqual(testMessage);\n\n\t\t\tws.close();\n\t\t});\n\n\t\ttest(\"should handle ping/pong protocol\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawWebSocketActor.getOrCreate([\"ping\"]);\n\n\t\t\tconst ws = await actor.websocket();\n\n\t\t\t// Check if WebSocket is already open\n\t\t\tif (ws.readyState !== WebSocket.OPEN) {\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tws.addEventListener(\"open\", () => resolve(), { once: true });\n\t\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Skip welcome message\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tws.addEventListener(\"message\", () => resolve(), { once: true });\n\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\t// Send ping\n\t\t\tws.send(JSON.stringify({ type: \"ping\" }));\n\n\t\t\tconst pongMessage = await new Promise<any>((resolve, reject) => {\n\t\t\t\tws.addEventListener(\"message\", (event: any) => {\n\t\t\t\t\tconst data = JSON.parse(event.data as string);\n\t\t\t\t\tif (data.type === \"pong\") {\n\t\t\t\t\t\tresolve(data);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\texpect(pongMessage.type).toBe(\"pong\");\n\t\t\texpect(pongMessage.timestamp).toBeDefined();\n\n\t\t\tws.close();\n\t\t});\n\n\t\ttest(\"should track stats across connections\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor1 = client.rawWebSocketActor.getOrCreate([\"stats\"]);\n\n\t\t\t// Create first connection to ensure actor exists\n\t\t\tconst ws1 = await actor1.websocket();\n\t\t\tconst ws1MessagePromise = new Promise<void>((resolve, reject) => {\n\t\t\t\tws1.addEventListener(\"message\", () => resolve(), { once: true });\n\t\t\t\tws1.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\t// Wait for first connection to establish before getting the actor\n\t\t\tawait ws1MessagePromise;\n\n\t\t\t// Now get reference to same actor\n\t\t\tconst actor2 = client.rawWebSocketActor.get([\"stats\"]);\n\t\t\tconst ws2 = await actor2.websocket();\n\t\t\tconst ws2MessagePromise = new Promise<void>((resolve, reject) => {\n\t\t\t\tws2.addEventListener(\"message\", () => resolve(), { once: true });\n\t\t\t\tws2.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\t// Wait for welcome messages\n\t\t\tawait Promise.all([ws1MessagePromise, ws2MessagePromise]);\n\n\t\t\t// Send some messages\n\t\t\tconst pingPromise = new Promise<any>((resolve, reject) => {\n\t\t\t\tws2.addEventListener(\"message\", (event: any) => {\n\t\t\t\t\tconst data = JSON.parse(event.data as string);\n\t\t\t\t\tif (data.type === \"pong\") {\n\t\t\t\t\t\tresolve(undefined);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tws2.addEventListener(\"close\", reject);\n\t\t\t});\n\t\t\tws1.send(JSON.stringify({ data: \"test1\" }));\n\t\t\tws1.send(JSON.stringify({ data: \"test3\" }));\n\t\t\tws2.send(JSON.stringify({ type: \"ping\" }));\n\t\t\tawait pingPromise;\n\n\t\t\t// Get stats\n\t\t\tconst statsPromise = new Promise<any>((resolve, reject) => {\n\t\t\t\tws1.addEventListener(\"message\", (event: any) => {\n\t\t\t\t\tconst data = JSON.parse(event.data as string);\n\t\t\t\t\tif (data.type === \"stats\") {\n\t\t\t\t\t\tresolve(data);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tws1.addEventListener(\"close\", reject);\n\t\t\t});\n\t\t\tws1.send(JSON.stringify({ type: \"getStats\" }));\n\t\t\tconst stats = await statsPromise;\n\t\t\texpect(stats.connectionCount).toBe(2);\n\t\t\texpect(stats.messageCount).toBe(4);\n\n\t\t\t// Verify via action\n\t\t\tconst actionStats = await actor1.getStats();\n\t\t\texpect(actionStats.connectionCount).toBe(2);\n\t\t\texpect(actionStats.messageCount).toBe(4);\n\n\t\t\tws1.close();\n\t\t\tws2.close();\n\t\t});\n\n\t\ttest(\"should handle binary data\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawWebSocketBinaryActor.getOrCreate([\"binary\"]);\n\n\t\t\tconst ws = await actor.websocket();\n\n\t\t\t// Check if WebSocket is already open\n\t\t\tif (ws.readyState !== WebSocket.OPEN) {\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tws.addEventListener(\"open\", () => resolve(), { once: true });\n\t\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Helper to receive and convert binary message\n\t\t\tconst receiveBinaryMessage = async (): Promise<Uint8Array> => {\n\t\t\t\tconst response = await new Promise<ArrayBuffer | Blob>(\n\t\t\t\t\t(resolve, reject) => {\n\t\t\t\t\t\tws.addEventListener(\n\t\t\t\t\t\t\t\"message\",\n\t\t\t\t\t\t\t(event: any) => {\n\t\t\t\t\t\t\t\tresolve(event.data);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ once: true },\n\t\t\t\t\t\t);\n\t\t\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\t// Convert Blob to ArrayBuffer if needed\n\t\t\t\tconst buffer =\n\t\t\t\t\tresponse instanceof Blob ? await response.arrayBuffer() : response;\n\n\t\t\t\treturn new Uint8Array(buffer);\n\t\t\t};\n\n\t\t\t// Test 1: Small binary data\n\t\t\tconst smallData = new Uint8Array([1, 2, 3, 4, 5]);\n\t\t\tws.send(smallData);\n\t\t\tconst smallReversed = await receiveBinaryMessage();\n\t\t\texpect(Array.from(smallReversed)).toEqual([5, 4, 3, 2, 1]);\n\n\t\t\t// Test 2: Large binary data (1KB)\n\t\t\tconst largeData = new Uint8Array(1024);\n\t\t\tfor (let i = 0; i < largeData.length; i++) {\n\t\t\t\tlargeData[i] = i % 256;\n\t\t\t}\n\t\t\tws.send(largeData);\n\t\t\tconst largeReversed = await receiveBinaryMessage();\n\n\t\t\t// Verify it's reversed correctly\n\t\t\tfor (let i = 0; i < largeData.length; i++) {\n\t\t\t\texpect(largeReversed[i]).toBe(largeData[largeData.length - 1 - i]);\n\t\t\t}\n\n\t\t\tws.close();\n\t\t});\n\n\t\ttest(\"should work with custom paths\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawWebSocketActor.getOrCreate([\"paths\"]);\n\n\t\t\tconst ws = await actor.websocket(\"custom/path\");\n\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tws.addEventListener(\"open\", () => {\n\t\t\t\t\tresolve();\n\t\t\t\t});\n\t\t\t\tws.addEventListener(\"error\", reject);\n\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\t// Should still work\n\t\t\tconst welcomeMessage = await new Promise<any>((resolve) => {\n\t\t\t\tws.addEventListener(\n\t\t\t\t\t\"message\",\n\t\t\t\t\t(event: any) => {\n\t\t\t\t\t\tresolve(JSON.parse(event.data as string));\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true },\n\t\t\t\t);\n\t\t\t});\n\n\t\t\texpect(welcomeMessage.type).toBe(\"welcome\");\n\n\t\t\tws.close();\n\t\t});\n\n\t\ttest(\"should handle connection close properly\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawWebSocketActor.getOrCreate([\"close-test\"]);\n\n\t\t\tconst ws = await actor.websocket();\n\n\t\t\t// Check if WebSocket is already open\n\t\t\tif (ws.readyState !== WebSocket.OPEN) {\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tws.addEventListener(\"open\", () => resolve(), { once: true });\n\t\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Get initial stats\n\t\t\tconst initialStats = await actor.getStats();\n\t\t\texpect(initialStats.connectionCount).toBe(1);\n\n\t\t\t// Wait for close event on client side\n\t\t\tconst closePromise = new Promise<void>((resolve) => {\n\t\t\t\tws.addEventListener(\"close\", () => resolve(), { once: true });\n\t\t\t});\n\n\t\t\t// Close connection\n\t\t\tws.close();\n\t\t\tawait closePromise;\n\n\t\t\t// Poll getStats until connection count is 0\n\t\t\tlet finalStats: any;\n\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\tfinalStats = await actor.getStats();\n\t\t\t\tif (finalStats.connectionCount === 0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 50));\n\t\t\t}\n\n\t\t\t// Check stats after close\n\t\t\texpect(finalStats?.connectionCount).toBe(0);\n\t\t});\n\n\t\ttest(\"should properly handle onWebSocket open and close events\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawWebSocketActor.getOrCreate([\"open-close-test\"]);\n\n\t\t\t// Create first connection\n\t\t\tconst ws1 = await actor.websocket();\n\n\t\t\t// Wait for open event\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tws1.addEventListener(\"open\", () => resolve(), { once: true });\n\t\t\t\tws1.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\t// Wait for welcome message which confirms onWebSocket was called\n\t\t\tconst welcome1 = await new Promise<any>((resolve, reject) => {\n\t\t\t\tws1.addEventListener(\n\t\t\t\t\t\"message\",\n\t\t\t\t\t(event: any) => {\n\t\t\t\t\t\tresolve(JSON.parse(event.data as string));\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true },\n\t\t\t\t);\n\t\t\t\tws1.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\texpect(welcome1.type).toBe(\"welcome\");\n\t\t\texpect(welcome1.connectionCount).toBe(1);\n\n\t\t\t// Create second connection to same actor\n\t\t\tconst ws2 = await actor.websocket();\n\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tws2.addEventListener(\"open\", () => resolve(), { once: true });\n\t\t\t\tws2.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\tconst welcome2 = await new Promise<any>((resolve, reject) => {\n\t\t\t\tws2.addEventListener(\n\t\t\t\t\t\"message\",\n\t\t\t\t\t(event: any) => {\n\t\t\t\t\t\tresolve(JSON.parse(event.data as string));\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true },\n\t\t\t\t);\n\t\t\t\tws2.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\texpect(welcome2.type).toBe(\"welcome\");\n\t\t\texpect(welcome2.connectionCount).toBe(2);\n\n\t\t\t// Verify stats\n\t\t\tconst midStats = await actor.getStats();\n\t\t\texpect(midStats.connectionCount).toBe(2);\n\n\t\t\t// Close first connection\n\t\t\tws1.close();\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tws1.addEventListener(\"close\", () => resolve(), { once: true });\n\t\t\t});\n\n\t\t\t// Poll getStats until connection count decreases to 1\n\t\t\tlet afterFirstClose: any;\n\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\tafterFirstClose = await actor.getStats();\n\t\t\t\tif (afterFirstClose.connectionCount === 1) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 50));\n\t\t\t}\n\n\t\t\t// Verify connection count decreased\n\t\t\texpect(afterFirstClose?.connectionCount).toBe(1);\n\n\t\t\t// Close second connection\n\t\t\tws2.close();\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tws2.addEventListener(\"close\", () => resolve(), { once: true });\n\t\t\t});\n\n\t\t\t// Poll getStats until connection count is 0\n\t\t\tlet finalStats: any;\n\t\t\tfor (let i = 0; i < 20; i++) {\n\t\t\t\tfinalStats = await actor.getStats();\n\t\t\t\tif (finalStats.connectionCount === 0) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 50));\n\t\t\t}\n\n\t\t\t// Verify final state\n\t\t\texpect(finalStats?.connectionCount).toBe(0);\n\t\t});\n\n\t\ttest(\"should handle query parameters in websocket paths\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\t\t\tconst actor = client.rawWebSocketActor.getOrCreate([\"query-params\"]);\n\n\t\t\t// Test WebSocket with query parameters\n\t\t\tconst ws = await actor.websocket(\"api/v1/stream?token=abc123&user=test\");\n\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tws.addEventListener(\"open\", () => resolve(), { once: true });\n\t\t\t\tws.addEventListener(\"error\", reject);\n\t\t\t});\n\n\t\t\tconst requestInfoPromise = new Promise<any>((resolve, reject) => {\n\t\t\t\tws.addEventListener(\"message\", (event: any) => {\n\t\t\t\t\tconst data = JSON.parse(event.data as string);\n\t\t\t\t\tif (data.type === \"requestInfo\") {\n\t\t\t\t\t\tresolve(data);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tws.addEventListener(\"close\", reject);\n\t\t\t});\n\n\t\t\t// Send request to get the request info\n\t\t\tws.send(JSON.stringify({ type: \"getRequestInfo\" }));\n\n\t\t\tconst requestInfo = await requestInfoPromise;\n\n\t\t\t// Verify the path and query parameters were preserved\n\t\t\texpect(requestInfo.url).toContain(\"api/v1/stream\");\n\t\t\texpect(requestInfo.url).toContain(\"token=abc123\");\n\t\t\texpect(requestInfo.url).toContain(\"user=test\");\n\n\t\t\tws.close();\n\t\t});\n\t});\n}\n","import { describe, expect, test } from \"vitest\";\nimport type { DriverTestConfig } from \"../mod\";\nimport { setupDriverTest } from \"../utils\";\n\nexport function runRequestAccessTests(driverTestConfig: DriverTestConfig) {\n\tdescribe(\"Request Access in Lifecycle Hooks\", () => {\n\t\ttest(\"should have access to request object in onBeforeConnect and createConnState\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor with request tracking enabled\n\t\t\tconst handle = client.requestAccessActor.getOrCreate([\"test-request\"], {\n\t\t\t\tparams: { trackRequest: true },\n\t\t\t});\n\t\t\tconst connection = handle.connect();\n\n\t\t\t// Get request info that was captured in onBeforeConnect\n\t\t\tconst requestInfo = await connection.getRequestInfo();\n\n\t\t\t// Verify request was accessible in HTTP mode, but not in inline mode\n\t\t\tif (driverTestConfig.clientType === \"http\") {\n\t\t\t\t// Check onBeforeConnect\n\t\t\t\texpect(requestInfo.onBeforeConnect.hasRequest).toBe(true);\n\t\t\t\texpect(requestInfo.onBeforeConnect.requestUrl).toBeDefined();\n\t\t\t\texpect(requestInfo.onBeforeConnect.requestMethod).toBeDefined();\n\t\t\t\texpect(requestInfo.onBeforeConnect.requestHeaders).toBeDefined();\n\n\t\t\t\t// Check createConnState\n\t\t\t\texpect(requestInfo.createConnState.hasRequest).toBe(true);\n\t\t\t\texpect(requestInfo.createConnState.requestUrl).toBeDefined();\n\t\t\t\texpect(requestInfo.createConnState.requestMethod).toBeDefined();\n\t\t\t\texpect(requestInfo.createConnState.requestHeaders).toBeDefined();\n\t\t\t} else {\n\t\t\t\t// Inline client may or may not have request object depending on the driver\n\t\t\t\t//\n\t\t\t\t// e.g.\n\t\t\t\t// - File system does not have a request for inline requests\n\t\t\t\t// - Rivet Engine proxies the request so it has access to the request object\n\t\t\t}\n\n\t\t\t// Clean up\n\t\t\tawait connection.dispose();\n\t\t});\n\n\t\ttest(\"should not have request when trackRequest is false\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor without request tracking\n\t\t\tconst handle = client.requestAccessActor.getOrCreate(\n\t\t\t\t[\"test-no-request\"],\n\t\t\t\t{\n\t\t\t\t\tparams: { trackRequest: false },\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst connection = handle.connect();\n\n\t\t\t// Get request info\n\t\t\tconst requestInfo = await connection.getRequestInfo();\n\n\t\t\t// Verify request was not tracked\n\t\t\texpect(requestInfo.onBeforeConnect.hasRequest).toBe(false);\n\t\t\texpect(requestInfo.onBeforeConnect.requestUrl).toBeNull();\n\t\t\texpect(requestInfo.onBeforeConnect.requestMethod).toBeNull();\n\t\t\texpect(\n\t\t\t\tObject.keys(requestInfo.onBeforeConnect.requestHeaders),\n\t\t\t).toHaveLength(0);\n\n\t\t\texpect(requestInfo.createConnState.hasRequest).toBe(false);\n\t\t\texpect(requestInfo.createConnState.requestUrl).toBeNull();\n\t\t\texpect(requestInfo.createConnState.requestMethod).toBeNull();\n\t\t\texpect(\n\t\t\t\tObject.keys(requestInfo.createConnState.requestHeaders),\n\t\t\t).toHaveLength(0);\n\n\t\t\t// Clean up\n\t\t\tawait connection.dispose();\n\t\t});\n\n\t\ttest(\"should capture request headers and method\", async (c) => {\n\t\t\tconst { client } = await setupDriverTest(c, driverTestConfig);\n\n\t\t\t// Create actor and connect with request tracking\n\t\t\tconst handle = client.requestAccessActor.getOrCreate([\"test-headers\"], {\n\t\t\t\tparams: { trackRequest: true },\n\t\t\t});\n\t\t\tconst connection = handle.connect();\n\n\t\t\t// Get request info\n\t\t\tconst requestInfo = await connection.getRequestInfo();\n\n\t\t\tif (driverTestConfig.clientType === \"http\") {\n\t\t\t\t// Verify request details were captured in both hooks\n\t\t\t\texpect(requestInfo.onBeforeConnect.hasRequest).toBe(true);\n\t\t\t\texpect(requestInfo.onBeforeConnect.requestMethod).toBeTruthy();\n\t\t\t\texpect(requestInfo.onBeforeConnect.requestUrl).toBeTruthy();\n\t\t\t\texpect(requestInfo.onBeforeConnect.requestHeaders).toBeTruthy();\n\t\t\t\texpect(typeof requestInfo.onBeforeConnect.requestHeaders).toBe(\n\t\t\t\t\t\"object\",\n\t\t\t\t);\n\n\t\t\t\texpect(requestInfo.createConnState.hasRequest).toBe(true);\n\t\t\t\texpect(requestInfo.createConnState.requestMethod).toBeTruthy();\n\t\t\t\texpect(requestInfo.createConnState.requestUrl).toBeTruthy();\n\t\t\t\texpect(requestInfo.createConnState.requestHeaders).toBeTruthy();\n\t\t\t\texpect(typeof requestInfo.createConnState.requestHeaders).toBe(\n\t\t\t\t\t\"object\",\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t// Inline client may or may not have request object depending on the driver\n\t\t\t\t//\n\t\t\t\t// See \"should have access to request object in onBeforeConnect and createConnState\"\n\t\t\t}\n\n\t\t\t// Clean up\n\t\t\tawait connection.dispose();\n\t\t});\n\n\t\t// TODO: re-expose this once we can have actor queries on the gateway\n\t\t// test(\"should have access to request object in onFetch\", async (c) => {\n\t\t// \tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\t\t//\n\t\t// \t// Create actor\n\t\t// \tconst handle = client.requestAccessActor.getOrCreate([\"test-fetch\"]);\n\t\t//\n\t\t// \t// Make a raw HTTP request to the actor\n\t\t// \tawait handle.resolve(); // Ensure actor is created\n\t\t//\n\t\t// \tconst actorQuery = {\n\t\t// \t\tgetOrCreateForKey: {\n\t\t// \t\t\tname: \"requestAccessActor\",\n\t\t// \t\t\tkey: [\"test-fetch\"],\n\t\t// \t\t},\n\t\t// \t};\n\t\t//\n\t\t// \tconst url = `${endpoint}/registry/actors/raw/http/test-path`;\n\t\t// \tconst response = await fetch(url, {\n\t\t// \t\tmethod: \"POST\",\n\t\t// \t\theaders: {\n\t\t// \t\t\t\"Content-Type\": \"application/json\",\n\t\t// \t\t\t\"X-Test-Header\": \"test-value\",\n\t\t// \t\t\t\"X-RivetKit-Query\": JSON.stringify(actorQuery),\n\t\t// \t\t},\n\t\t// \t\tbody: JSON.stringify({ test: \"data\" }),\n\t\t// \t});\n\t\t//\n\t\t// \tif (!response.ok) {\n\t\t// \t\tconst errorText = await response.text();\n\t\t// \t\tconsole.error(\n\t\t// \t\t\t`HTTP request failed: ${response.status} ${response.statusText}`,\n\t\t// \t\t\terrorText,\n\t\t// \t\t);\n\t\t// \t}\n\t\t//\n\t\t// \texpect(response.ok).toBe(true);\n\t\t// \tconst data = await response.json();\n\t\t//\n\t\t// \t// Verify request info from onFetch\n\t\t// \texpect((data as any).hasRequest).toBe(true);\n\t\t// \texpect((data as any).requestUrl).toContain(\"/test-path\");\n\t\t// \texpect((data as any).requestMethod).toBe(\"POST\");\n\t\t// \texpect((data as any).requestHeaders).toBeDefined();\n\t\t// \texpect((data as any).requestHeaders[\"content-type\"]).toBe(\n\t\t// \t\t\"application/json\",\n\t\t// \t);\n\t\t// \texpect((data as any).requestHeaders[\"x-test-header\"]).toBe(\"test-value\");\n\t\t// });\n\n\t\t// test(\"should have access to request object in onWebSocket\", async (c) => {\n\t\t// \tconst { client, endpoint } = await setupDriverTest(c, driverTestConfig);\n\t\t//\n\t\t// \t// Only test in environments that support WebSocket\n\t\t// \tif (typeof WebSocket !== \"undefined\") {\n\t\t// \t\t// Create actor\n\t\t// \t\tconst handle = client.requestAccessActor.getOrCreate([\n\t\t// \t\t\t\"test-websocket\",\n\t\t// \t\t]);\n\t\t// \t\tawait handle.resolve(); // Ensure actor is created\n\t\t//\n\t\t// \t\tconst actorQuery = {\n\t\t// \t\t\tgetOrCreateForKey: {\n\t\t// \t\t\t\tname: \"requestAccessActor\",\n\t\t// \t\t\t\tkey: [\"test-websocket\"],\n\t\t// \t\t\t},\n\t\t// \t\t};\n\t\t//\n\t\t// \t\t// Encode query as WebSocket subprotocol\n\t\t// \t\tconst queryProtocol = `query.${encodeURIComponent(JSON.stringify(actorQuery))}`;\n\t\t//\n\t\t// \t\t// Create raw WebSocket connection\n\t\t// \t\tconst wsUrl = endpoint\n\t\t// \t\t\t.replace(\"http://\", \"ws://\")\n\t\t// \t\t\t.replace(\"https://\", \"wss://\");\n\t\t// \t\tconst ws = new WebSocket(\n\t\t// \t\t\t`${wsUrl}/registry/actors/raw/websocket/test-path`,\n\t\t// \t\t\t[\n\t\t// \t\t\t\tqueryProtocol,\n\t\t// \t\t\t\t\"rivetkit\", // Required protocol\n\t\t// \t\t\t],\n\t\t// \t\t);\n\t\t//\n\t\t// \t\t// Wait for connection and first message\n\t\t// \t\tawait new Promise<void>((resolve, reject) => {\n\t\t// \t\t\tws.onopen = () => {\n\t\t// \t\t\t\t// Connection established\n\t\t// \t\t\t};\n\t\t//\n\t\t// \t\t\tws.onmessage = (event) => {\n\t\t// \t\t\t\ttry {\n\t\t// \t\t\t\t\tconst data = JSON.parse(event.data);\n\t\t//\n\t\t// \t\t\t\t\t// Verify request info from onWebSocket\n\t\t// \t\t\t\t\texpect(data.hasRequest).toBe(true);\n\t\t// \t\t\t\t\texpect(data.requestUrl).toContain(\"/test-path\");\n\t\t// \t\t\t\t\texpect(data.requestMethod).toBe(\"GET\");\n\t\t// \t\t\t\t\texpect(data.requestHeaders).toBeDefined();\n\t\t//\n\t\t// \t\t\t\t\tws.close();\n\t\t// \t\t\t\t\tresolve();\n\t\t// \t\t\t\t} catch (error) {\n\t\t// \t\t\t\t\treject(error);\n\t\t// \t\t\t\t}\n\t\t// \t\t\t};\n\t\t//\n\t\t// \t\t\tws.onerror = (error) => {\n\t\t// \t\t\t\treject(error);\n\t\t// \t\t\t};\n\t\t// \t\t});\n\t\t// \t}\n\t\t// });\n\t});\n}\n"]}