filecoin-pin
Version:
Bridge IPFS content to Filecoin Onchain Cloud using familiar tools
203 lines (183 loc) • 6.37 kB
text/typescript
import { EventEmitter } from 'node:events'
import type { ProviderInfo } from '@filoz/synapse-sdk'
/**
* Test utilities for mocking Synapse SDK components
*
* This file provides realistic mock implementations for testing Synapse integrations.
* It simulates the key SDK behaviors including:
* - Provider discovery and selection
* - Data set creation and management
* - Upload lifecycle with proper callback ordering
* - Network identification
*/
// Mock provider info matching real PDP provider structure
export const mockProviderInfo: ProviderInfo = {
id: 1,
serviceProvider: '0x78bF4d833fC2ba1Abd42Bc772edbC788EC76A28F', // Provider's contract address
payee: '0x78bF4d833fC2ba1Abd42Bc772edbC788EC76A28F', // Payment recipient
name: 'Mock Provider',
description: 'Mock provider for testing',
active: true, // Provider is accepting new data
products: {
PDP: {
// Proof of Data Possession service
type: 'PDP',
isActive: true,
capabilities: {},
data: {
// PDP-specific configuration
serviceURL: 'http://localhost:8888/pdp', // Where to upload data
minPieceSizeInBytes: 127n, // Minimum piece size (127 bytes)
maxPieceSizeInBytes: 34359738368n, // Maximum piece size (32 GiB)
ipniPiece: false, // IPNI indexing capabilities
ipniHttp: false,
ipniBitswap: false,
storagePricePerTibPerMonth: 5000000000000000000n, // Price in attoFIL
location: 'Test Location',
bandwidth: 1000, // Mbps
throughput: 100, // MB/s
storageCapacity: 1000n, // TiB total capacity
storageAvailable: 800n, // TiB available
} as any,
},
},
}
/**
* Mock storage context that simulates SDK's storage operations
*
* In production, StorageContext manages:
* - Communication with PDP servers
* - On-chain data set operations
* - Upload lifecycle and retries
* - Provider health monitoring
*/
export class MockStorageContext extends EventEmitter {
public readonly dataSetId = 123 // Simulated on-chain data set ID
public readonly serviceProvider = mockProviderInfo.serviceProvider
async upload(_data: ArrayBuffer | Uint8Array, options?: any): Promise<any> {
// Extract callbacks from options (handle both old and new API)
const callbacks = options?.onUploadComplete ? options : options?.callbacks || options
// Simulate network delay for realistic testing
await new Promise((resolve) => setTimeout(resolve, 100))
// Generate mock CommP (piece commitment) with correct prefix
// Real CommP: bafkzcib... (raw multibase + CID with CommP codec)
const pieceCid = `bafkzcib${Math.random().toString(36).substring(2, 15)}`
const pieceId = Math.floor(Math.random() * 1000) // Piece index in data set
// Simulate callback sequence matching real SDK behavior
if (callbacks?.onUploadComplete != null) {
// First: data uploaded to PDP server
callbacks.onUploadComplete(pieceCid)
}
if (callbacks?.onPieceAdded != null) {
// Second: piece registered (may require transaction)
callbacks.onPieceAdded()
}
return { pieceCid, pieceId, size: 1024 }
}
}
/**
* Mock Synapse instance simulating the main SDK class
*
* Real Synapse manages:
* - Wallet and transaction signing
* - Network configuration and RPC communication
* - Contract interactions
* - Storage context factory
*/
export class MockSynapse extends EventEmitter {
private _storageContext: MockStorageContext | null = null
// Storage namespace matches SDK structure
public readonly storage = {
createContext: this.createStorageContext.bind(this),
upload: (data: any, options: any) => this._storageContext?.upload(data, options),
}
/**
* Mock network identification
* Real networks: 314 (mainnet), 314159 (calibration testnet)
*/
getNetwork(): any {
return { chainId: 314159n, name: 'calibration' }
}
/**
* Mock provider getter - returns a mock provider with destroy method
*/
getProvider(): any {
return {
destroy: async () => {
// Mock provider cleanup
},
}
}
/**
* Mock signer getter
*/
getSigner(): any {
return {
getAddress: async () => '0x1234567890123456789012345678901234567890',
}
}
/**
* Mock client getter (returns owner wallet)
*/
getClient(): any {
return {
getAddress: async () => '0x1234567890123456789012345678901234567890',
}
}
/**
* Mock session key creation
*/
createSessionKey(_sessionWallet: any): any {
const now = Math.floor(Date.now() / 1000)
const oneYear = 365 * 24 * 60 * 60 // One year from now
return {
/**
* Fetch expiries for various permission types
* Returns a map of typehash -> expiry timestamp
*
* By default, mock both CREATE_DATA_SET and ADD_PIECES with valid future expiries
* Tests can override this to simulate different scenarios:
* - CREATE_DATA_SET = 0: No permission to create datasets
* - CREATE_DATA_SET < now + 30min: Expired/expiring soon
*/
fetchExpiries: async (typehashes: string[]) => {
const expiries: Record<string, bigint> = {}
for (const typehash of typehashes) {
// By default, all permissions valid for one year
expiries[typehash] = BigInt(now + oneYear)
}
return expiries
},
}
}
/**
* Mock session key setter
*/
setSession(_sessionKey: any): void {
// No-op in mock
}
/**
* Create a storage context with lifecycle callbacks
*
* Real process:
* 1. Query on-chain registry for active providers
* 2. Select best provider based on criteria
* 3. Check for existing data set or create new one
* 4. Initialize upload session
*/
async createStorageContext(options?: any): Promise<any> {
// Simulate provider discovery and selection
if (options?.callbacks?.onProviderSelected != null) {
options.callbacks.onProviderSelected(mockProviderInfo)
}
// Simulate data set creation or reuse
if (options?.callbacks?.onDataSetResolved != null) {
options.callbacks.onDataSetResolved({
dataSetId: 123,
isExisting: false, // false = new data set created
})
}
this._storageContext = new MockStorageContext()
return this._storageContext
}
}