feat: SIWE auth, auto-pin, pin search/share, explorer improvements

- SIWE server-side auth (siwe + jose JWT)
- Wallet provider fallback (MAOS -> MetaMask)
- Auto-pin after upload (quick + crypto)
- Pin search/filter in explorer
- Share button (gateway link) per pin
- Copy CID button per pin
- Breadcrumb root navigation fix
- Hydration mismatch fix (mounted state)
- Defensive truncateCid for empty values
- CSP fix (template literal)
- Error boundaries (upload, profile, usage)
- 191 tests passing
This commit is contained in:
maikrolf
2026-09-19 12:25:36 +02:00
parent f72b775379
commit 7c7854e08f
24 changed files with 1349 additions and 125 deletions
@@ -0,0 +1,113 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { PaymentService } from '../write-service';
// Mock viem
vi.mock('viem', () => ({
createWalletClient: vi.fn(() => ({
getAddresses: vi.fn().mockResolvedValue(['0x1234567890123456789012345678901234567890']),
sendTransaction: vi.fn().mockResolvedValue('0xTxHash'),
writeContract: vi.fn().mockResolvedValue('0xContractHash'),
})),
createPublicClient: vi.fn(() => ({
readContract: vi.fn().mockResolvedValue(1000000n),
getBytecode: vi.fn().mockResolvedValue('0x123'),
})),
encodeFunctionData: vi.fn().mockReturnValue('0xEncodedData'),
custom: vi.fn(),
http: vi.fn(),
}));
// Mock abi
vi.mock('../abi', () => ({
zkSyncLocal: {},
IPFS_PORTAL_PAYMENT_ABI: [],
ERC20_ABI: [],
DEFAULT_CONTRACT_ADDRESS: '0x0000000000000000000000000000000000000000',
}));
describe('PaymentService', () => {
let service: PaymentService;
let mockProvider: { request: (args: { method: string; params?: unknown[] }) => Promise<unknown> };
beforeEach(() => {
vi.clearAllMocks();
service = new PaymentService();
mockProvider = {
request: vi.fn() as (args: { method: string; params?: unknown[] }) => Promise<unknown>,
};
});
describe('payWithETH', () => {
it('sends ETH payment', async () => {
const hash = await service.payWithETH(
mockProvider,
'QmTest123',
1024,
1000000000000000n
);
expect(hash).toBe('0xTxHash');
});
});
describe('approveAndPayWithToken', () => {
it('approves and pays with token', async () => {
const result = await service.approveAndPayWithToken(
mockProvider,
'0xTokenAddress',
'QmTest123',
1024,
1000000n,
'USDC'
);
expect(result).toHaveProperty('payHash');
});
});
describe('adminSetPricePerMB', () => {
it('sets price per MB', async () => {
const hash = await service.adminSetPricePerMB(
mockProvider,
1000000000000000n
);
expect(hash).toBe('0xTxHash');
});
});
describe('adminSetFreeTierBytes', () => {
it('sets free tier bytes', async () => {
const hash = await service.adminSetFreeTierBytes(
mockProvider,
1048576n
);
expect(hash).toBe('0xTxHash');
});
});
describe('adminConfigureToken', () => {
it('configures token', async () => {
const hash = await service.adminConfigureToken(
mockProvider,
'USDC',
'0xTokenAddress',
6,
1000000n,
true
);
expect(hash).toBe('0xTxHash');
});
});
describe('isDeployed', () => {
it('returns false when using default contract address', async () => {
// Default address is 0x0...0, dus zou false moeten retourneren
const result = await service.isDeployed();
expect(result).toBe(false);
});
});
});