import { Logger } from "@nestjs/common"; import type { AgentProviderConfig } from "@prisma/client"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { PrismaService } from "../../prisma/prisma.service"; import { AgentProviderRegistry } from "../agents/agent-provider.registry"; import { OpenClawProviderFactory } from "./openclaw/openclaw.provider-factory"; import { ProvidersModule } from "./providers.module"; type MockOpenClawProvider = { providerId: string; validateBaseUrl: ReturnType; validateToken: ReturnType; isAvailable: ReturnType; }; describe("ProvidersModule", () => { let moduleRef: ProvidersModule; let prisma: { agentProviderConfig: { findMany: ReturnType; }; }; let registry: { registerProvider: ReturnType; }; let factory: { createProvider: ReturnType; }; const config: AgentProviderConfig = { id: "cfg-openclaw-1", workspaceId: "workspace-1", name: "openclaw-home", provider: "openclaw", gatewayUrl: "https://gateway.example.com", credentials: { apiToken: "enc:token-value" }, isActive: true, createdAt: new Date("2026-03-07T15:00:00.000Z"), updatedAt: new Date("2026-03-07T15:00:00.000Z"), }; beforeEach(() => { prisma = { agentProviderConfig: { findMany: vi.fn(), }, }; registry = { registerProvider: vi.fn(), }; factory = { createProvider: vi.fn(), }; moduleRef = new ProvidersModule( prisma as unknown as PrismaService, registry as unknown as AgentProviderRegistry, factory as unknown as OpenClawProviderFactory ); }); it("registers reachable OpenClaw providers", async () => { const provider: MockOpenClawProvider = { providerId: "openclaw-home", validateBaseUrl: vi.fn(), validateToken: vi.fn(), isAvailable: vi.fn().mockResolvedValue(true), }; prisma.agentProviderConfig.findMany.mockResolvedValue([config]); factory.createProvider.mockReturnValue(provider); await moduleRef.onModuleInit(); expect(prisma.agentProviderConfig.findMany).toHaveBeenCalledWith({ where: { provider: "openclaw", isActive: true, }, orderBy: [{ createdAt: "asc" }, { id: "asc" }], }); expect(factory.createProvider).toHaveBeenCalledWith(config); expect(provider.validateBaseUrl).toHaveBeenCalledTimes(1); expect(provider.validateToken).toHaveBeenCalledTimes(1); expect(provider.isAvailable).toHaveBeenCalledTimes(1); expect(registry.registerProvider).toHaveBeenCalledWith(provider); }); it("skips provider registration when gateway is unreachable", async () => { const warnSpy = vi.spyOn(Logger.prototype, "warn").mockImplementation(() => undefined); const provider: MockOpenClawProvider = { providerId: "openclaw-home", validateBaseUrl: vi.fn(), validateToken: vi.fn(), isAvailable: vi.fn().mockResolvedValue(false), }; prisma.agentProviderConfig.findMany.mockResolvedValue([config]); factory.createProvider.mockReturnValue(provider); await moduleRef.onModuleInit(); expect(registry.registerProvider).not.toHaveBeenCalled(); expect(warnSpy).toHaveBeenCalledWith( expect.stringContaining("Skipping OpenClaw provider openclaw-home") ); }); it("skips provider registration when token decryption fails", async () => { const errorSpy = vi.spyOn(Logger.prototype, "error").mockImplementation(() => undefined); const provider: MockOpenClawProvider = { providerId: "openclaw-home", validateBaseUrl: vi.fn(), validateToken: vi.fn().mockImplementation(() => { throw new Error("Failed to decrypt API token"); }), isAvailable: vi.fn().mockResolvedValue(true), }; prisma.agentProviderConfig.findMany.mockResolvedValue([config]); factory.createProvider.mockReturnValue(provider); await moduleRef.onModuleInit(); expect(registry.registerProvider).not.toHaveBeenCalled(); expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("token decryption failed")); expect(provider.isAvailable).not.toHaveBeenCalled(); }); });