import '@angular/compiler'; import assert from 'node:assert/strict'; import test from 'node:test'; import { Injector } from '@angular/core'; import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; import { of, throwError } from 'rxjs'; import { AdminAuthService, AuthService, AuthMarketplaceContext, TelegramSessionApiService, HttpMarketplacesAuthGateway, MARKETPLACES_AUTH_GATEWAY, MARKETPLACES_AUTH_CONFIG, AUTH_API_URL, } from '../dist/fesm2022/marketplaces-auth.mjs'; const API_URL = 'https://api.example.com'; const CREDENTIALS_URL = `${API_URL}/auth/credentials/login`; /** Wires up the real gateway/context/service classes with a scripted HttpClient - no TestBed, no NgModule compiler needed. */ function setup(postResponse) { const calls = []; const fakeHttp = { post: (url, body, opts) => { calls.push({ url, body, opts }); return postResponse; } }; const injector = Injector.create({ providers: [ { provide: HttpClient, useValue: fakeHttp }, { provide: MARKETPLACES_AUTH_CONFIG, useValue: { apiUrl: API_URL, marketplaceDomain: 'admin.tenant-a.example.com', credentialsPath: '/auth/credentials/login', } }, { provide: AUTH_API_URL, useValue: API_URL }, AuthMarketplaceContext, TelegramSessionApiService, AuthService, AdminAuthService, HttpMarketplacesAuthGateway, { provide: MARKETPLACES_AUTH_GATEWAY, useExisting: HttpMarketplacesAuthGateway }, ], }); return { admin: injector.get(AdminAuthService), calls }; } test('loginWithCredentials: success activates the returned session and stores tokens', () => { const backendResponse = { session: { sessionId: 's1', userId: 1, username: 'root', displayName: 'Root', active: true, expires: new Date(Date.now() + 60_000).toISOString(), }, accessToken: 'jwt-access', refreshToken: 'jwt-refresh', }; const { admin, calls } = setup(of(backendResponse)); let result; admin.loginWithCredentials({ login: 'root', password: 'secret' }).subscribe(r => (result = r)); assert.equal(calls.length, 1); assert.equal(calls[0].url, CREDENTIALS_URL); assert.deepEqual(calls[0].body, { login: 'root', password: 'secret', mode: 'admin' }); assert.equal(calls[0].opts.headers.get('X-Marketplace-Domain'), 'admin.tenant-a.example.com'); assert.equal(admin.isAuthenticated(), true); assert.equal(admin.session()?.sessionId, 's1'); // getAdminToken()/setAdminTokens() are guarded no-ops without a `localStorage` // (e.g. this Node test runner), same as production SSR - token storage // itself isn't under test here, just that acceptSession() was invoked. assert.equal(result.accessToken, 'jwt-access'); assert.equal(result.mode, 'admin'); assert.equal(result.method, 'credentials'); }); test('loginWithCredentials: 401 surfaces invalid_credentials and does not mutate session state', () => { const error = new HttpErrorResponse({ status: 401, statusText: 'Unauthorized', error: { message: 'Invalid login or password' } }); const { admin } = setup(throwError(() => error)); let failure; admin.loginWithCredentials({ login: 'root', password: 'wrong' }).subscribe({ next: () => assert.fail('should not succeed'), error: err => (failure = err), }); assert.equal(failure.code, 'invalid_credentials'); assert.equal(failure.status, 401); assert.equal(admin.isAuthenticated(), false); assert.equal(admin.session(), null); assert.equal(admin.getAdminToken(), null); }); test('loginWithCredentials: 429 surfaces rate_limited with parsed Retry-After', () => { const error = new HttpErrorResponse({ status: 429, statusText: 'Too Many Requests', error: { message: 'Too many attempts' }, headers: new HttpHeaders({ 'Retry-After': '30' }), }); const { admin } = setup(throwError(() => error)); let failure; admin.loginWithCredentials({ login: 'root', password: 'secret' }).subscribe({ next: () => assert.fail('should not succeed'), error: err => (failure = err), }); assert.equal(failure.code, 'rate_limited'); assert.equal(failure.retryAfterSeconds, 30); assert.equal(admin.isAuthenticated(), false); }); test('loginWithCredentials: 403 surfaces as a generic backend failure', () => { const error = new HttpErrorResponse({ status: 403, statusText: 'Forbidden', error: { message: 'Tenant disabled' } }); const { admin } = setup(throwError(() => error)); let failure; admin.loginWithCredentials({ login: 'root', password: 'secret' }).subscribe({ next: () => assert.fail('should not succeed'), error: err => (failure = err), }); assert.equal(failure.code, 'backend'); assert.equal(failure.status, 403); assert.equal(admin.isAuthenticated(), false); });