/* Copyright 2026 Lotus Guild SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ import { describe, expect, test } from "vitest"; import { type AudioProcessorOptions } from "livekit-client"; import { LotusDenoiseProcessor } from "./lotusDenoiseProcessor"; function makeProcessor(): LotusDenoiseProcessor { return new LotusDenoiseProcessor({ model: "rnnoise", assetBase: "https://example.invalid/denoise/", gate: false, gateThreshold: -45, floor: 0.15, }); } describe("LotusDenoiseProcessor.restart", () => { test("falls back to processedTrack = undefined (never the raw LiveKit track) when graph rebuild fails", async () => { const processor = makeProcessor(); // Stub out the AudioContext/graph plumbing: pretend the context is fine // but the graph rebuild (wasm load / worklet construction) throws, which // is the path this test targets. // eslint-disable-next-line @typescript-eslint/no-explicit-any (processor as any).ensureContext = async (): Promise => { await Promise.resolve(); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any (processor as any).buildGraph = async (): Promise => { await Promise.resolve(); throw new Error("simulated graph build failure"); }; const rawTrack = { stop: (): void => undefined, } as unknown as MediaStreamTrack; await processor.restart({ track: rawTrack, } as unknown as AudioProcessorOptions); // Must NOT be the raw, LiveKit-owned track: LiveKit's // internalStopProcessor() calls `processor.processedTrack?.stop()` then // re-publishes the same `_mediaStreamTrack` object, which would kill the // live mic on the next stopProcessor()/teardown if we handed it back here. expect(processor.processedTrack).toBeUndefined(); expect(processor.processedTrack).not.toBe(rawTrack); }); });