import { describe, expect, it } from "@suaveplan/testing/runner"; import z from "zod"; import { AbstractAudioFxPlugin } from "../base/audio-fx-plugin.js"; import { AUDIO_FX_CODES, type AudioFxError, } from "../error-codes/error-codes.js"; import { PluginRegistry } from "./registry.js"; const SCHEMA = z.object({}).strict(); const STEREO_IO = { inputs: [{ count: 2 as const }], outputs: [{ count: "preserve" as const }], }; class FixturePlugin extends AbstractAudioFxPlugin { public constructor(id: string) { super({ metadata: { id, name: id, version: "1.0.0", category: "utility" }, schema: SCHEMA, io: STEREO_IO, parameters: [], supportedSchemaVersion: 1, }); } protected processBlock( input: Float32Array[], output: Float32Array[] ): void { for (let ch = 0; ch < input.length; ch++) { output[ch]!.set(input[ch]!); } } } describe("PluginRegistry: register + create", () => { it("registers a factory and creates fresh instances", { timeout: 5_000, }, () => { const r = new PluginRegistry(); r.register("fx-a", () => new FixturePlugin("fx-a")); const a = r.create("fx-a"); const b = r.create("fx-a"); expect(a.metadata.id).toBe("fx-a"); expect(b.metadata.id).toBe("fx-a"); expect(a).not.toBe(b); }); it("has(pluginType) reflects registration state", { timeout: 5_000, }, () => { const r = new PluginRegistry(); expect(r.has("fx-a")).toBe(false); r.register("fx-a", () => new FixturePlugin("fx-a")); expect(r.has("fx-a")).toBe(true); }); it("list() returns registered types in insertion order", { timeout: 5_000, }, () => { const r = new PluginRegistry(); r.register("fx-b", () => new FixturePlugin("fx-b")); r.register("fx-a", () => new FixturePlugin("fx-a")); r.register("fx-c", () => new FixturePlugin("fx-c")); expect(r.list()).toEqual(["fx-b", "fx-a", "fx-c"]); }); it("re-registering a type overwrites the previous factory", { timeout: 5_000, }, () => { const r = new PluginRegistry(); r.register("fx-a", () => new FixturePlugin("fx-a")); r.register("fx-a", () => new FixturePlugin("fx-a-2")); expect(r.create("fx-a").metadata.id).toBe("fx-a-2"); expect(r.list()).toEqual(["fx-a"]); }); }); describe("PluginRegistry: missing factory rejection", () => { it("create() rejects an unregistered type with CHAIN_SLOT_NOT_FOUND", { timeout: 5_000, }, () => { const r = new PluginRegistry(); try { r.create("fx-missing"); } catch (error) { expect((error as AudioFxError).code).toBe( AUDIO_FX_CODES.CHAIN_SLOT_NOT_FOUND ); expect((error as AudioFxError).context).toEqual({ pluginType: "fx-missing", }); return; } throw new Error("expected CHAIN_SLOT_NOT_FOUND"); }); });