filecoin-pin
Version:
Bridge IPFS content to Filecoin Onchain Cloud using familiar tools
220 lines • 8.76 kB
JavaScript
/**
* Unit tests for calculateActualStorage
*
* Tests abort handling, timeout behavior, and basic calculation correctness.
*/
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { calculateActualStorage } from '../../core/data-set/calculate-actual-storage.js';
// Mock the dependencies
const { mockSynapse, mockCreateStorageContext, mockGetDataSetPieces, defaultCreateStorageContext, defaultGetDataSetPieces, state, } = vi.hoisted(() => {
const state = {
pieces: [],
};
const mockGetAddress = vi.fn(async () => '0xtest-address');
const defaultGetDataSetPieces = async (_synapse, _context, _options) => {
if (_options?.signal?.aborted) {
const error = new Error('This operation was aborted');
error.name = 'AbortError';
throw error;
}
const pieces = state.pieces.map((p) => ({
pieceId: p.pieceId,
pieceCid: p.pieceCid,
size: p.size ?? undefined,
}));
const totalSizeBytes = pieces.reduce((sum, p) => sum + BigInt(p.size ?? 0), 0n);
return {
pieces,
dataSetId: _context?.dataSetId ?? 1,
totalSizeBytes,
warnings: [],
};
};
const mockGetDataSetPieces = vi.fn(defaultGetDataSetPieces);
const defaultCreateStorageContext = async (_synapse, dataSetId) => ({
storage: { dataSetId },
providerInfo: { id: 1 },
});
const mockCreateStorageContext = vi.fn(defaultCreateStorageContext);
const mockSynapse = {
getClient: () => ({ getAddress: mockGetAddress }),
};
return {
mockSynapse,
mockCreateStorageContext,
mockGetDataSetPieces,
defaultCreateStorageContext,
defaultGetDataSetPieces,
state,
};
});
// Mock the imports
vi.mock('../../core/synapse/storage-context-helper.js', () => ({
createStorageContextFromDataSetId: mockCreateStorageContext,
}));
vi.mock('../../core/data-set/get-data-set-pieces.js', () => ({
getDataSetPieces: mockGetDataSetPieces,
}));
describe('calculateActualStorage', () => {
beforeEach(() => {
vi.resetAllMocks();
state.pieces = [];
mockCreateStorageContext.mockImplementation(defaultCreateStorageContext);
mockGetDataSetPieces.mockImplementation(defaultGetDataSetPieces);
});
describe('basic calculation', () => {
it('should calculate total storage from multiple data sets', async () => {
// Setup: 2 data sets with different piece sizes
const dataSets = [
{
dataSetId: 1,
providerId: 1,
serviceProvider: '0xprovider1',
isLive: true,
},
{
dataSetId: 2,
providerId: 1,
serviceProvider: '0xprovider1',
isLive: true,
},
];
const oneGiB = 1024n * 1024n * 1024n;
// pieces apply to both data sets
state.pieces = [
{ pieceId: 1, pieceCid: 'bafy1', size: Number(oneGiB) },
{ pieceId: 2, pieceCid: 'bafy2', size: Number(oneGiB) },
];
const result = await calculateActualStorage(mockSynapse, dataSets);
expect(result.dataSetCount).toBe(2);
expect(result.dataSetsProcessed).toBe(2);
expect(result.totalBytes).toBe(oneGiB * 2n * 2n); // 2 pieces × 2 datasets
expect(result.pieceCount).toBe(4);
expect(result.timedOut).toBeFalsy();
expect(result.warnings).toHaveLength(0);
});
it('should handle empty data sets', async () => {
const result = await calculateActualStorage(mockSynapse, []);
expect(result.dataSetCount).toBe(0);
expect(result.dataSetsProcessed).toBe(0);
expect(result.totalBytes).toBe(0n);
expect(result.pieceCount).toBe(0);
expect(result.timedOut).toBeFalsy();
});
it('should handle data sets with no pieces', async () => {
const dataSets = [
{
dataSetId: 1,
providerId: 1,
serviceProvider: '0xprovider1',
isLive: true,
},
];
state.pieces = [];
const result = await calculateActualStorage(mockSynapse, dataSets);
expect(result.dataSetCount).toBe(1);
expect(result.dataSetsProcessed).toBe(1);
expect(result.totalBytes).toBe(0n);
expect(result.pieceCount).toBe(0);
});
});
describe('abort handling', () => {
it('should handle immediate abort', async () => {
const dataSets = [
{
dataSetId: 1,
providerId: 1,
serviceProvider: '0xprovider1',
isLive: true,
},
];
// Create already-aborted signal
const controller = new AbortController();
controller.abort();
const result = await calculateActualStorage(mockSynapse, dataSets, {
signal: controller.signal,
});
expect(result.timedOut).toBe(true);
expect(result.dataSetsProcessed).toBe(0);
expect(result.warnings.some((w) => w.code === 'CALCULATION_ABORTED')).toBe(true);
});
it('should return partial results on abort', async () => {
const dataSets = [
{
dataSetId: 1,
providerId: 1,
serviceProvider: '0xprovider1',
isLive: true,
},
{
dataSetId: 2,
providerId: 2,
serviceProvider: '0xprovider2',
isLive: true,
},
];
const controller = new AbortController();
// Allow first dataset to complete
let callCount = 0;
mockGetDataSetPieces.mockImplementation(async (_synapse, _context, _options) => {
callCount++;
if (callCount === 1) {
return {
pieces: [{ pieceId: 1, pieceCid: 'bafy1', size: 1024 }],
dataSetId: 1,
totalSizeBytes: 1024n,
warnings: [],
};
}
controller.abort();
const error = new Error('This operation was aborted');
error.name = 'AbortError';
throw error;
});
const result = await calculateActualStorage(mockSynapse, dataSets, {
signal: controller.signal,
});
expect(result.timedOut).toBe(true);
expect(result.totalBytes).toBe(1024n); // Partial result from first dataset
expect(result.dataSetsProcessed).toBe(1);
expect(result.pieceCount).toBe(1);
expect(result.warnings.some((w) => w.code === 'CALCULATION_ABORTED')).toBe(true);
});
});
describe('error handling', () => {
it('should continue processing other datasets when one fails', async () => {
const dataSets = [
{
dataSetId: 1,
providerId: 1,
serviceProvider: '0xprovider1',
isLive: true,
},
{
dataSetId: 2,
providerId: 2,
serviceProvider: '0xprovider2',
isLive: true,
},
];
let callCount = 0;
mockGetDataSetPieces.mockImplementation(async (_synapse, _context, _options) => {
callCount++;
if (callCount === 1) {
throw new Error('Dataset query failed');
}
return {
pieces: [{ pieceId: 1, pieceCid: 'bafy1', size: 1024 }],
dataSetId: 2,
totalSizeBytes: 1024n,
warnings: [],
};
});
const result = await calculateActualStorage(mockSynapse, dataSets);
expect(result.dataSetsProcessed).toBe(1); // Only second dataset succeeded
expect(result.totalBytes).toBe(1024n);
expect(result.warnings.some((w) => w.code === 'DATA_SET_QUERY_FAILED')).toBe(true);
});
});
});
//# sourceMappingURL=calculate-actual-storage.test.js.map