diff --git a/src/app/drive/services/limit.service.test.ts b/src/app/drive/services/limit.service.test.ts new file mode 100644 index 0000000000..4e5b4a17c7 --- /dev/null +++ b/src/app/drive/services/limit.service.test.ts @@ -0,0 +1,97 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import limitService from './limit.service'; +import { SdkFactory } from '../../core/factory/sdk'; +import { HUNDRED_TB } from 'app/core/components/Sidenav/Sidenav'; + +vi.mock('@internxt/sdk', () => ({ + Network: {}, +})); + +vi.mock('app/store', () => ({ + default: { + dispatch: vi.fn(), + getState: vi.fn(), + }, +})); + +vi.mock('app/store/slices/session', () => ({ + sessionActions: { + resetState: vi.fn(), + }, +})); + +vi.mock('../../core/factory/sdk', () => ({ + SdkFactory: { + getNewApiInstance: vi.fn(), + }, +})); + +vi.mock('./size.service', () => { + const bytesToStringMock = (size) => { + return size > 0 ? 'formatted-size' : ''; + }; + + return { + bytesToString: bytesToStringMock, + default: { + bytesToString: bytesToStringMock, + }, + }; +}); + +describe('limitService', () => { + const mockStorageClient = { + spaceLimitV2: vi.fn(), + }; + + beforeEach(() => { + vi.clearAllMocks(); + + vi.spyOn(SdkFactory, 'getNewApiInstance').mockReturnValue({ + createNewStorageClient: vi.fn().mockReturnValue(mockStorageClient), + } as any); + }); + + describe('fetchLimit', () => { + it('should fetch space limit successfully', async () => { + const maxSpaceBytes = 1024 * 1024 * 1024 * 10; // 10GB + mockStorageClient.spaceLimitV2.mockResolvedValue({ maxSpaceBytes }); + + const result = await limitService.fetchLimit(); + + expect(SdkFactory.getNewApiInstance).toHaveBeenCalled(); + expect(mockStorageClient.spaceLimitV2).toHaveBeenCalled(); + expect(result).toEqual(maxSpaceBytes); + }); + + it('should handle errors when fetching space limit', async () => { + const error = new Error('Failed to fetch space limit'); + mockStorageClient.spaceLimitV2.mockRejectedValue(error); + + await expect(limitService.fetchLimit()).rejects.toThrow(error); + }); + }); + + describe('formatLimit', () => { + it('should return ellipsis for zero or negative limit values', () => { + expect(limitService.formatLimit(0)).toBe('...'); + expect(limitService.formatLimit(-100)).toBe('...'); + }); + + it('should return infinity symbol for limits greater than 100TB', () => { + const largeLimit = HUNDRED_TB + 1024; + const result = limitService.formatLimit(largeLimit); + expect(result).toBe('\u221E'); + }); + + it('should format positive values less than 100TB', () => { + const smallLimit = 1024 * 1024 * 50; // 50MB + const result = limitService.formatLimit(smallLimit); + + expect(result).not.toBe('...'); + expect(result).not.toBe('\u221E'); + expect(typeof result).toBe('string'); + expect(result.length).toBeGreaterThan(0); + }); + }); +}); diff --git a/src/app/drive/services/limit.service.ts b/src/app/drive/services/limit.service.ts index ebe3800c99..b3337c6605 100644 --- a/src/app/drive/services/limit.service.ts +++ b/src/app/drive/services/limit.service.ts @@ -3,8 +3,8 @@ import { SdkFactory } from '../../core/factory/sdk'; import { HUNDRED_TB } from 'app/core/components/Sidenav/Sidenav'; async function fetchLimit(): Promise { - const storageClient = SdkFactory.getInstance().createStorageClient(); - return storageClient.spaceLimit().then((response) => { + const storageClient = SdkFactory.getNewApiInstance().createNewStorageClient(); + return storageClient.spaceLimitV2().then((response) => { return response.maxSpaceBytes; }); } diff --git a/src/app/drive/services/usage.service.test.ts b/src/app/drive/services/usage.service.test.ts new file mode 100644 index 0000000000..4ec8c5cfd7 --- /dev/null +++ b/src/app/drive/services/usage.service.test.ts @@ -0,0 +1,116 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import usageService from './usage.service'; +import { SdkFactory } from '../../core/factory/sdk'; +import errorService from '../../core/services/error.service'; +import { UsageResponseV2 } from '@internxt/sdk/dist/drive/storage/types'; +vi.mock('../../core/factory/sdk', () => ({ + SdkFactory: { + getNewApiInstance: vi.fn(), + }, +})); + +vi.mock('../../core/services/error.service', () => ({ + default: { + reportError: vi.fn(), + }, +})); + +describe('usageService', () => { + const mockStorageClient = { + spaceUsageV2: vi.fn(), + }; + + beforeEach(() => { + vi.clearAllMocks(); + + vi.spyOn(SdkFactory, 'getNewApiInstance').mockReturnValue({ + createNewStorageClient: vi.fn().mockReturnValue(mockStorageClient), + } as any); + }); + + describe('fetchUsage', () => { + it('should fetch usage successfully', async () => { + const usageResponse: UsageResponseV2 = { + drive: 1024 * 1024 * 100, // 100MB + backups: 1024 * 1024 * 50, // 50MB + total: 1024 * 1024 * 150, // 150MB + }; + mockStorageClient.spaceUsageV2.mockResolvedValue(usageResponse); + + const result = await usageService.fetchUsage(); + + expect(SdkFactory.getNewApiInstance).toHaveBeenCalled(); + expect(mockStorageClient.spaceUsageV2).toHaveBeenCalled(); + expect(result).toEqual(usageResponse); + }); + + it('should handle errors when fetching usage', async () => { + const error = new Error('Failed to fetch usage'); + mockStorageClient.spaceUsageV2.mockRejectedValue(error); + + await expect(usageService.fetchUsage()).rejects.toThrow(error); + }); + }); + + describe('getUsageDetails', () => { + it('should get usage details successfully', async () => { + const usageResponse = { + drive: 1024 * 1024 * 100, // 100MB + backups: 1024 * 1024 * 50, // 50MB + }; + mockStorageClient.spaceUsageV2.mockResolvedValue(usageResponse); + + const result = await usageService.getUsageDetails(); + + expect(SdkFactory.getNewApiInstance).toHaveBeenCalled(); + expect(mockStorageClient.spaceUsageV2).toHaveBeenCalled(); + expect(result).toEqual({ + drive: usageResponse.drive, + photos: 0, + backups: usageResponse.backups, + }); + }); + + it('should handle errors and report them', async () => { + const error = new Error('Failed to fetch usage details'); + mockStorageClient.spaceUsageV2.mockRejectedValue(error); + + const result = await usageService.getUsageDetails(); + + expect(errorService.reportError).toHaveBeenCalledWith(error); + expect(result).toEqual({ drive: 0, photos: 0, backups: 0 }); + }); + }); + + describe('getUsagePercent', () => { + it('should calculate usage percentage correctly', () => { + const usage = 250; + const limit = 1000; + const expectedPercent = 25; + + const result = usageService.getUsagePercent(usage, limit); + + expect(result).toBe(expectedPercent); + }); + + it('should round up percentage to nearest integer', () => { + const usage = 101; + const limit = 1000; + const expectedPercent = 11; + + const result = usageService.getUsagePercent(usage, limit); + + expect(result).toBe(expectedPercent); + }); + + it('should handle 100% usage correctly', () => { + const usage = 1000; + const limit = 1000; + const expectedPercent = 100; + + const result = usageService.getUsagePercent(usage, limit); + + expect(result).toBe(expectedPercent); + }); + }); +}); diff --git a/src/app/drive/services/usage.service.ts b/src/app/drive/services/usage.service.ts index e4fd5b5983..27a5efd80a 100644 --- a/src/app/drive/services/usage.service.ts +++ b/src/app/drive/services/usage.service.ts @@ -1,4 +1,4 @@ -import { UsageResponse } from '@internxt/sdk/dist/drive/storage/types'; +import { UsageResponseV2 } from '@internxt/sdk/dist/drive/storage/types'; import { SdkFactory } from '../../core/factory/sdk'; import errorService from '../../core/services/error.service'; @@ -8,21 +8,21 @@ export interface UsageDetailsProps { backups: number; } -export async function fetchUsage(): Promise { - const storageClient = SdkFactory.getInstance().createStorageClient(); - const driveUsage = await storageClient.spaceUsage(); +export async function fetchUsage(): Promise { + const storageClient = SdkFactory.getNewApiInstance().createNewStorageClient(); + const driveUsage = await storageClient.spaceUsageV2(); return driveUsage; } async function getUsageDetails(): Promise { - const storageClient = SdkFactory.getInstance().createStorageClient(); + const storageClient = SdkFactory.getNewApiInstance().createNewStorageClient(); let drive = 0; let backups = 0; try { - const { drive: storageDrive, backups: storageBackups } = await storageClient.spaceUsage(); + const { drive: storageDrive, backups: storageBackups } = await storageClient.spaceUsageV2(); drive = storageDrive; backups = storageBackups; } catch (error) { diff --git a/src/app/newSettings/Sections/Account/Plans/utils/planUtils.ts b/src/app/newSettings/Sections/Account/Plans/utils/planUtils.ts index e7e3e72e09..0efdc3ce0d 100644 --- a/src/app/newSettings/Sections/Account/Plans/utils/planUtils.ts +++ b/src/app/newSettings/Sections/Account/Plans/utils/planUtils.ts @@ -4,7 +4,7 @@ import { StoragePlan, UserSubscription, } from '@internxt/sdk/dist/drive/payments/types/types'; -import { UsageResponse } from '@internxt/sdk/dist/drive/storage/types'; +import { UsageResponseV2 } from '@internxt/sdk/dist/drive/storage/types'; import { bytesToString } from 'app/drive/services/size.service'; import { t } from 'i18next'; import { FreeStoragePlan } from '../../../../../drive/types'; @@ -86,7 +86,7 @@ const getPlanName = (storagePlan: StoragePlan | null, limit?: number) => { if (limit) return bytesToString(limit, false); return FreeStoragePlan.simpleName; }; -const getCurrentUsage = (usage: UsageResponse | null) => { +const getCurrentUsage = (usage: UsageResponseV2 | null) => { return usage?.total ?? -1; }; diff --git a/src/app/store/slices/plan/index.ts b/src/app/store/slices/plan/index.ts index f5ca40067a..7fec770bcd 100644 --- a/src/app/store/slices/plan/index.ts +++ b/src/app/store/slices/plan/index.ts @@ -1,7 +1,7 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import { StoragePlan, UserSubscription, UserType } from '@internxt/sdk/dist/drive/payments/types/types'; -import { UsageResponse } from '@internxt/sdk/dist/drive/storage/types'; +import { UsageResponse, UsageResponseV2 } from '@internxt/sdk/dist/drive/storage/types'; import { GetMemberUsageResponse } from '@internxt/sdk/dist/workspaces'; import workspacesService from 'app/core/services/workspace.service'; import limitService from 'app/drive/services/limit.service'; @@ -20,7 +20,7 @@ export interface PlanState { teamPlan: StoragePlan | null; planLimit: number; planUsage: number; - usageDetails: UsageResponse | null; + usageDetails: UsageResponseV2 | null; individualSubscription: UserSubscription | null; businessSubscription: UserSubscription | null; businessPlanLimit: number; @@ -77,7 +77,7 @@ export const fetchLimitThunk = createAsyncThunk( +export const fetchUsageThunk = createAsyncThunk( 'plan/fetchUsage', async (payload: void, { getState }) => { const isAuthenticated = getState().user.isAuthenticated;