diff --git a/package.json b/package.json index 12648b8c4..d3d98c103 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,7 @@ "packages/components/*", "packages/misc/*", "packages/frameworks/*", - "examples/*", - "templates/*" + "examples/*" ], "scripts": { "start": "npx turbo watch build", diff --git a/packages/actor-core/src/test/driver/actor.ts b/packages/actor-core/src/test/driver/actor.ts index 1ad2238d2..4840e9f81 100644 --- a/packages/actor-core/src/test/driver/actor.ts +++ b/packages/actor-core/src/test/driver/actor.ts @@ -1,7 +1,10 @@ import type { ActorDriver, AnyActorInstance } from "@/driver-helpers/mod"; import type { TestGlobalState } from "./global_state"; -export type ActorDriverContext = Record; +export interface ActorDriverContext { + // Used to test that the actor context works from tests + isTest: boolean; +} export class TestActorDriver implements ActorDriver { #state: TestGlobalState; @@ -11,7 +14,9 @@ export class TestActorDriver implements ActorDriver { } getContext(_actorId: string): ActorDriverContext { - return {}; + return { + isTest: true, + }; } async readPersistedData(actorId: string): Promise { @@ -23,8 +28,9 @@ export class TestActorDriver implements ActorDriver { } async setAlarm(actor: AnyActorInstance, timestamp: number): Promise { + const delay = Math.max(timestamp - Date.now(), 0); setTimeout(() => { actor.onAlarm(); - }, timestamp - Date.now()); + }, delay); } } diff --git a/packages/actor-core/src/test/driver/global_state.ts b/packages/actor-core/src/test/driver/global_state.ts index e3ea6a98a..3fdb2fa97 100644 --- a/packages/actor-core/src/test/driver/global_state.ts +++ b/packages/actor-core/src/test/driver/global_state.ts @@ -10,6 +10,7 @@ export class ActorState { name: string; tags: ActorTags; + // Persisted data persistedData: unknown = undefined; constructor(id: string, name: string, tags: ActorTags) { @@ -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 = 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) { @@ -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)) { @@ -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)) { @@ -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()); } } diff --git a/packages/actor-core/src/test/driver/manager.ts b/packages/actor-core/src/test/driver/manager.ts index ef80b1196..1d30494e7 100644 --- a/packages/actor-core/src/test/driver/manager.ts +++ b/packages/actor-core/src/test/driver/manager.ts @@ -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, + state: TestGlobalState, + ) { this.#state = state; } @@ -37,13 +50,24 @@ export class TestManagerDriver implements ManagerDriver { name, tags, }: GetWithTagsInput): Promise { - // 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 { @@ -63,6 +87,9 @@ export class TestManagerDriver implements ManagerDriver { }: CreateActorInput): Promise { const actorId = crypto.randomUUID(); this.#state.createActor(actorId, name, tags); + + this.inspector.onActorsChange(this.#state.getAllActors()); + return { endpoint: buildActorEndpoint(baseUrl, actorId), }; diff --git a/packages/actor-core/src/test/mod.ts b/packages/actor-core/src/test/mod.ts index 679a3f5aa..c254040ce 100644 --- a/packages/actor-core/src/test/mod.ts +++ b/packages/actor-core/src/test/mod.ts @@ -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); diff --git a/packages/actor-core/tests/vars.test.ts b/packages/actor-core/tests/vars.test.ts index 8d28ac4dc..e9f597621 100644 --- a/packages/actor-core/tests/vars.test.ts +++ b/packages/actor-core/tests/vars.test.ts @@ -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({ @@ -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: { @@ -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); }); }); });