From 34eca1d495e44643397939956e930a046c644aa8 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 28 Mar 2026 15:42:27 +0100 Subject: [PATCH] feat(portal-status): add unit tests for PortalStatusService and enhance error handling --- .../src/lib/portal-status.service.spec.ts | 78 +++++++++++++++++++ .../services/src/lib/portal-status.service.ts | 8 +- 2 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 libs/services/src/lib/portal-status.service.spec.ts diff --git a/libs/services/src/lib/portal-status.service.spec.ts b/libs/services/src/lib/portal-status.service.spec.ts new file mode 100644 index 000000000..378774a18 --- /dev/null +++ b/libs/services/src/lib/portal-status.service.spec.ts @@ -0,0 +1,78 @@ +import { + EnvironmentInjector, + Injector, + createEnvironmentInjector, + runInInjectionContext, +} from '@angular/core'; +import { DataService } from './data.service'; +import { PortalStatusService } from './portal-status.service'; + +describe('PortalStatusService', () => { + let service: PortalStatusService; + let dataService: { sendIpcEvent: jest.Mock }; + + beforeEach(() => { + dataService = { + sendIpcEvent: jest.fn(), + }; + const injector = createEnvironmentInjector([ + { + provide: DataService, + useValue: dataService, + }, + ], Injector.NULL as unknown as EnvironmentInjector); + + service = runInInjectionContext( + injector, + () => new PortalStatusService() + ); + }); + + it('marks portal status checks as silent background probes', async () => { + const futureExpDate = String( + Math.floor(Date.now() / 1000) + 60 * 60 * 24 + ); + dataService.sendIpcEvent.mockResolvedValue({ + payload: { + user_info: { + status: 'Active', + exp_date: futureExpDate, + }, + }, + }); + + const status = await service.checkPortalStatus( + 'http://example.com', + 'user', + 'pass' + ); + + expect(status).toBe('active'); + expect(dataService.sendIpcEvent).toHaveBeenCalledWith( + 'XTREAM_REQUEST', + expect.objectContaining({ + suppressErrorLog: true, + url: 'http://example.com', + params: { + action: 'get_account_info', + password: 'pass', + username: 'user', + }, + }) + ); + }); + + it('returns unavailable without console noise when the portal is offline', async () => { + const consoleErrorSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined); + dataService.sendIpcEvent.mockRejectedValue(new Error('ECONNREFUSED')); + + await expect( + service.checkPortalStatus('http://example.com', 'user', 'pass') + ).resolves.toBe('unavailable'); + expect(consoleErrorSpy).not.toHaveBeenCalled(); + + consoleErrorSpy.mockRestore(); + }); +}); diff --git a/libs/services/src/lib/portal-status.service.ts b/libs/services/src/lib/portal-status.service.ts index e7a9849de..9dcfb14c3 100644 --- a/libs/services/src/lib/portal-status.service.ts +++ b/libs/services/src/lib/portal-status.service.ts @@ -47,6 +47,7 @@ export class PortalStatusService { username, action: 'get_account_info', }, + suppressErrorLog: true, } ); const payload = response?.payload; @@ -56,15 +57,18 @@ export class PortalStatusService { } if (payload.user_info.status === 'Active') { + if (!payload.user_info.exp_date) { + return 'active'; + } + const expDate = new Date( - parseInt(payload.user_info.exp_date) * 1000 + parseInt(payload.user_info.exp_date, 10) * 1000 ); return expDate < new Date() ? 'expired' : 'active'; } else { return 'inactive'; } } catch (error) { - console.error('Error checking portal status:', error); return 'unavailable'; } }