/** * @file: kyc.live.test.js * @description: Live integration tests for the KYC backend API. * * @preconditions: * 1. A local Pezkuwi dev node must be running and accessible at `ws://127.0.0.1:8082`. * 2. The KYC backend server must be running and accessible at `http://127.0.0.1:8082`. * 3. The Supabase database must be clean, or the tests will fail on unique constraints. * 4. The backend's .env file must be correctly configured (SUDO_SEED, FOUNDER_ADDRESS, etc.). * 5. The backend must be running in a mode that bypasses signature checks for these tests (e.g., NODE_ENV=test). * * @execution: * Run this file with Jest: `npx jest kyc.live.test.js` */ import { ApiPromise, WsProvider, Keyring } from '@pezkuwi/api'; import axios from 'axios'; // Using axios for HTTP requests // ======================================== // TEST CONFIGURATION // ======================================== const API_BASE_URL = 'http://127.0.0.1:8082/api'; const WS_ENDPOINT = 'ws://127.0.0.1:8082'; // Set a long timeout for all tests in this file jest.setTimeout(60000); // 60 seconds // ======================================== // TEST SETUP & TEARDOWN // ======================================== let api; let keyring; let sudo, founder, councilMember, user1, user2; beforeAll(async () => { const wsProvider = new WsProvider(WS_ENDPOINT); api = await ApiPromise.create({ provider: wsProvider }); keyring = new Keyring({ type: 'sr25519' }); // Define accounts from the well-known dev seeds sudo = keyring.addFromUri('//Alice'); founder = keyring.addFromUri('//Alice'); // Assuming founder is also sudo for tests councilMember = keyring.addFromUri('//Bob'); user1 = keyring.addFromUri('//Charlie'); user2 = keyring.addFromUri('//Dave'); console.log('Connected to node and initialized accounts.'); }); afterAll(async () => { if (api) await api.disconnect(); console.log('Disconnected from node.'); }); // Helper to wait for the next finalized block const nextBlock = () => new Promise(res => api.rpc.chain.subscribeFinalizedHeads(() => res())); // ======================================== // LIVE INTEGRATION TESTS // ======================================== describe('Live KYC Workflow', () => { it('should run a full KYC lifecycle: Setup -> Propose -> Vote -> Approve -> Verify', async () => { // ----------------------------------------------------------------- // PHASE 1: SETUP // ----------------------------------------------------------------- console.log('PHASE 1: Setting up initial state...'); // 1a. Clear and set up the council in the database via API await axios.post(`${API_BASE_URL}/council/add-member`, { newMemberAddress: councilMember.address, signature: '0x00', message: `addCouncilMember:${councilMember.address}` }); // 1b. User1 sets their identity on-chain await api.tx.identityKyc.setIdentity("User1", "user1@test.com").signAndSend(user1); // 1c. User1 applies for KYC on-chain await api.tx.identityKyc.applyForKyc([], "Live test application").signAndSend(user1); await nextBlock(); // Wait for setup transactions to be finalized // Verification of setup let kycStatus = (await api.query.identityKyc.kycStatusOf(user1.address)).toString(); expect(kycStatus).toBe('Pending'); console.log('User1 KYC status is correctly set to Pending.'); // ----------------------------------------------------------------- // PHASE 2: API ACTION (Propose & Vote) // ----------------------------------------------------------------- console.log('PHASE 2: Council member proposes user via API...'); const proposeResponse = await axios.post(`${API_BASE_URL}/kyc/propose`, { userAddress: user1.address, proposerAddress: councilMember.address, signature: '0x00', message: `proposeKYC:${user1.address}` }); expect(proposeResponse.status).toBe(201); console.log('Proposal successful. Backend should now be executing `approve_kyc`...'); // Since we have 1 council member and the threshold is 60%, the proposer's // automatic "aye" vote is enough to trigger `checkAndExecute`. // We need to wait for the backend to see the vote, execute the transaction, // and for that transaction to be finalized on-chain. This can take time. await new Promise(resolve => setTimeout(resolve, 15000)); // Wait 15s for finalization // ----------------------------------------------------------------- // PHASE 3: VERIFICATION // ----------------------------------------------------------------- console.log('PHASE 3: Verifying final state on-chain and in DB...'); // 3a. Verify on-chain status is now 'Approved' kycStatus = (await api.query.identityKyc.kycStatusOf(user1.address)).toString(); expect(kycStatus).toBe('Approved'); console.log('SUCCESS: On-chain KYC status for User1 is now Approved.'); // 3b. Verify via API that the proposal is no longer pending const pendingResponse = await axios.get(`${API_BASE_URL}/kyc/pending`); const pendingForUser1 = pendingResponse.data.pending.find(p => p.userAddress === user1.address); expect(pendingForUser1).toBeUndefined(); console.log('SUCCESS: Pending proposals list is correctly updated.'); }); it('should reject a proposal from a non-council member', async () => { console.log('Testing rejection of non-council member proposal...'); const nonCouncilMember = keyring.addFromUri('//Eve'); // Attempt to propose from an address not in the council DB await expect(axios.post(`${API_BASE_URL}/kyc/propose`, { userAddress: user2.address, proposerAddress: nonCouncilMember.address, signature: '0x00', message: `proposeKYC:${user2.address}` })).rejects.toThrow('Request failed with status code 403'); console.log('SUCCESS: API correctly returned 403 Forbidden.'); }); });