// npx vitest run src/__tests__/CloudService.test.ts import * as vscode from "vscode" import { CloudService } from "../CloudService" import { AuthService } from "../AuthService" import { SettingsService } from "../SettingsService" import { TelemetryService } from "@roo-code/telemetry" import { CloudServiceCallbacks } from "../types" vi.mock("vscode", () => ({ ExtensionContext: vi.fn(), window: { showInformationMessage: vi.fn(), showErrorMessage: vi.fn(), }, env: { openExternal: vi.fn(), }, Uri: { parse: vi.fn(), }, })) vi.mock("@roo-code/telemetry") vi.mock("../AuthService") vi.mock("../SettingsService") describe("CloudService", () => { let mockContext: vscode.ExtensionContext let mockAuthService: { initialize: ReturnType login: ReturnType logout: ReturnType isAuthenticated: ReturnType hasActiveSession: ReturnType getUserInfo: ReturnType getState: ReturnType getSessionToken: ReturnType handleCallback: ReturnType on: ReturnType off: ReturnType once: ReturnType emit: ReturnType } let mockSettingsService: { initialize: ReturnType getSettings: ReturnType getAllowList: ReturnType dispose: ReturnType } let mockTelemetryService: { hasInstance: ReturnType instance: { register: ReturnType } } beforeEach(() => { CloudService.resetInstance() mockContext = { secrets: { get: vi.fn(), store: vi.fn(), delete: vi.fn(), }, globalState: { get: vi.fn(), update: vi.fn(), }, extension: { packageJSON: { version: "1.0.0", }, }, } as unknown as vscode.ExtensionContext mockAuthService = { initialize: vi.fn(), login: vi.fn(), logout: vi.fn(), isAuthenticated: vi.fn().mockReturnValue(false), hasActiveSession: vi.fn().mockReturnValue(false), getUserInfo: vi.fn(), getState: vi.fn().mockReturnValue("logged-out"), getSessionToken: vi.fn(), handleCallback: vi.fn(), on: vi.fn(), off: vi.fn(), once: vi.fn(), emit: vi.fn(), } mockSettingsService = { initialize: vi.fn(), getSettings: vi.fn(), getAllowList: vi.fn(), dispose: vi.fn(), } mockTelemetryService = { hasInstance: vi.fn().mockReturnValue(true), instance: { register: vi.fn(), }, } vi.mocked(AuthService.createInstance).mockResolvedValue(mockAuthService as unknown as AuthService) Object.defineProperty(AuthService, "instance", { get: () => mockAuthService, configurable: true }) vi.mocked(SettingsService.createInstance).mockResolvedValue(mockSettingsService as unknown as SettingsService) Object.defineProperty(SettingsService, "instance", { get: () => mockSettingsService, configurable: true }) vi.mocked(TelemetryService.hasInstance).mockReturnValue(true) Object.defineProperty(TelemetryService, "instance", { get: () => mockTelemetryService.instance, configurable: true, }) }) afterEach(() => { vi.clearAllMocks() CloudService.resetInstance() }) describe("createInstance", () => { it("should create and initialize CloudService instance", async () => { const callbacks = { userChanged: vi.fn(), settingsChanged: vi.fn() } const cloudService = await CloudService.createInstance(mockContext, callbacks) expect(cloudService).toBeInstanceOf(CloudService) expect(AuthService.createInstance).toHaveBeenCalledWith(mockContext, expect.any(Function)) expect(SettingsService.createInstance).toHaveBeenCalledWith(mockContext, expect.any(Function)) }) it("should throw error if instance already exists", async () => { await CloudService.createInstance(mockContext) await expect(CloudService.createInstance(mockContext)).rejects.toThrow( "CloudService instance already created", ) }) }) describe("authentication methods", () => { let cloudService: CloudService let callbacks: CloudServiceCallbacks beforeEach(async () => { callbacks = { userChanged: vi.fn(), settingsChanged: vi.fn() } cloudService = await CloudService.createInstance(mockContext, callbacks) }) it("should delegate login to AuthService", async () => { await cloudService.login() expect(mockAuthService.login).toHaveBeenCalled() }) it("should delegate logout to AuthService", async () => { await cloudService.logout() expect(mockAuthService.logout).toHaveBeenCalled() }) it("should delegate isAuthenticated to AuthService", () => { const result = cloudService.isAuthenticated() expect(mockAuthService.isAuthenticated).toHaveBeenCalled() expect(result).toBe(false) }) it("should delegate hasActiveSession to AuthService", () => { const result = cloudService.hasActiveSession() expect(mockAuthService.hasActiveSession).toHaveBeenCalled() expect(result).toBe(false) }) it("should delegate getUserInfo to AuthService", async () => { await cloudService.getUserInfo() expect(mockAuthService.getUserInfo).toHaveBeenCalled() }) it("should delegate getAuthState to AuthService", () => { const result = cloudService.getAuthState() expect(mockAuthService.getState).toHaveBeenCalled() expect(result).toBe("logged-out") }) it("should delegate handleAuthCallback to AuthService", async () => { await cloudService.handleAuthCallback("code", "state") expect(mockAuthService.handleCallback).toHaveBeenCalledWith("code", "state") }) }) describe("organization settings methods", () => { let cloudService: CloudService beforeEach(async () => { cloudService = await CloudService.createInstance(mockContext) }) it("should delegate getAllowList to SettingsService", () => { cloudService.getAllowList() expect(mockSettingsService.getAllowList).toHaveBeenCalled() }) }) describe("error handling", () => { it("should throw error when accessing methods before initialization", () => { expect(() => CloudService.instance.login()).toThrow("CloudService not initialized") }) it("should throw error when accessing instance before creation", () => { expect(() => CloudService.instance).toThrow("CloudService not initialized") }) }) describe("hasInstance", () => { it("should return false when no instance exists", () => { expect(CloudService.hasInstance()).toBe(false) }) it("should return true when instance exists and is initialized", async () => { await CloudService.createInstance(mockContext) expect(CloudService.hasInstance()).toBe(true) }) }) describe("dispose", () => { it("should dispose of all services and clean up", async () => { const cloudService = await CloudService.createInstance(mockContext) cloudService.dispose() expect(mockSettingsService.dispose).toHaveBeenCalled() }) }) })