Skip to content

Commit bf5e675

Browse files
committed
feat: add actor-core/test (#790)
1 parent 991fc99 commit bf5e675

File tree

21 files changed

+848
-343
lines changed

21 files changed

+848
-343
lines changed

.github/workflows/test.yml

Lines changed: 59 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,60 @@
11
# TODO: Cache both yarn & cargo
2-
#
3-
# name: 'Test'
4-
# on:
5-
# pull_request:
6-
# workflow_dispatch:
7-
#
8-
# jobs:
9-
# test:
10-
# runs-on: ubuntu-latest
11-
#
12-
# steps:
13-
# - uses: actions/checkout@v4
14-
# - uses: actions/setup-node@v4
15-
# with:
16-
# node-version: '22.14'
17-
# - run: corepack enable
18-
# - name: Install dependencies
19-
# run: yarn install --frozen-lockfile
20-
# - name: Run actor-core tests
21-
# run: yarn test
22-
# - name: Install Rust
23-
# uses: dtolnay/rust-toolchain@stable
24-
# - name: Run Rust client tests
25-
# run: cd rust/client && cargo test
26-
#
27-
# # TODO: This is broken
28-
# # test-cli:
29-
# # runs-on: ubuntu-latest
30-
# #
31-
# # services:
32-
# # verdaccio:
33-
# # image: verdaccio/verdaccio:6
34-
# # ports:
35-
# # - 4873:4873
36-
# # options: --name verdaccio
37-
# #
38-
# # steps:
39-
# # - uses: actions/checkout@v4
40-
# # - run: corepack enable
41-
# # # https://github.com/orgs/community/discussions/42127
42-
# # - run: /usr/bin/docker cp ${{ github.workspace }}/.verdaccio/conf/config.yaml verdaccio:/verdaccio/conf/config.yaml
43-
# # - run: /usr/bin/docker restart verdaccio
44-
# #
45-
# # - uses: actions/cache@v4
46-
# # with:
47-
# # path: .turbo
48-
# # key: ${{ runner.os }}-turbo-${{ github.sha }}
49-
# # restore-keys: |
50-
# # ${{ runner.os }}-turbo-
51-
# # - uses: actions/setup-node@v4
52-
# # with:
53-
# # node-version: '22.14'
54-
# # cache: 'yarn'
55-
# # - run: yarn install
56-
# # - run: yarn build
57-
# # - run: npm i -g tsx
58-
# # - run: ./scripts/e2e-publish.ts
59-
# # - run: yarn workspace @actor-core/cli run test
60-
# #
2+
3+
name: 'Test'
4+
on:
5+
pull_request:
6+
workflow_dispatch:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-node@v4
15+
with:
16+
node-version: '22.14'
17+
- run: corepack enable
18+
- name: Install dependencies
19+
run: yarn install --frozen-lockfile
20+
- name: Run actor-core tests
21+
run: yarn test
22+
# - name: Install Rust
23+
# uses: dtolnay/rust-toolchain@stable
24+
# - name: Run Rust client tests
25+
# run: cd rust/client && cargo test
26+
27+
# TODO: This is broken
28+
# test-cli:
29+
# runs-on: ubuntu-latest
30+
#
31+
# services:
32+
# verdaccio:
33+
# image: verdaccio/verdaccio:6
34+
# ports:
35+
# - 4873:4873
36+
# options: --name verdaccio
37+
#
38+
# steps:
39+
# - uses: actions/checkout@v4
40+
# - run: corepack enable
41+
# # https://github.com/orgs/community/discussions/42127
42+
# - run: /usr/bin/docker cp ${{ github.workspace }}/.verdaccio/conf/config.yaml verdaccio:/verdaccio/conf/config.yaml
43+
# - run: /usr/bin/docker restart verdaccio
44+
#
45+
# - uses: actions/cache@v4
46+
# with:
47+
# path: .turbo
48+
# key: ${{ runner.os }}-turbo-${{ github.sha }}
49+
# restore-keys: |
50+
# ${{ runner.os }}-turbo-
51+
# - uses: actions/setup-node@v4
52+
# with:
53+
# node-version: '22.14'
54+
# cache: 'yarn'
55+
# - run: yarn install
56+
# - run: yarn build
57+
# - run: npm i -g tsx
58+
# - run: ./scripts/e2e-publish.ts
59+
# - run: yarn workspace @actor-core/cli run test
60+
#

examples/chat-room/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
"private": true,
55
"type": "module",
66
"scripts": {
7-
"check-types": "tsc --noEmit",
8-
"test": "tsx tests/client.ts"
7+
"check-types": "tsc --noEmit"
98
},
109
"devDependencies": {
1110
"@types/node": "^22.13.9",

examples/counter/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
"private": true,
55
"type": "module",
66
"scripts": {
7-
"check-types": "tsc --noEmit",
8-
"test": "tsx tests/client.ts"
7+
"check-types": "tsc --noEmit"
98
},
109
"devDependencies": {
1110
"@types/node": "^22.13.9",

packages/actor-core-cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"scripts": {
3030
"build": "tsup",
3131
"dev": "yarn build --watch",
32-
"test": "vitest",
32+
"test": "vitest run",
3333
"check-types": "tsc --noEmit"
3434
},
3535
"dependencies": {

packages/actor-core-cli/tests/create.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ beforeAll(async () => {
3030
const testCases = PLATFORMS.flatMap((platform) =>
3131
EXAMPLES.map((example) => [example, platform]),
3232
);
33-
describe("npx create-actor", () => {
33+
// TODO: These tests time out
34+
describe.skip("npx create-actor", () => {
3435
describe.each(testCases)(
3536
"should create example '%s' for '%s'",
3637
async (example, platform) => {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// tsup.config.ts
2+
import { sentryEsbuildPlugin } from "@sentry/esbuild-plugin";
3+
import { defineConfig } from "tsup";
4+
import Macros from "unplugin-macros/esbuild";
5+
var createRequireSnippet = `
6+
import { createRequire as topLevelCreateRequire } from "node:module";
7+
import { fileURLToPath as topLevelFileURLToPath, URL as topLevelURL } from "node:url";
8+
const require = topLevelCreateRequire(import.meta.url);
9+
const __filename = topLevelFileURLToPath(import.meta.url);
10+
const __dirname = topLevelFileURLToPath(new topLevelURL(".", import.meta.url));
11+
`;
12+
var tsup_config_default = defineConfig({
13+
entry: ["src/mod.ts", "src/cli.ts"],
14+
platform: "node",
15+
bundle: true,
16+
format: "esm",
17+
clean: true,
18+
minify: true,
19+
shims: true,
20+
dts: true,
21+
sourcemap: true,
22+
external: [
23+
"yoga-wasm-web",
24+
"@sentry/profiling-node",
25+
"bundle-require",
26+
"esbuild"
27+
],
28+
define: {
29+
"process.env.DEV": JSON.stringify(false),
30+
"process.env.NODE_ENV": JSON.stringify("production"),
31+
"process.env.SENTRY_DSN": JSON.stringify(process.env.SENTRY_DSN || "")
32+
},
33+
banner(ctx) {
34+
return { js: `#!/usr/bin/env node${createRequireSnippet}` };
35+
},
36+
esbuildOptions(options) {
37+
options.alias = {
38+
...options.alias,
39+
"react-devtools-core": "./rdt-mock.js"
40+
};
41+
return options;
42+
},
43+
esbuildPlugins: [
44+
// @ts-ignore
45+
Macros(),
46+
sentryEsbuildPlugin({
47+
authToken: process.env.SENTRY_AUTH_TOKEN,
48+
org: "rivet-gaming",
49+
project: "actor-core-cli"
50+
}),
51+
{
52+
name: "remove-devtools-import",
53+
setup(build) {
54+
build.onEnd((result) => {
55+
result.outputFiles = result.outputFiles?.filter(
56+
(file) => !file.path.includes("dist/devtools-")
57+
);
58+
});
59+
}
60+
}
61+
]
62+
});
63+
export {
64+
tsup_config_default as default
65+
};
66+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidHN1cC5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9faW5qZWN0ZWRfZmlsZW5hbWVfXyA9IFwiL1VzZXJzL25hdGhhbi9yaXZldC9hY3Rvci1jb3JlL3BhY2thZ2VzL2FjdG9yLWNvcmUtY2xpL3RzdXAuY29uZmlnLnRzXCI7Y29uc3QgX19pbmplY3RlZF9kaXJuYW1lX18gPSBcIi9Vc2Vycy9uYXRoYW4vcml2ZXQvYWN0b3ItY29yZS9wYWNrYWdlcy9hY3Rvci1jb3JlLWNsaVwiO2NvbnN0IF9faW5qZWN0ZWRfaW1wb3J0X21ldGFfdXJsX18gPSBcImZpbGU6Ly8vVXNlcnMvbmF0aGFuL3JpdmV0L2FjdG9yLWNvcmUvcGFja2FnZXMvYWN0b3ItY29yZS1jbGkvdHN1cC5jb25maWcudHNcIjtpbXBvcnQgeyBzZW50cnlFc2J1aWxkUGx1Z2luIH0gZnJvbSBcIkBzZW50cnkvZXNidWlsZC1wbHVnaW5cIjtcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ0c3VwXCI7XG5pbXBvcnQgTWFjcm9zIGZyb20gXCJ1bnBsdWdpbi1tYWNyb3MvZXNidWlsZFwiO1xuXG5jb25zdCBjcmVhdGVSZXF1aXJlU25pcHBldCA9IGBcbmltcG9ydCB7IGNyZWF0ZVJlcXVpcmUgYXMgdG9wTGV2ZWxDcmVhdGVSZXF1aXJlIH0gZnJvbSBcIm5vZGU6bW9kdWxlXCI7XG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIGFzIHRvcExldmVsRmlsZVVSTFRvUGF0aCwgVVJMIGFzIHRvcExldmVsVVJMIH0gZnJvbSBcIm5vZGU6dXJsXCI7XG5jb25zdCByZXF1aXJlID0gdG9wTGV2ZWxDcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCk7XG5jb25zdCBfX2ZpbGVuYW1lID0gdG9wTGV2ZWxGaWxlVVJMVG9QYXRoKGltcG9ydC5tZXRhLnVybCk7XG5jb25zdCBfX2Rpcm5hbWUgPSB0b3BMZXZlbEZpbGVVUkxUb1BhdGgobmV3IHRvcExldmVsVVJMKFwiLlwiLCBpbXBvcnQubWV0YS51cmwpKTtcbmA7XG5cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG5cdGVudHJ5OiBbXCJzcmMvbW9kLnRzXCIsIFwic3JjL2NsaS50c1wiXSxcblx0cGxhdGZvcm06IFwibm9kZVwiLFxuXHRidW5kbGU6IHRydWUsXG5cdGZvcm1hdDogXCJlc21cIixcblx0Y2xlYW46IHRydWUsXG5cdG1pbmlmeTogdHJ1ZSxcblx0c2hpbXM6IHRydWUsXG5cdGR0czogdHJ1ZSxcblx0c291cmNlbWFwOiB0cnVlLFxuXHRleHRlcm5hbDogW1xuXHRcdFwieW9nYS13YXNtLXdlYlwiLFxuXHRcdFwiQHNlbnRyeS9wcm9maWxpbmctbm9kZVwiLFxuXHRcdFwiYnVuZGxlLXJlcXVpcmVcIixcblx0XHRcImVzYnVpbGRcIixcblx0XSxcblx0ZGVmaW5lOiB7XG5cdFx0XCJwcm9jZXNzLmVudi5ERVZcIjogSlNPTi5zdHJpbmdpZnkoZmFsc2UpLFxuXHRcdFwicHJvY2Vzcy5lbnYuTk9ERV9FTlZcIjogSlNPTi5zdHJpbmdpZnkoXCJwcm9kdWN0aW9uXCIpLFxuXHRcdFwicHJvY2Vzcy5lbnYuU0VOVFJZX0RTTlwiOiBKU09OLnN0cmluZ2lmeShwcm9jZXNzLmVudi5TRU5UUllfRFNOIHx8IFwiXCIpLFxuXHR9LFxuXHRiYW5uZXIoY3R4KSB7XG5cdFx0cmV0dXJuIHsganM6IGAjIS91c3IvYmluL2VudiBub2RlJHtjcmVhdGVSZXF1aXJlU25pcHBldH1gIH07XG5cdH0sXG5cdGVzYnVpbGRPcHRpb25zKG9wdGlvbnMpIHtcblx0XHRvcHRpb25zLmFsaWFzID0ge1xuXHRcdFx0Li4ub3B0aW9ucy5hbGlhcyxcblx0XHRcdFwicmVhY3QtZGV2dG9vbHMtY29yZVwiOiBcIi4vcmR0LW1vY2suanNcIixcblx0XHR9O1xuXHRcdHJldHVybiBvcHRpb25zO1xuXHR9LFxuXHRlc2J1aWxkUGx1Z2luczogW1xuXHRcdC8vIEB0cy1pZ25vcmVcblx0XHRNYWNyb3MoKSxcblx0XHRzZW50cnlFc2J1aWxkUGx1Z2luKHtcblx0XHRcdGF1dGhUb2tlbjogcHJvY2Vzcy5lbnYuU0VOVFJZX0FVVEhfVE9LRU4sXG5cdFx0XHRvcmc6IFwicml2ZXQtZ2FtaW5nXCIsXG5cdFx0XHRwcm9qZWN0OiBcImFjdG9yLWNvcmUtY2xpXCIsXG5cdFx0fSksXG5cdFx0e1xuXHRcdFx0bmFtZTogXCJyZW1vdmUtZGV2dG9vbHMtaW1wb3J0XCIsXG5cdFx0XHRzZXR1cChidWlsZCkge1xuXHRcdFx0XHRidWlsZC5vbkVuZCgocmVzdWx0KSA9PiB7XG5cdFx0XHRcdFx0cmVzdWx0Lm91dHB1dEZpbGVzID0gcmVzdWx0Lm91dHB1dEZpbGVzPy5maWx0ZXIoXG5cdFx0XHRcdFx0XHQoZmlsZSkgPT4gIWZpbGUucGF0aC5pbmNsdWRlcyhcImRpc3QvZGV2dG9vbHMtXCIpLFxuXHRcdFx0XHRcdCk7XG5cdFx0XHRcdH0pO1xuXHRcdFx0fSxcblx0XHR9LFxuXHRdLFxufSk7XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQWdULFNBQVMsMkJBQTJCO0FBQ3BWLFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sWUFBWTtBQUVuQixJQUFNLHVCQUF1QjtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQVE3QixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMzQixPQUFPLENBQUMsY0FBYyxZQUFZO0FBQUEsRUFDbEMsVUFBVTtBQUFBLEVBQ1YsUUFBUTtBQUFBLEVBQ1IsUUFBUTtBQUFBLEVBQ1IsT0FBTztBQUFBLEVBQ1AsUUFBUTtBQUFBLEVBQ1IsT0FBTztBQUFBLEVBQ1AsS0FBSztBQUFBLEVBQ0wsV0FBVztBQUFBLEVBQ1gsVUFBVTtBQUFBLElBQ1Q7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxFQUNEO0FBQUEsRUFDQSxRQUFRO0FBQUEsSUFDUCxtQkFBbUIsS0FBSyxVQUFVLEtBQUs7QUFBQSxJQUN2Qyx3QkFBd0IsS0FBSyxVQUFVLFlBQVk7QUFBQSxJQUNuRCwwQkFBMEIsS0FBSyxVQUFVLFFBQVEsSUFBSSxjQUFjLEVBQUU7QUFBQSxFQUN0RTtBQUFBLEVBQ0EsT0FBTyxLQUFLO0FBQ1gsV0FBTyxFQUFFLElBQUksc0JBQXNCLG9CQUFvQixHQUFHO0FBQUEsRUFDM0Q7QUFBQSxFQUNBLGVBQWUsU0FBUztBQUN2QixZQUFRLFFBQVE7QUFBQSxNQUNmLEdBQUcsUUFBUTtBQUFBLE1BQ1gsdUJBQXVCO0FBQUEsSUFDeEI7QUFDQSxXQUFPO0FBQUEsRUFDUjtBQUFBLEVBQ0EsZ0JBQWdCO0FBQUE7QUFBQSxJQUVmLE9BQU87QUFBQSxJQUNQLG9CQUFvQjtBQUFBLE1BQ25CLFdBQVcsUUFBUSxJQUFJO0FBQUEsTUFDdkIsS0FBSztBQUFBLE1BQ0wsU0FBUztBQUFBLElBQ1YsQ0FBQztBQUFBLElBQ0Q7QUFBQSxNQUNDLE1BQU07QUFBQSxNQUNOLE1BQU0sT0FBTztBQUNaLGNBQU0sTUFBTSxDQUFDLFdBQVc7QUFDdkIsaUJBQU8sY0FBYyxPQUFPLGFBQWE7QUFBQSxZQUN4QyxDQUFDLFNBQVMsQ0FBQyxLQUFLLEtBQUssU0FBUyxnQkFBZ0I7QUFBQSxVQUMvQztBQUFBLFFBQ0QsQ0FBQztBQUFBLE1BQ0Y7QUFBQSxJQUNEO0FBQUEsRUFDRDtBQUNELENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==

packages/actor-core/package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,21 @@
121121
"types": "./dist/actor/protocol/http/rpc.d.cts",
122122
"default": "./dist/actor/protocol/http/rpc.cjs"
123123
}
124+
},
125+
"./test": {
126+
"import": {
127+
"types": "./dist/test/mod.d.ts",
128+
"default": "./dist/test/mod.js"
129+
},
130+
"require": {
131+
"types": "./dist/test/mod.d.cts",
132+
"default": "./dist/test/mod.cjs"
133+
}
124134
}
125135
},
126136
"sideEffects": false,
127137
"scripts": {
128-
"build": "tsup src/mod.ts src/client/mod.ts src/common/log.ts src/actor/errors.ts src/topologies/coordinate/mod.ts src/topologies/partition/mod.ts src/utils.ts src/driver-helpers/mod.ts src/cli/mod.ts src/actor/protocol/inspector/mod.ts src/actor/protocol/http/rpc.ts",
138+
"build": "tsup src/mod.ts src/client/mod.ts src/common/log.ts src/actor/errors.ts src/topologies/coordinate/mod.ts src/topologies/partition/mod.ts src/utils.ts src/driver-helpers/mod.ts src/cli/mod.ts src/actor/protocol/inspector/mod.ts src/actor/protocol/http/rpc.ts src/test/mod.ts",
129139
"check-types": "tsc --noEmit",
130140
"boop": "tsc --outDir dist/test -d",
131141
"test": "vitest run",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { DriverConfigSchema } from "@/driver-helpers/mod";
2+
import { z } from "zod";
3+
4+
export const ConfigSchema = DriverConfigSchema.extend({
5+
hostname: z
6+
.string()
7+
.optional()
8+
.default(process.env.HOSTNAME ?? "127.0.0.1"),
9+
port: z
10+
.number()
11+
.optional()
12+
.default(Number.parseInt(process.env.PORT ?? "6420")),
13+
}).default({});
14+
export type InputConfig = z.input<typeof ConfigSchema>;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import type { ActorDriver, KvKey, KvValue, AnyActorInstance } from "@/driver-helpers/mod";
2+
import type { TestGlobalState } from "./global_state";
3+
4+
export interface ActorDriverContext {
5+
state: TestGlobalState;
6+
}
7+
8+
export class TestActorDriver implements ActorDriver {
9+
#state: TestGlobalState;
10+
11+
constructor(state: TestGlobalState) {
12+
this.#state = state;
13+
}
14+
15+
get context(): ActorDriverContext {
16+
return { state: this.#state };
17+
}
18+
19+
async kvGet(actorId: string, key: KvKey): Promise<KvValue | undefined> {
20+
const serializedKey = this.#serializeKey(key);
21+
const value = this.#state.getKv(actorId, serializedKey);
22+
23+
if (value !== undefined) return JSON.parse(value);
24+
return undefined;
25+
}
26+
27+
async kvGetBatch(
28+
actorId: string,
29+
keys: KvKey[],
30+
): Promise<(KvValue | undefined)[]> {
31+
return keys.map((key) => {
32+
const serializedKey = this.#serializeKey(key);
33+
const value = this.#state.getKv(actorId, serializedKey);
34+
if (value !== undefined) return JSON.parse(value);
35+
return undefined;
36+
});
37+
}
38+
39+
async kvPut(actorId: string, key: KvKey, value: KvValue): Promise<void> {
40+
const serializedKey = this.#serializeKey(key);
41+
this.#state.putKv(actorId, serializedKey, JSON.stringify(value));
42+
}
43+
44+
async kvPutBatch(
45+
actorId: string,
46+
keyValuePairs: [KvKey, KvValue][],
47+
): Promise<void> {
48+
for (const [key, value] of keyValuePairs) {
49+
const serializedKey = this.#serializeKey(key);
50+
this.#state.putKv(actorId, serializedKey, JSON.stringify(value));
51+
}
52+
}
53+
54+
async kvDelete(actorId: string, key: KvKey): Promise<void> {
55+
const serializedKey = this.#serializeKey(key);
56+
this.#state.deleteKv(actorId, serializedKey);
57+
}
58+
59+
async kvDeleteBatch(actorId: string, keys: KvKey[]): Promise<void> {
60+
for (const key of keys) {
61+
const serializedKey = this.#serializeKey(key);
62+
this.#state.deleteKv(actorId, serializedKey);
63+
}
64+
}
65+
66+
async setAlarm(actor: AnyActorInstance, timestamp: number): Promise<void> {
67+
setTimeout(() => {
68+
actor.onAlarm();
69+
}, timestamp - Date.now());
70+
}
71+
72+
// Simple key serialization without depending on keys.ts
73+
#serializeKey(key: KvKey): string {
74+
return JSON.stringify(key);
75+
}
76+
}

0 commit comments

Comments
 (0)