Metaplex MCP Server
by aldrin-labs
import { describe, expect, it, jest, beforeEach } from '@jest/globals';
import { Connection as SolanaConnection } from '@solana/web3.js';
import { Program as AnchorProgram, Idl } from '@project-serum/anchor';
import { PublicKey, Program, Connection } from '../../types/mocks';
import { FeeService } from '../fees';
import type { MockedFunction } from '../../types/mocks';
describe('FeeService', () => {
let feeService: FeeService;
let mockConnection: Connection;
let mockProgram: Program;
let mockRecipeAll: MockedFunction<(filters?: any[]) => Promise<any[]>>;
let mockEscrowAll: MockedFunction<(filters?: any[]) => Promise<any[]>>;
beforeEach(() => {
mockConnection = new Connection('mock-endpoint');
mockProgram = new Program('MPL4o4wMzndgh8T1NVDxELQCj5UQfYTYEkabX3wNKtb');
mockRecipeAll = mockProgram.account.recipeV1.all as MockedFunction<(filters?: any[]) => Promise<any[]>>;
mockEscrowAll = mockProgram.account.escrowV1.all as MockedFunction<(filters?: any[]) => Promise<any[]>>;
feeService = new FeeService(
mockConnection as unknown as SolanaConnection,
mockProgram as unknown as AnchorProgram<Idl>
);
});
describe('calculateFees', () => {
const mockRecipeAccount = {
account: {
feeAmountCapture: { toNumber: () => 0.01 }, // 1%
feeAmountRelease: { toNumber: () => 0.02 }, // 2%
solFeeAmountCapture: { toNumber: () => 0.001 },
solFeeAmountRelease: { toNumber: () => 0.002 },
},
publicKey: new PublicKey('11111111111111111111111111111111'),
};
const mockEscrowAccount = {
account: {
feeAmount: { toNumber: () => 0.015 }, // 1.5%
solFeeAmount: { toNumber: () => 0.0015 },
},
publicKey: new PublicKey('22222222222222222222222222222222'),
};
it('should calculate capture fees correctly', async () => {
mockRecipeAll.mockResolvedValue([mockRecipeAccount]);
const result = await feeService.calculateFees('capture', 1000);
expect(result.isValid).toBe(true);
expect(result.operation).toBe('capture');
expect(result.amount).toBe(1000);
expect(result.tokenFees.protocol).toBe(1); // 0.1% of 1000
expect(result.tokenFees.project).toBe(10); // 1% of 1000
expect(result.solFees.protocol).toBe(0.00001);
expect(result.solFees.project).toBe(0.001);
expect(result.tokenFees.total).toBe(11); // protocol + project
expect(result.solFees.total).toBe(0.00101); // protocol + project
});
it('should calculate release fees correctly', async () => {
mockEscrowAll.mockResolvedValue([mockEscrowAccount]);
const result = await feeService.calculateFees('release', 1000);
expect(result.isValid).toBe(true);
expect(result.operation).toBe('release');
expect(result.amount).toBe(1000);
expect(result.tokenFees.protocol).toBe(1); // 0.1% of 1000
expect(result.tokenFees.project).toBe(15); // 1.5% of 1000
expect(result.solFees.protocol).toBe(0.00001);
expect(result.solFees.project).toBe(0.0015);
expect(result.tokenFees.total).toBe(16); // protocol + project
expect(result.solFees.total).toBe(0.00151); // protocol + project
});
it('should handle invalid amount', async () => {
const result = await feeService.calculateFees('capture', 0);
expect(result.isValid).toBe(false);
expect(result.issues).toContain('Amount must be greater than 0');
expect(result.tokenFees.total).toBe(0);
expect(result.solFees.total).toBe(0);
});
it('should handle fee configuration fetch errors', async () => {
mockRecipeAll.mockRejectedValue(new Error('Failed to fetch fee configuration'));
const result = await feeService.calculateFees('capture', 1000);
expect(result.isValid).toBe(false);
expect(result.issues).toContain(
'Failed to fetch project fee configuration: Failed to fetch fee configuration'
);
expect(result.tokenFees.total).toBe(0);
expect(result.solFees.total).toBe(0);
});
it('should handle missing fee configuration', async () => {
mockRecipeAll.mockResolvedValue([]);
const result = await feeService.calculateFees('capture', 1000);
expect(result.isValid).toBe(false);
expect(result.issues).toContain('No fee configuration found');
expect(result.tokenFees.total).toBe(0);
expect(result.solFees.total).toBe(0);
});
});
});