import { Injector, runInInjectionContext } from '@angular/core'; import { environment } from '../../../../../environments/environment'; import type { ServerEndpoint } from '../../domain/models/server-directory.model'; import * as serverDirectoryStorageKeys from '../../infrastructure/constants/server-directory.infrastructure.constants'; import { ServerEndpointStorageService } from '../../infrastructure/services/server-endpoint-storage.service'; import { ServerEndpointStateService } from './server-endpoint-state.service'; function createLocalStorageMock(): Storage { const store = new Map(); return { get length(): number { return store.size; }, clear(): void { store.clear(); }, getItem(key: string): string | null { return store.get(key) ?? null; }, key(index: number): string | null { return [...store.keys()][index] ?? null; }, removeItem(key: string): void { store.delete(key); }, setItem(key: string, value: string): void { store.set(key, value); } }; } Object.defineProperty(globalThis, 'localStorage', { value: createLocalStorageMock(), configurable: true }); function getConfiguredDefaultServer(key: string): { key?: string; name?: string; url?: string } { const defaultServer = environment.defaultServers.find((server) => server.key === key); if (!defaultServer) { throw new Error(`Missing configured default server for key: ${key}`); } return defaultServer; } function seedStoredEndpoints(endpoints: ServerEndpoint[]): void { localStorage.setItem(serverDirectoryStorageKeys.SERVER_ENDPOINTS_STORAGE_KEY, JSON.stringify(endpoints)); } function createService(): ServerEndpointStateService { const injector = Injector.create({ providers: [ { provide: ServerEndpointStorageService, useClass: ServerEndpointStorageService, deps: [] } ] }); return runInInjectionContext(injector, () => new ServerEndpointStateService()); } function getRequiredDefaultEndpoint(service: ServerEndpointStateService, defaultKey: string | undefined): ServerEndpoint { const endpoint = service.servers().find((candidate) => candidate.defaultKey === defaultKey); if (!endpoint) { throw new Error(`Expected default endpoint for key: ${defaultKey ?? 'unknown'}`); } return endpoint; } describe('ServerEndpointStateService', () => { beforeEach(() => { localStorage.clear(); }); it('reactivates configured default endpoints unless the user disabled them', () => { const defaultServer = getConfiguredDefaultServer('toju-primary'); seedStoredEndpoints([ { id: 'default-server', name: 'Stored Default', url: defaultServer.url ?? '', isActive: false, isDefault: true, defaultKey: defaultServer.key, status: 'unknown' } ]); const service = createService(); const endpoint = service.servers().find((candidate) => candidate.defaultKey === defaultServer.key); expect(endpoint?.isActive).toBe(true); }); it('keeps a configured default endpoint inactive after the user turned it off', () => { const defaultServer = getConfiguredDefaultServer('toju-primary'); seedStoredEndpoints([ { id: 'default-server', name: 'Stored Default', url: defaultServer.url ?? '', isActive: true, isDefault: true, defaultKey: defaultServer.key, status: 'unknown' } ]); localStorage.setItem( serverDirectoryStorageKeys.DISABLED_DEFAULT_SERVER_KEYS_STORAGE_KEY, JSON.stringify([defaultServer.key]) ); const service = createService(); const endpoint = service.servers().find((candidate) => candidate.defaultKey === defaultServer.key); expect(endpoint?.isActive).toBe(false); }); it('keeps configured default endpoints active even when stored as incompatible unless the user disabled them', () => { const defaultServer = getConfiguredDefaultServer('toju-primary'); seedStoredEndpoints([ { id: 'default-server', name: 'Stored Default', url: defaultServer.url ?? '', isActive: false, isDefault: true, defaultKey: defaultServer.key, status: 'incompatible' } ]); const service = createService(); const endpoint = service.servers().find((candidate) => candidate.defaultKey === defaultServer.key); expect(endpoint?.isActive).toBe(true); expect(endpoint?.status).toBe('incompatible'); }); it('does not deactivate configured default endpoints when compatibility checks fail', () => { const defaultServer = getConfiguredDefaultServer('toju-primary'); const service = createService(); const endpoint = getRequiredDefaultEndpoint(service, defaultServer.key); service.updateServerStatus(endpoint.id, 'incompatible'); expect(service.servers().find((candidate) => candidate.id === endpoint.id)?.isActive).toBe(true); }); it('resolves legacy https source URLs to the local http default endpoint on the same host', () => { const defaultServer = getConfiguredDefaultServer('default'); const service = createService(); const legacyHttpsUrl = defaultServer.url?.replace(/^http:\/\//, 'https://') ?? ''; expect(service.findServerByUrl(legacyHttpsUrl)?.url).toBe(defaultServer.url); }); it('persists turning a configured default endpoint off and back on', () => { const defaultServer = getConfiguredDefaultServer('toju-primary'); const service = createService(); const endpoint = getRequiredDefaultEndpoint(service, defaultServer.key); service.deactivateServer(endpoint.id); expect(service.servers().find((candidate) => candidate.id === endpoint.id)?.isActive).toBe(false); expect(JSON.parse( localStorage.getItem(serverDirectoryStorageKeys.DISABLED_DEFAULT_SERVER_KEYS_STORAGE_KEY) ?? '[]' )).toContain(defaultServer.key); service.setActiveServer(endpoint.id); expect(service.servers().find((candidate) => candidate.id === endpoint.id)?.isActive).toBe(true); expect(localStorage.getItem(serverDirectoryStorageKeys.DISABLED_DEFAULT_SERVER_KEYS_STORAGE_KEY)).toBeNull(); }); });