import { mock } from "jest-mock-extended"; import { ReplaySubject, combineLatest, map } from "rxjs"; import { AccountInfo, AccountService } from "../src/auth/abstractions/account.service"; import { UserId } from "../src/types/guid"; export function mockAccountServiceWith( userId: UserId, info: Partial = {}, activity: Record = {}, ): FakeAccountService { const fullInfo: AccountInfo = { ...info, ...{ name: "name", email: "email", emailVerified: true, }, }; const fullActivity = { [userId]: new Date(), ...activity }; const service = new FakeAccountService({ [userId]: fullInfo }, fullActivity); service.activeAccountSubject.next({ id: userId, ...fullInfo }); return service; } export class FakeAccountService implements AccountService { mock = mock(); // eslint-disable-next-line rxjs/no-exposed-subjects -- test class accountsSubject = new ReplaySubject>(1); // eslint-disable-next-line rxjs/no-exposed-subjects -- test class activeAccountSubject = new ReplaySubject<{ id: UserId } & AccountInfo>(1); // eslint-disable-next-line rxjs/no-exposed-subjects -- test class accountActivitySubject = new ReplaySubject>(1); private _activeUserId: UserId; get activeUserId() { return this._activeUserId; } accounts$ = this.accountsSubject.asObservable(); activeAccount$ = this.activeAccountSubject.asObservable(); accountActivity$ = this.accountActivitySubject.asObservable(); get sortedUserIds$() { return this.accountActivity$.pipe( map((activity) => { return Object.entries(activity) .map(([userId, lastActive]: [UserId, Date]) => ({ userId, lastActive })) .sort((a, b) => a.lastActive.getTime() - b.lastActive.getTime()) .map((a) => a.userId); }), ); } get nextUpAccount$() { return combineLatest([this.accounts$, this.activeAccount$, this.sortedUserIds$]).pipe( map(([accounts, activeAccount, sortedUserIds]) => { const nextId = sortedUserIds.find((id) => id !== activeAccount?.id && accounts[id] != null); return nextId ? { id: nextId, ...accounts[nextId] } : null; }), ); } constructor(initialData: Record, accountActivity?: Record) { this.accountsSubject.next(initialData); this.activeAccountSubject.subscribe((data) => (this._activeUserId = data?.id)); this.activeAccountSubject.next(null); this.accountActivitySubject.next(accountActivity); } setAccountActivity(userId: UserId, lastActivity: Date): Promise { this.accountActivitySubject.next({ ...this.accountActivitySubject["_buffer"][0], [userId]: lastActivity, }); return this.mock.setAccountActivity(userId, lastActivity); } async addAccount(userId: UserId, accountData: AccountInfo): Promise { const current = this.accountsSubject["_buffer"][0] ?? {}; this.accountsSubject.next({ ...current, [userId]: accountData }); await this.mock.addAccount(userId, accountData); } async setAccountName(userId: UserId, name: string): Promise { await this.mock.setAccountName(userId, name); } async setAccountEmail(userId: UserId, email: string): Promise { await this.mock.setAccountEmail(userId, email); } async setAccountEmailVerified(userId: UserId, emailVerified: boolean): Promise { await this.mock.setAccountEmailVerified(userId, emailVerified); } async switchAccount(userId: UserId): Promise { const next = userId == null ? null : { id: userId, ...this.accountsSubject["_buffer"]?.[0]?.[userId] }; this.activeAccountSubject.next(next); await this.mock.switchAccount(userId); } async clean(userId: UserId): Promise { const current = this.accountsSubject["_buffer"][0] ?? {}; const updated = { ...current, [userId]: loggedOutInfo }; this.accountsSubject.next(updated); await this.mock.clean(userId); } } const loggedOutInfo: AccountInfo = { name: undefined, email: "", emailVerified: false, };