/** * @file: pez-rewards.live.test.js * @description: Live integration tests for the PezRewards pallet. * * @preconditions: * 1. A local Pezkuwi dev node must be running and accessible at `ws://127.0.0.1:8082`. * 2. The node must have the `pezRewards` pallet. * 3. The tests require a funded sudo account (`//Alice`). */ import { ApiPromise, WsProvider, Keyring } from '@pezkuwi/api'; import { jest } from '@jest/globals'; // ======================================== // TEST CONFIGURATION // ======================================== const WS_ENDPOINT = 'ws://127.0.0.1:8082'; jest.setTimeout(120000); // 2 minutes, as this involves waiting for blocks // ======================================== // TEST SETUP & TEARDOWN // ======================================== let api; let keyring; let sudo, user1, user2; // Helper to wait for N finalized blocks const waitForBlocks = async (count) => { let blocksLeft = count; return new Promise(resolve => { const unsubscribe = api.rpc.chain.subscribeFinalizedHeads(() => { blocksLeft--; if (blocksLeft <= 0) { unsubscribe(); resolve(); } }); }); }; // Helper to send a transaction and wait for it to be finalized const sendAndFinalize = (tx, signer) => { return new Promise((resolve, reject) => { tx.signAndSend(signer, ({ status, dispatchError }) => { if (status.isFinalized) { if (dispatchError) { const decoded = api.registry.findMetaError(dispatchError.asModule); reject(new Error(`${decoded.section}.${decoded.name}`)); } else { resolve(); } } }).catch(reject); }); }; beforeAll(async () => { const wsProvider = new WsProvider(WS_ENDPOINT); api = await ApiPromise.create({ provider: wsProvider }); keyring = new Keyring({ type: 'sr25519' }); sudo = keyring.addFromUri('//Alice'); user1 = keyring.addFromUri('//Charlie'); user2 = keyring.addFromUri('//Dave'); console.log('Connected to node for PezRewards tests.'); }); afterAll(async () => { if (api) await api.disconnect(); }); // ======================================== // LIVE PALLET TESTS // ======================================== describe('PezRewards Pallet Live Workflow', () => { // We run the tests in a single, sequential `it` block to manage state // across different epochs without complex cleanup. it('should run a full epoch lifecycle: Record -> Finalize -> Claim', async () => { // ----------------------------------------------------------------- // PHASE 1: RECORD SCORES (in the current epoch) // ----------------------------------------------------------------- console.log('PHASE 1: Recording trust scores...'); const currentEpoch = (await api.query.pezRewards.getCurrentEpochInfo()).currentEpoch.toNumber(); console.log(`Operating in Epoch ${currentEpoch}.`); await sendAndFinalize(api.tx.pezRewards.recordTrustScore(), user1); await sendAndFinalize(api.tx.pezRewards.recordTrustScore(), user2); const score1 = (await api.query.pezRewards.getUserTrustScoreForEpoch(currentEpoch, user1.address)).unwrap().toNumber(); const score2 = (await api.query.pezRewards.getUserTrustScoreForEpoch(currentEpoch, user2.address)).unwrap().toNumber(); // These values depend on the mock trust score provider in the dev node console.log(`Scores recorded: User1 (${score1}), User2 (${score2})`); expect(score1).toBeGreaterThan(0); expect(score2).toBeGreaterThanOrEqual(0); // Dave might have 0 score // ----------------------------------------------------------------- // PHASE 2: FINALIZE EPOCH // ----------------------------------------------------------------- console.log('PHASE 2: Waiting for epoch to end and finalizing...'); // Wait for the epoch duration to pass. Get this from the pallet's constants. const blocksPerEpoch = api.consts.pezRewards.blocksPerEpoch.toNumber(); console.log(`Waiting for ${blocksPerEpoch} blocks to pass...`); await waitForBlocks(blocksPerEpoch); await sendAndFinalize(api.tx.pezRewards.finalizeEpoch(), sudo); const epochStatus = (await api.query.pezRewards.epochStatus(currentEpoch)).toString(); expect(epochStatus).toBe('ClaimPeriod'); console.log(`Epoch ${currentEpoch} is now in ClaimPeriod.`); // ----------------------------------------------------------------- // PHASE 3: CLAIM REWARDS // ----------------------------------------------------------------- console.log('PHASE 3: Claiming rewards...'); // User 1 claims their reward await sendAndFinalize(api.tx.pezRewards.claimReward(currentEpoch), user1); const claimedReward = await api.query.pezRewards.getClaimedReward(currentEpoch, user1.address); expect(claimedReward.isSome).toBe(true); console.log(`User1 successfully claimed a reward of ${claimedReward.unwrap().toNumber()}.`); // ----------------------------------------------------------------- // PHASE 4: VERIFY FAILURE CASES // ----------------------------------------------------------------- console.log('PHASE 4: Verifying failure cases...'); // User 1 tries to claim again await expect( sendAndFinalize(api.tx.pezRewards.claimReward(currentEpoch), user1) ).rejects.toThrow('pezRewards.RewardAlreadyClaimed'); console.log('Verified that a user cannot claim twice.'); // Wait for the claim period to expire const claimPeriodBlocks = api.consts.pezRewards.claimPeriodBlocks.toNumber(); console.log(`Waiting for claim period (${claimPeriodBlocks} blocks) to expire...`); await waitForBlocks(claimPeriodBlocks + 1); // +1 to be safe // User 2 tries to claim after the period is over await expect( sendAndFinalize(api.tx.pezRewards.claimReward(currentEpoch), user2) ).rejects.toThrow('pezRewards.ClaimPeriodExpired'); console.log('Verified that a user cannot claim after the claim period.'); }); });