Skip to content
This repository was archived by the owner on Oct 22, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
"packages/components/*",
"packages/misc/*",
"packages/frameworks/*",
"examples/*",
"templates/*"
"examples/*"
],
"scripts": {
"start": "npx turbo watch build",
Expand Down
12 changes: 9 additions & 3 deletions packages/actor-core/src/test/driver/actor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { ActorDriver, AnyActorInstance } from "@/driver-helpers/mod";
import type { TestGlobalState } from "./global_state";

export type ActorDriverContext = Record<never, never>;
export interface ActorDriverContext {
// Used to test that the actor context works from tests
isTest: boolean;
}

export class TestActorDriver implements ActorDriver {
#state: TestGlobalState;
Expand All @@ -11,7 +14,9 @@ export class TestActorDriver implements ActorDriver {
}

getContext(_actorId: string): ActorDriverContext {
return {};
return {
isTest: true,
};
}

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

async setAlarm(actor: AnyActorInstance, timestamp: number): Promise<void> {
const delay = Math.max(timestamp - Date.now(), 0);
setTimeout(() => {
actor.onAlarm();
}, timestamp - Date.now());
}, delay);
}
}
26 changes: 4 additions & 22 deletions packages/actor-core/src/test/driver/global_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class ActorState {
name: string;
tags: ActorTags;

// Persisted data
persistedData: unknown = undefined;

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

/**
* Global state singleton for the memory driver
* Global state singleton for the test driver
*/
export class TestGlobalState {
// Single map for all actor state
#actors: Map<string, ActorState> = new Map();

/**
* Get an actor by ID, throwing an error if it doesn't exist
* @private
*/
#getActor(actorId: string): ActorState {
const actor = this.#actors.get(actorId);
if (!actor) {
Expand All @@ -46,9 +42,6 @@ export class TestGlobalState {
this.#getActor(actorId).persistedData = data;
}

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

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

/**
* Get actor state
*/
getActor(actorId: string): ActorState | undefined {
return this.#actors.get(actorId);
}

/**
* Check if an actor exists
*/
hasActor(actorId: string): boolean {
return this.#actors.has(actorId);
getAllActors(): ActorState[] {
return Array.from(this.#actors.values());
}
}
43 changes: 35 additions & 8 deletions packages/actor-core/src/test/driver/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,24 @@ import type {
ManagerDriver,
} from "@/driver-helpers/mod";
import type { TestGlobalState } from "./global_state";
import { ManagerInspector } from "@/inspector/manager";
import type { ActorCoreApp } from "@/app/mod";

export class TestManagerDriver implements ManagerDriver {
#state: TestGlobalState;

constructor(state: TestGlobalState) {
/**
* @internal
*/
inspector: ManagerInspector = new ManagerInspector(this, {
getAllActors: () => this.#state.getAllActors(),
getAllTypesOfActors: () => Object.keys(this.app.config.actors),
});

constructor(
private readonly app: ActorCoreApp<any>,
state: TestGlobalState,
) {
this.#state = state;
}

Expand All @@ -37,13 +50,24 @@ export class TestManagerDriver implements ManagerDriver {
name,
tags,
}: GetWithTagsInput): Promise<GetActorOutput | undefined> {
// TODO: Update tag search to use inverse tree
const serializedSearchTags = JSON.stringify(tags);
const actor = this.#state.findActor(
(actor) =>
actor.name === name &&
JSON.stringify(actor.tags) === serializedSearchTags,
);
// NOTE: This is a slow implementation that checks each actor individually.
// This can be optimized with an index in the future.

// Search through all actors to find a match
// Find actors with a superset of the queried tags
const actor = this.#state.findActor((actor) => {
if (actor.name !== name) return false;

for (const key in tags) {
const value = tags[key];

// If actor doesn't have this tag key, or values don't match, it's not a match
if (actor.tags[key] === undefined || actor.tags[key] !== value) {
return false;
}
}
return true;
});

if (actor) {
return {
Expand All @@ -63,6 +87,9 @@ export class TestManagerDriver implements ManagerDriver {
}: CreateActorInput): Promise<CreateActorOutput> {
const actorId = crypto.randomUUID();
this.#state.createActor(actorId, name, tags);

this.inspector.onActorsChange(this.#state.getAllActors());

return {
endpoint: buildActorEndpoint(baseUrl, actorId),
};
Expand Down
2 changes: 1 addition & 1 deletion packages/actor-core/src/test/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function createRouter(
if (!config.drivers.manager || !config.drivers.actor) {
const memoryState = new TestGlobalState();
if (!config.drivers.manager) {
config.drivers.manager = new TestManagerDriver(memoryState);
config.drivers.manager = new TestManagerDriver(app, memoryState);
}
if (!config.drivers.actor) {
config.drivers.actor = new TestActorDriver(memoryState);
Expand Down
9 changes: 3 additions & 6 deletions packages/actor-core/tests/vars.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,7 @@ describe("Actor Vars", () => {

// Define actor with createVars that uses driver context
interface DriverVars {
hasState: boolean;
driverName: string;
hasDriverCtx: boolean;
}

const driverCtxActor = actor({
Expand All @@ -187,8 +186,7 @@ describe("Actor Vars", () => {
createVars: (c, driverCtx: any): DriverVars => {
// In test environment, we get a context with a state property
return {
hasState: Boolean(driverCtx?.state),
driverName: "memory",
hasDriverCtx: driverCtx?.isTest,
};
},
actions: {
Expand All @@ -212,8 +210,7 @@ describe("Actor Vars", () => {
const vars = await instance.getVars();

// Verify we can access driver context
expect(vars.hasState).toBe(true);
expect(vars.driverName).toBe("memory");
expect(vars.hasDriverCtx).toBe(true);
});
});
});
Loading