import { beforeEach, describe, expect, it, vi } from 'vitest' import { MeApi } from './MeApi' import type { ApiClient } from '../../core/ApiClient' import type { User } from '../users/types' import type { Account } from '../accounts/types' import type { UpdateMePayload, UpdateMeStatusPayload, UpdateMeAccountPayload, TakeoverPayload, SetOtpSecretKeyPayload, RegisterTokenPayload, } from './types' // ── Mock client factory ────────────────────────────────────────────── function createMockClient(): ApiClient { return { setAccessToken: vi.fn().mockReturnThis(), request: vi.fn(), get: vi.fn(), post: vi.fn(), put: vi.fn(), patch: vi.fn(), delete: vi.fn(), } } // ── Fixtures ───────────────────────────────────────────────────────── const user: User = { id: 'user-1', name: 'Alice', email: 'alice@example.com', phoneNumber: null, branch: null, isSuperAdmin: false, isClientUser: false, active: true, language: 'en', isFirstLogin: false, status: 'online', clientsStatus: null, offlineAt: null, isActiveInternalChat: true, passwordExpiresAt: null, otpAuthActive: false, preferences: {}, archivedAt: null, accountId: 'acc-1', timetableId: null, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z', deletedAt: null, } const account: Account = { id: 'acc-1', name: 'Acme', alias: 'acme', isActive: true, isCampaignActive: false, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z', expiresAt: null, deletedAt: null, data: { managerNumber: '123' }, plan: {} as Account['plan'], settings: {} as Account['settings'], wizardProgress: '', encryptionKey: '', branch: '', correlationId: '', promptAiFinalize: null, promptAiTransfer: null, promptAiCsat: null, creditsControl: {} as Account['creditsControl'], defaultDepartmentId: '', clusterId: null, agnusSignatureKey: null, twoFactorAuthMandatoryActive: false, } // ── Tests ──────────────────────────────────────────────────────────── describe('MeApi', () => { let client: ReturnType let api: MeApi beforeEach(() => { client = createMockClient() api = new MeApi(client) }) // ── getMe ──────────────────────────────────────────────────────── describe('getMe', () => { it('calls client.get with /me', async () => { vi.mocked(client.get).mockResolvedValue(user) const result = await api.getMe() expect(client.get).toHaveBeenCalledWith('/me', undefined) expect(result).toEqual(user) }) it('forwards custom headers', async () => { vi.mocked(client.get).mockResolvedValue(user) const headers = { 'X-Custom': 'h' } await api.getMe(headers) expect(client.get).toHaveBeenCalledWith('/me', headers) }) }) // ── getAccount ─────────────────────────────────────────────────── describe('getAccount', () => { it('calls client.get with /me/account', async () => { vi.mocked(client.get).mockResolvedValue(account) const result = await api.getAccount() expect(client.get).toHaveBeenCalledWith('/me/account', undefined) expect(result).toEqual(account) }) it('forwards custom headers', async () => { vi.mocked(client.get).mockResolvedValue(account) const headers = { 'X-Custom': 'h' } await api.getAccount(headers) expect(client.get).toHaveBeenCalledWith('/me/account', headers) }) }) // ── getAccountAmounts ──────────────────────────────────────────── describe('getAccountAmounts', () => { it('calls client.get with /me/get-account-amounts', async () => { vi.mocked(client.get).mockResolvedValue({ credits: 100 }) const result = await api.getAccountAmounts() expect(client.get).toHaveBeenCalledWith('/me/get-account-amounts', undefined) expect(result).toEqual({ credits: 100 }) }) it('forwards custom headers', async () => { vi.mocked(client.get).mockResolvedValue({}) const headers = { 'X-Custom': 'h' } await api.getAccountAmounts(headers) expect(client.get).toHaveBeenCalledWith('/me/get-account-amounts', headers) }) }) // ── getAgnusMyPlanUrl ──────────────────────────────────────────── describe('getAgnusMyPlanUrl', () => { it('calls client.get with /me/account/agnus-my-plan-url', async () => { vi.mocked(client.get).mockResolvedValue({ url: 'https://plan.example.com' }) const result = await api.getAgnusMyPlanUrl() expect(client.get).toHaveBeenCalledWith('/me/account/agnus-my-plan-url', undefined) expect(result).toEqual({ url: 'https://plan.example.com' }) }) it('forwards custom headers', async () => { vi.mocked(client.get).mockResolvedValue({ url: '' }) const headers = { 'X-Custom': 'h' } await api.getAgnusMyPlanUrl(headers) expect(client.get).toHaveBeenCalledWith('/me/account/agnus-my-plan-url', headers) }) }) // ── updateMe ───────────────────────────────────────────────────── describe('updateMe', () => { it('calls client.put with /me and body', async () => { vi.mocked(client.put).mockResolvedValue(user) const body: UpdateMePayload = { name: 'Alice Updated' } const result = await api.updateMe(body) expect(client.put).toHaveBeenCalledWith('/me', body, undefined) expect(result).toEqual(user) }) it('forwards custom headers', async () => { vi.mocked(client.put).mockResolvedValue(user) const headers = { 'X-Custom': 'h' } await api.updateMe({ name: 'x' }, headers) expect(client.put).toHaveBeenCalledWith('/me', { name: 'x' }, headers) }) }) // ── updateAccount ──────────────────────────────────────────────── describe('updateAccount', () => { it('calls client.put with /me/account and body', async () => { vi.mocked(client.put).mockResolvedValue(account) const body: UpdateMeAccountPayload = { name: 'New Name' } const result = await api.updateAccount(body) expect(client.put).toHaveBeenCalledWith('/me/account', body, undefined) expect(result).toEqual(account) }) it('forwards custom headers', async () => { vi.mocked(client.put).mockResolvedValue(account) const headers = { 'X-Custom': 'h' } await api.updateAccount({ name: 'x' }, headers) expect(client.put).toHaveBeenCalledWith('/me/account', { name: 'x' }, headers) }) }) // ── updateStatus ───────────────────────────────────────────────── describe('updateStatus', () => { it('calls client.put with /me/status and body', async () => { vi.mocked(client.put).mockResolvedValue(user) const body: UpdateMeStatusPayload = { status: 'online' } const result = await api.updateStatus(body) expect(client.put).toHaveBeenCalledWith('/me/status', body, undefined) expect(result).toEqual(user) }) it('forwards custom headers', async () => { vi.mocked(client.put).mockResolvedValue(user) const headers = { 'X-Custom': 'h' } await api.updateStatus({ status: 'offline' }, headers) expect(client.put).toHaveBeenCalledWith('/me/status', { status: 'offline' }, headers) }) }) // ── extendAccountGracePeriod ───────────────────────────────────── describe('extendAccountGracePeriod', () => { it('calls client.post with /me/account/extend-grace-period', async () => { vi.mocked(client.post).mockResolvedValue(account) const result = await api.extendAccountGracePeriod() expect(client.post).toHaveBeenCalledWith( '/me/account/extend-grace-period', undefined, undefined, ) expect(result).toEqual(account) }) it('forwards custom headers', async () => { vi.mocked(client.post).mockResolvedValue(account) const headers = { 'X-Custom': 'h' } await api.extendAccountGracePeriod(headers) expect(client.post).toHaveBeenCalledWith( '/me/account/extend-grace-period', undefined, headers, ) }) }) // ── registerToken ──────────────────────────────────────────────── describe('registerToken', () => { it('calls client.post with /me/one-signal/register-token and body', async () => { vi.mocked(client.post).mockResolvedValue(undefined) const body: RegisterTokenPayload = { token: 'abc123', platform: 'web' } await api.registerToken(body) expect(client.post).toHaveBeenCalledWith('/me/one-signal/register-token', body, undefined) }) it('forwards custom headers', async () => { vi.mocked(client.post).mockResolvedValue(undefined) const headers = { 'X-Custom': 'h' } await api.registerToken({ token: 'x' }, headers) expect(client.post).toHaveBeenCalledWith( '/me/one-signal/register-token', { token: 'x' }, headers, ) }) }) // ── reportFirstLogin ───────────────────────────────────────────── describe('reportFirstLogin', () => { it('calls client.post with /me/report-login', async () => { vi.mocked(client.post).mockResolvedValue(user) const result = await api.reportFirstLogin() expect(client.post).toHaveBeenCalledWith('/me/report-login', undefined, undefined) expect(result).toEqual(user) }) it('forwards custom headers', async () => { vi.mocked(client.post).mockResolvedValue(user) const headers = { 'X-Custom': 'h' } await api.reportFirstLogin(headers) expect(client.post).toHaveBeenCalledWith('/me/report-login', undefined, headers) }) }) // ── takeover ───────────────────────────────────────────────────── describe('takeover', () => { it('calls client.post with /me/takeover without body', async () => { vi.mocked(client.post).mockResolvedValue(undefined) await api.takeover() expect(client.post).toHaveBeenCalledWith('/me/takeover', undefined, undefined) }) it('calls client.post with /me/takeover with body', async () => { vi.mocked(client.post).mockResolvedValue(undefined) const body: TakeoverPayload = { client: 'web' } await api.takeover(body) expect(client.post).toHaveBeenCalledWith('/me/takeover', body, undefined) }) it('forwards custom headers', async () => { vi.mocked(client.post).mockResolvedValue(undefined) const headers = { 'X-Custom': 'h' } await api.takeover(undefined, headers) expect(client.post).toHaveBeenCalledWith('/me/takeover', undefined, headers) }) }) // ── setOtpSecretKey ────────────────────────────────────────────── describe('setOtpSecretKey', () => { it('calls client.patch with /me/set-otp-secret-key and body', async () => { vi.mocked(client.patch).mockResolvedValue(undefined) const body: SetOtpSecretKeyPayload = { secretKey: 'ABCDEFGH12345678' } await api.setOtpSecretKey(body) expect(client.patch).toHaveBeenCalledWith('/me/set-otp-secret-key', body, undefined) }) it('forwards custom headers', async () => { vi.mocked(client.patch).mockResolvedValue(undefined) const headers = { 'X-OTP': '123456' } await api.setOtpSecretKey({ secretKey: 'KEY' }, headers) expect(client.patch).toHaveBeenCalledWith( '/me/set-otp-secret-key', { secretKey: 'KEY' }, headers, ) }) }) // ── activateOtpAuth ────────────────────────────────────────────── describe('activateOtpAuth', () => { it('calls client.post with /me/activate-otp-auth', async () => { vi.mocked(client.post).mockResolvedValue(true) const result = await api.activateOtpAuth() expect(client.post).toHaveBeenCalledWith('/me/activate-otp-auth', undefined, undefined) expect(result).toBe(true) }) it('forwards custom headers', async () => { vi.mocked(client.post).mockResolvedValue(true) const headers = { 'x-digisac-otp': '123456' } await api.activateOtpAuth(headers) expect(client.post).toHaveBeenCalledWith('/me/activate-otp-auth', undefined, headers) }) }) // ── Error propagation ──────────────────────────────────────────── describe('error propagation', () => { it('propagates errors from getMe', async () => { vi.mocked(client.get).mockRejectedValue(new Error('Unauthorized')) await expect(api.getMe()).rejects.toThrow('Unauthorized') }) it('propagates errors from updateMe', async () => { vi.mocked(client.put).mockRejectedValue(new Error('Validation failed')) await expect(api.updateMe({ name: 'x' })).rejects.toThrow('Validation failed') }) it('propagates errors from activateOtpAuth', async () => { vi.mocked(client.post).mockRejectedValue(new Error('Invalid OTP')) await expect(api.activateOtpAuth()).rejects.toThrow('Invalid OTP') }) }) })