UNPKG

@river-build/sdk

Version:

For more details, visit the following resources:

280 lines 14.2 kB
import { dlog } from '@river-build/dlog'; import { makeRiverConfig } from '../../riverConfig'; import { Bot } from '../../sync-agent/utils/bot'; import { ethers } from 'ethers'; import { ETH_ADDRESS, getSpaceReviewEventData, getSpaceReviewEventDataBin, SpaceReviewAction, } from '@river-build/web3'; import { waitFor } from '../testUtils'; import { BlockchainTransaction_SpaceReview_Action } from '@river-build/proto'; import { UnauthenticatedClient } from '../../unauthenticatedClient'; const base_log = dlog('csb:test:transaction_SpaceReview'); describe('transaction_SpaceReview', () => { const riverConfig = makeRiverConfig(); const bobIdentity = new Bot(undefined, riverConfig); const aliceIdentity = new Bot(undefined, riverConfig); const alicesOtherWallet = ethers.Wallet.createRandom(); let bob; let alice; let spaceIdWithAlice; let spaceIdWithoutAlice; let aliceTokenId; const chainId = riverConfig.base.chainConfig.chainId; let spaceReviewEventId; let orphanReviewReceipt; beforeAll(async () => { const log = base_log.extend('beforeAll'); log('start'); // fund wallets await Promise.all([bobIdentity.fundWallet(), aliceIdentity.fundWallet()]); // make agents bob = await bobIdentity.makeSyncAgent(); alice = await aliceIdentity.makeSyncAgent(); // start agents await Promise.all([ bob.start(), alice.start(), alice.riverConnection.spaceDapp.walletLink.linkWalletToRootKey(aliceIdentity.signer, alicesOtherWallet), ]); // make a space const { spaceId: sid1 } = await bob.spaces.createSpace({ spaceName: 'Lets REvieW 1' }, bobIdentity.signer); spaceIdWithAlice = sid1; // join the space await alice.spaces.joinSpace(spaceIdWithAlice, aliceIdentity.signer); // get alice's token id const aliceTokenId_ = await bob.riverConnection.spaceDapp.getTokenIdOfOwner(spaceIdWithAlice, aliceIdentity.rootWallet.address); expect(aliceTokenId_).toBeDefined(); aliceTokenId = aliceTokenId_; // make another space const { spaceId: sid2 } = await bob.spaces.createSpace({ spaceName: 'Lets REvieW 2' }, bobIdentity.signer); spaceIdWithoutAlice = sid2; log('complete', { spaceIdWithAlice, spaceIdWithoutAlice }); // todo, leave a review on the without alice space }); afterAll(async () => { await bob.stop(); await alice.stop(); }); test('alice adds review', async () => { const web3Space = alice.riverConnection.spaceDapp.getSpace(spaceIdWithAlice); expect(web3Space).toBeDefined(); const tx = await web3Space.Review.addReview({ rating: 5, comment: 'This is a test review', }, aliceIdentity.signer); expect(tx).toBeDefined(); const receipt = await tx.wait(2); expect(receipt).toBeDefined(); const reviewEvent = getSpaceReviewEventData(receipt.logs, aliceIdentity.userId); expect(reviewEvent).toBeDefined(); expect(reviewEvent.rating).toBe(5); expect(reviewEvent.comment).toBe('This is a test review'); await alice.riverConnection.client?.addTransaction_SpaceReview(chainId, receipt, reviewEvent, spaceIdWithAlice); }); test('alice sees review in user stream', async () => { const stream = alice.riverConnection.client.stream(alice.riverConnection.client.userStreamId); if (!stream) throw new Error('no stream found'); const reviewEvent = await waitFor(() => { const reviewEvents = stream.view.timeline.filter(isUserBlockchainTransaction); expect(reviewEvents.length).toBeGreaterThan(0); const reviewEvent = reviewEvents[0]; expect(reviewEvent).toBeDefined(); if (!reviewEvent || reviewEvent.remoteEvent?.event.payload.value?.content.case !== 'blockchainTransaction') throw new Error('no review event whaaa?'); return reviewEvent.remoteEvent.event.payload.value.content.value; }); if (reviewEvent?.content.case !== 'spaceReview') { throw new Error('no review event whaaa?'); } expect(reviewEvent.receipt).toBeDefined(); expect(reviewEvent.content.value.action).toBe(BlockchainTransaction_SpaceReview_Action.Add); if (!reviewEvent.content.value.event) { throw new Error('no event in space review'); } const { comment, rating, action } = getSpaceReviewEventDataBin(reviewEvent.receipt.logs, reviewEvent.content.value.event.user); expect(action).toBe(SpaceReviewAction.Add); expect(comment).toBe('This is a test review'); expect(rating).toBe(5); }); test('alice sees review in space stream', async () => { const stream = alice.riverConnection.client.stream(spaceIdWithAlice); if (!stream) throw new Error('no stream found'); const reviewEvent = await waitFor(() => { const reviewEvents = stream.view.membershipContent.spaceReviews; expect(reviewEvents.length).toBe(1); const reviewEvent = reviewEvents[0]; return reviewEvent; }); spaceReviewEventId = reviewEvent.eventHashStr; const { comment, rating } = reviewEvent.review; expect(comment).toBe('This is a test review'); expect(rating).toBe(5); }); test('bob sees review in space stream', async () => { const stream = bob.riverConnection.client.stream(spaceIdWithAlice); if (!stream) throw new Error('no stream found'); const reviewEvent = await waitFor(() => { const reviewEvents = stream.view.membershipContent.spaceReviews; expect(reviewEvents.length).toBe(1); return reviewEvents[0]; }); expect(reviewEvent).toBeDefined(); expect(stream.view.membershipContent.spaceReviews.length).toBe(1); }); test('bob can tip review', async () => { const tx = await bob.riverConnection.spaceDapp.tip({ spaceId: spaceIdWithAlice, tokenId: aliceTokenId, currency: ETH_ADDRESS, amount: 1000n, messageId: spaceReviewEventId, channelId: spaceIdWithAlice, receiver: aliceIdentity.userId, }, bobIdentity.signer); const receipt = await tx.wait(2); expect(receipt).toBeDefined(); const tipEvent = bob.riverConnection.spaceDapp.getTipEvent(spaceIdWithAlice, receipt, bobIdentity.rootWallet.address); expect(tipEvent).toBeDefined(); if (!tipEvent) throw new Error('no tip event found'); await bob.riverConnection.client.addTransaction_Tip(chainId, receipt, tipEvent, aliceIdentity.rootWallet.address); }); test('alice can see tip received in user stream', async () => { const stream = alice.riverConnection.client.stream(alice.riverConnection.client.userStreamId); if (!stream) throw new Error('no stream found'); const tipEvent = await waitFor(() => { const tipEvents = stream.view.timeline.filter(isUserReceivedBlockchainTransaction); expect(tipEvents.length).toBeGreaterThan(0); const tip = tipEvents[0]; if (!tip || tip.remoteEvent?.event.payload.value?.content.case !== 'receivedBlockchainTransaction') throw new Error('no tip event found'); return tip.remoteEvent.event.payload.value.content.value; }); if (!tipEvent) throw new Error('no tip event found'); expect(tipEvent.transaction?.receipt).toBeDefined(); }); test('alice can see tip in space stream', async () => { const stream = alice.riverConnection.client.stream(spaceIdWithAlice); if (!stream) throw new Error('no stream found'); const tipEvent = await waitFor(() => { const tipEvents = stream.view.timeline.filter(isMemberBlockchainTransaction); expect(tipEvents.length).toBeGreaterThan(0); const tip = tipEvents[0]; if (!tip || tip.remoteEvent?.event.payload.value?.content.case !== 'memberBlockchainTransaction') throw new Error('no tip event found'); return tip.remoteEvent.event.payload.value.content.value; }); if (!tipEvent) throw new Error('no tip event found'); expect(tipEvent.transaction?.receipt).toBeDefined(); }); test('alice updates review', async () => { const web3Space = alice.riverConnection.spaceDapp.getSpace(spaceIdWithAlice); expect(web3Space).toBeDefined(); const tx = await web3Space.Review.updateReview({ rating: 4, comment: 'This is a worse test review', }, aliceIdentity.signer); expect(tx).toBeDefined(); const receipt = await tx.wait(2); expect(receipt).toBeDefined(); const reviewEvent = getSpaceReviewEventData(receipt.logs, aliceIdentity.userId); expect(reviewEvent).toBeDefined(); expect(reviewEvent.rating).toBe(4); expect(reviewEvent.comment).toBe('This is a worse test review'); await expect(alice.riverConnection.client.addTransaction_SpaceReview(chainId, receipt, reviewEvent, spaceIdWithAlice)).resolves.not.toThrow(); }); test('alice deletes review', async () => { const web3Space = alice.riverConnection.spaceDapp.getSpace(spaceIdWithAlice); expect(web3Space).toBeDefined(); const tx = await web3Space.Review.deleteReview(aliceIdentity.signer); expect(tx).toBeDefined(); const receipt = await tx.wait(2); expect(receipt).toBeDefined(); const reviewEvent = getSpaceReviewEventData(receipt.logs, aliceIdentity.userId); expect(reviewEvent).toBeDefined(); expect(reviewEvent.rating).toBe(0); expect(reviewEvent.comment).toBeUndefined(); await expect(alice.riverConnection.client.addTransaction_SpaceReview(chainId, receipt, reviewEvent, spaceIdWithAlice)).resolves.not.toThrow(); }); test('bob leaves an orphan review', async () => { const tx = await bob.riverConnection.spaceDapp .getSpace(spaceIdWithoutAlice) ?.Review.addReview({ rating: 5, comment: 'This is an orphan review', }, bobIdentity.signer); expect(tx).toBeDefined(); const receipt = await tx.wait(2); orphanReviewReceipt = receipt; }); test('cant add review with bad space', async () => { // bob leaves a review on spaceIdWithoutAlice const reviewEvent = { action: SpaceReviewAction.Add, user: bobIdentity.userId, rating: 5, }; await expect(bob.riverConnection.client.addTransaction_SpaceReview(chainId, orphanReviewReceipt, reviewEvent, spaceIdWithAlice)).rejects.toThrow('matching review event not found in receipt logs'); }); test('cant add review with bad sender', async () => { const reviewEvent = { action: SpaceReviewAction.Add, user: bobIdentity.userId, rating: 5, }; await expect( // alice didn't send this! alice.riverConnection.client.addTransaction_SpaceReview(chainId, orphanReviewReceipt, reviewEvent, spaceIdWithoutAlice)).rejects.toThrow('IsEntitled failed'); }); test('cant add review with bad rating', async () => { const reviewEvent = { action: SpaceReviewAction.Add, user: bobIdentity.userId, rating: 1, // wrong rating! }; await expect(bob.riverConnection.client.addTransaction_SpaceReview(chainId, orphanReviewReceipt, reviewEvent, spaceIdWithoutAlice)).rejects.toThrow('matching review event not found in receipt logs'); }); test('cant add review with bad action', async () => { const reviewEvent = { action: SpaceReviewAction.Update, // wrong action! user: bobIdentity.userId, rating: 5, }; await expect(bob.riverConnection.client.addTransaction_SpaceReview(chainId, orphanReviewReceipt, reviewEvent, spaceIdWithoutAlice)).rejects.toThrow('matching review event not found in receipt logs'); }); test('alice snapshot', async () => { await alice.riverConnection.client.debugForceMakeMiniblock(alice.riverConnection.client.userStreamId, { forceSnapshot: true }); const streamView = await alice.riverConnection.client.getStream(alice.riverConnection.client.userStreamId); expect(streamView).toBeDefined(); }); test('space snapshot', async () => { await bob.riverConnection.client.debugForceMakeMiniblock(spaceIdWithAlice, { forceSnapshot: true, }); const streamView = await bob.riverConnection.client.getStream(spaceIdWithAlice); expect(streamView).toBeDefined(); // after the snapshot, we no longer see the review expect(streamView.membershipContent.spaceReviews.length).toBe(0); // do some scrollback const unauthenticatedClient = new UnauthenticatedClient(alice.riverConnection.client.rpcClient); await unauthenticatedClient.scrollbackByMs(streamView, 5000); expect(streamView.membershipContent.spaceReviews.length).toBe(1); }); }); const isMemberBlockchainTransaction = (e) => e.remoteEvent?.event.payload.case === 'memberPayload' && e.remoteEvent.event.payload.value.content.case === 'memberBlockchainTransaction'; const isUserBlockchainTransaction = (e) => e.remoteEvent?.event.payload.case === 'userPayload' && e.remoteEvent.event.payload.value.content.case === 'blockchainTransaction'; const isUserReceivedBlockchainTransaction = (e) => e.remoteEvent?.event.payload.case === 'userPayload' && e.remoteEvent.event.payload.value.content.case === 'receivedBlockchainTransaction'; //# sourceMappingURL=transactions_SpaceReview.test.js.map