Skip to content

Commit 6fffd2a

Browse files
committed
fix(actor-core): update test driver to match new memory driver implementation (#874)
1 parent 867d34d commit 6fffd2a

File tree

6 files changed

+53
-42
lines changed

6 files changed

+53
-42
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
"packages/components/*",
1010
"packages/misc/*",
1111
"packages/frameworks/*",
12-
"examples/*",
13-
"templates/*"
12+
"examples/*"
1413
],
1514
"scripts": {
1615
"start": "npx turbo watch build",

packages/actor-core/src/test/driver/actor.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import type { ActorDriver, AnyActorInstance } from "@/driver-helpers/mod";
22
import type { TestGlobalState } from "./global_state";
33

4-
export type ActorDriverContext = Record<never, never>;
4+
export interface ActorDriverContext {
5+
// Used to test that the actor context works from tests
6+
isTest: boolean;
7+
}
58

69
export class TestActorDriver implements ActorDriver {
710
#state: TestGlobalState;
@@ -11,7 +14,9 @@ export class TestActorDriver implements ActorDriver {
1114
}
1215

1316
getContext(_actorId: string): ActorDriverContext {
14-
return {};
17+
return {
18+
isTest: true,
19+
};
1520
}
1621

1722
async readPersistedData(actorId: string): Promise<unknown | undefined> {
@@ -23,8 +28,9 @@ export class TestActorDriver implements ActorDriver {
2328
}
2429

2530
async setAlarm(actor: AnyActorInstance, timestamp: number): Promise<void> {
31+
const delay = Math.max(timestamp - Date.now(), 0);
2632
setTimeout(() => {
2733
actor.onAlarm();
28-
}, timestamp - Date.now());
34+
}, delay);
2935
}
3036
}

packages/actor-core/src/test/driver/global_state.ts

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export class ActorState {
1010
name: string;
1111
tags: ActorTags;
1212

13+
// Persisted data
1314
persistedData: unknown = undefined;
1415

1516
constructor(id: string, name: string, tags: ActorTags) {
@@ -20,16 +21,11 @@ export class ActorState {
2021
}
2122

2223
/**
23-
* Global state singleton for the memory driver
24+
* Global state singleton for the test driver
2425
*/
2526
export class TestGlobalState {
26-
// Single map for all actor state
2727
#actors: Map<string, ActorState> = new Map();
2828

29-
/**
30-
* Get an actor by ID, throwing an error if it doesn't exist
31-
* @private
32-
*/
3329
#getActor(actorId: string): ActorState {
3430
const actor = this.#actors.get(actorId);
3531
if (!actor) {
@@ -46,9 +42,6 @@ export class TestGlobalState {
4642
this.#getActor(actorId).persistedData = data;
4743
}
4844

49-
/**
50-
* Create or update an actor
51-
*/
5245
createActor(actorId: string, name: string, tags: ActorTags): void {
5346
// Create actor state if it doesn't exist
5447
if (!this.#actors.has(actorId)) {
@@ -58,11 +51,6 @@ export class TestGlobalState {
5851
}
5952
}
6053

61-
/**
62-
* Find an actor by a filter function
63-
* @param filter A function that takes an ActorState and returns true if it matches the filter criteria
64-
* @returns The matching ActorState or undefined if no match is found
65-
*/
6654
findActor(filter: (actor: ActorState) => boolean): ActorState | undefined {
6755
for (const actor of this.#actors.values()) {
6856
if (filter(actor)) {
@@ -72,17 +60,11 @@ export class TestGlobalState {
7260
return undefined;
7361
}
7462

75-
/**
76-
* Get actor state
77-
*/
7863
getActor(actorId: string): ActorState | undefined {
7964
return this.#actors.get(actorId);
8065
}
8166

82-
/**
83-
* Check if an actor exists
84-
*/
85-
hasActor(actorId: string): boolean {
86-
return this.#actors.has(actorId);
67+
getAllActors(): ActorState[] {
68+
return Array.from(this.#actors.values());
8769
}
8870
}

packages/actor-core/src/test/driver/manager.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,24 @@ import type {
77
ManagerDriver,
88
} from "@/driver-helpers/mod";
99
import type { TestGlobalState } from "./global_state";
10+
import { ManagerInspector } from "@/inspector/manager";
11+
import type { ActorCoreApp } from "@/app/mod";
1012

1113
export class TestManagerDriver implements ManagerDriver {
1214
#state: TestGlobalState;
1315

14-
constructor(state: TestGlobalState) {
16+
/**
17+
* @internal
18+
*/
19+
inspector: ManagerInspector = new ManagerInspector(this, {
20+
getAllActors: () => this.#state.getAllActors(),
21+
getAllTypesOfActors: () => Object.keys(this.app.config.actors),
22+
});
23+
24+
constructor(
25+
private readonly app: ActorCoreApp<any>,
26+
state: TestGlobalState,
27+
) {
1528
this.#state = state;
1629
}
1730

@@ -37,13 +50,24 @@ export class TestManagerDriver implements ManagerDriver {
3750
name,
3851
tags,
3952
}: GetWithTagsInput): Promise<GetActorOutput | undefined> {
40-
// TODO: Update tag search to use inverse tree
41-
const serializedSearchTags = JSON.stringify(tags);
42-
const actor = this.#state.findActor(
43-
(actor) =>
44-
actor.name === name &&
45-
JSON.stringify(actor.tags) === serializedSearchTags,
46-
);
53+
// NOTE: This is a slow implementation that checks each actor individually.
54+
// This can be optimized with an index in the future.
55+
56+
// Search through all actors to find a match
57+
// Find actors with a superset of the queried tags
58+
const actor = this.#state.findActor((actor) => {
59+
if (actor.name !== name) return false;
60+
61+
for (const key in tags) {
62+
const value = tags[key];
63+
64+
// If actor doesn't have this tag key, or values don't match, it's not a match
65+
if (actor.tags[key] === undefined || actor.tags[key] !== value) {
66+
return false;
67+
}
68+
}
69+
return true;
70+
});
4771

4872
if (actor) {
4973
return {
@@ -63,6 +87,9 @@ export class TestManagerDriver implements ManagerDriver {
6387
}: CreateActorInput): Promise<CreateActorOutput> {
6488
const actorId = crypto.randomUUID();
6589
this.#state.createActor(actorId, name, tags);
90+
91+
this.inspector.onActorsChange(this.#state.getAllActors());
92+
6693
return {
6794
endpoint: buildActorEndpoint(baseUrl, actorId),
6895
};

packages/actor-core/src/test/mod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function createRouter(
2929
if (!config.drivers.manager || !config.drivers.actor) {
3030
const memoryState = new TestGlobalState();
3131
if (!config.drivers.manager) {
32-
config.drivers.manager = new TestManagerDriver(memoryState);
32+
config.drivers.manager = new TestManagerDriver(app, memoryState);
3333
}
3434
if (!config.drivers.actor) {
3535
config.drivers.actor = new TestActorDriver(memoryState);

packages/actor-core/tests/vars.test.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,7 @@ describe("Actor Vars", () => {
177177

178178
// Define actor with createVars that uses driver context
179179
interface DriverVars {
180-
hasState: boolean;
181-
driverName: string;
180+
hasDriverCtx: boolean;
182181
}
183182

184183
const driverCtxActor = actor({
@@ -187,8 +186,7 @@ describe("Actor Vars", () => {
187186
createVars: (c, driverCtx: any): DriverVars => {
188187
// In test environment, we get a context with a state property
189188
return {
190-
hasState: Boolean(driverCtx?.state),
191-
driverName: "memory",
189+
hasDriverCtx: driverCtx?.isTest,
192190
};
193191
},
194192
actions: {
@@ -212,8 +210,7 @@ describe("Actor Vars", () => {
212210
const vars = await instance.getVars();
213211

214212
// Verify we can access driver context
215-
expect(vars.hasState).toBe(true);
216-
expect(vars.driverName).toBe("memory");
213+
expect(vars.hasDriverCtx).toBe(true);
217214
});
218215
});
219216
});

0 commit comments

Comments
 (0)