import { Connection, Keypair } from '@solana/web3.js'; import { BN } from '@staratlas/anchor'; import { BuildTransactionsType, InstructionWithSigners, buildAndSignTransactionsFromIxWithSigners, keypairToAsyncSigner, sendTransaction, } from '@staratlas/data-source'; import { Fleet, ShipStats } from '../src'; import { SageGameTest } from './helpers'; describe('More Fleet Tests', () => { const connection = new Connection('http://localhost:8899', 'confirmed'); const walletKeypair = Keypair.generate(); const walletSigner = keypairToAsyncSigner(walletKeypair); let sageGameTest: SageGameTest; beforeAll(async () => { const airdropTx = await connection.requestAirdrop( walletSigner.publicKey(), 500_000_000_000, ); await connection.confirmTransaction(airdropTx); sageGameTest = await SageGameTest.createGame({ funder: walletKeypair, connection, numberOfShipsInFleet: 5, addCargoToFleet: false, fleetToIdle: false, }); }); it('prepare', async () => { if ( !sageGameTest.mints || !sageGameTest.fleets || !sageGameTest.sectors || !sageGameTest.cargoStatsDefinition || !sageGameTest.gameState ) { throw 'test environment not ready'; } // create one new sector const newSectorCoords = [new BN(0), new BN(-1)] as [BN, BN]; await sageGameTest.createSector({ coordinates: newSectorCoords, chunkCrewInstructions: 2, }); const oldSector = sageGameTest.sectors[0]; const newSector = sageGameTest.sectors[1]; const fleet1 = sageGameTest.fleets[0]; const fleetAccount = await sageGameTest.loadFleet(fleet1); // move to upgradeable starbase const cargoAmount = (fleetAccount.data.stats).cargoStats .ammoCapacity; await sageGameTest.sendManyToChain( [ ...sageGameTest.depositCargoToFleetInstructions( fleet1, fleetAccount.data.fuelTank, oldSector.starbases[0], oldSector.starbasePlayers[0].starbasePlayer, sageGameTest.mints[SageGameTest.FUEL], (fleetAccount.data.stats).cargoStats.fuelCapacity, ), ...sageGameTest.depositCargoToFleetInstructions( fleet1, fleetAccount.data.cargoHold, oldSector.starbases[0], oldSector.starbasePlayers[0].starbasePlayer, sageGameTest.mints[SageGameTest.REPAIR_KIT], cargoAmount, ), Fleet.loadingBayToIdle( sageGameTest.program, sageGameTest.profiles.player1.key, sageGameTest.profiles.player1.profile, sageGameTest.getProfileFactionAddress( sageGameTest.profiles.player1.profile, ), fleet1, oldSector.starbases[0], oldSector.starbasePlayers[0].starbasePlayer, sageGameTest.gameId, sageGameTest.gameState, sageGameTest.profiles.player1.index, ), ...sageGameTest.warpToCoordinateInstructions( fleetAccount.key, fleetAccount.data.fuelTank, newSectorCoords, ), ], true, ); const fleetAccount2 = await sageGameTest.loadFleet(fleet1); if (!fleetAccount2.state.MoveWarp) { throw 'Fleet should be in MoveWarp state'; } const moveTime = fleetAccount2.state.MoveWarp.warpFinish .sub(fleetAccount2.state.MoveWarp.warpStart) .toNumber(); // wait for movement to finish await new Promise((r) => setTimeout( r, (Math.max( moveTime, (fleetAccount2.data.stats).movementStats.warpCoolDown, ) + 1) * 1000, ), ); // cannot transfer cargo to fleet if not docked let success = true; try { await sageGameTest.sendManyToChain([ ...sageGameTest.moveWarpHandlerInstructions(fleet1), ...sageGameTest.depositCargoToFleetInstructions( fleet1, fleetAccount.data.fuelTank, oldSector.starbases[0], oldSector.starbasePlayers[0].starbasePlayer, sageGameTest.mints[SageGameTest.FUEL], (fleetAccount.data.stats).cargoStats.fuelCapacity, ), ]); } catch (err) { expect(JSON.stringify(err as Error)).toContain('InvalidState'); success = false; } expect(success).toBe(false); // dock await sageGameTest.sendManyToChain([ ...sageGameTest.moveWarpHandlerInstructions(fleet1), Fleet.idleToLoadingBay( sageGameTest.program, sageGameTest.profiles.player1.key, sageGameTest.profiles.player1.profile, sageGameTest.getProfileFactionAddress( sageGameTest.profiles.player1.profile, ), fleet1, newSector.starbases[0], newSector.starbasePlayers[0].starbasePlayer, sageGameTest.gameId, sageGameTest.gameState, sageGameTest.profiles.player1.index, ), ]); }); it('fleet can only transfer cargo at the starbase where it is docked', async () => { if ( !sageGameTest.mints || !sageGameTest.sectors || !sageGameTest.cargoStatsDefinition || !sageGameTest.fleets || !sageGameTest.gameState ) { throw 'test environment not ready'; } const oldSector = sageGameTest.sectors[0]; const newSector = sageGameTest.sectors[1]; const fleet1 = sageGameTest.fleets[0]; const fleetAccount = await sageGameTest.loadFleet(fleet1); const depositCargoIxs = sageGameTest.depositCargoToFleetInstructions( fleet1, fleetAccount.data.cargoHold, oldSector.starbases[0], oldSector.starbasePlayers[0].starbasePlayer, sageGameTest.mints[SageGameTest.REPAIR_KIT], 1, ); const depositCargoAtWrongStarbase = async () => { const signedIxs = await Promise.all( depositCargoIxs.map((it) => it(sageGameTest.funder)), ); const originalDepositCargoIx = [signedIxs.pop()].flat(); // switch the starbase to the wrong one originalDepositCargoIx[0]?.instruction.keys.pop(); originalDepositCargoIx[0]?.instruction.keys.push({ pubkey: newSector.starbases[0], isSigner: false, isWritable: false, }); signedIxs.push( originalDepositCargoIx as unknown as InstructionWithSigners, ); const transactions = signedIxs.map( (ixs): BuildTransactionsType => ({ ixs: ixs, connectionOrRbh: { connection: sageGameTest.connection }, }), ); const txs = await buildAndSignTransactionsFromIxWithSigners( transactions, sageGameTest.funder, ); for (const tx of txs) { const result = await sendTransaction(tx, connection, { sendOptions: { skipPreflight: false, }, }); if (result.value.isErr()) { throw result.value.error; } } return signedIxs; }; let success = true; try { await depositCargoAtWrongStarbase(); } catch (err) { expect(JSON.stringify(err as Error)).toContain('StarbaseMismatch'); success = false; } expect(success).toBe(false); const withdrawCargoIxs = sageGameTest.withdrawCargoFromFleetInstructions( fleet1, fleetAccount.data.cargoHold, oldSector.starbases[0], oldSector.starbasePlayers[0].starbasePlayer, sageGameTest.mints[SageGameTest.REPAIR_KIT], 1, ); const withdrawCargoAtWrongStarbase = async () => { const signedIxs = await Promise.all( withdrawCargoIxs.map((it) => it(sageGameTest.funder)), ); const originalDepositCargoIx = [signedIxs.pop()].flat(); // switch the starbase to the wrong one originalDepositCargoIx[0]?.instruction.keys.pop(); originalDepositCargoIx[0]?.instruction.keys.push({ pubkey: newSector.starbases[0], isSigner: false, isWritable: false, }); signedIxs.push( originalDepositCargoIx as unknown as InstructionWithSigners, ); const transactions = signedIxs.map( (ixs): BuildTransactionsType => ({ ixs: ixs, connectionOrRbh: { connection: sageGameTest.connection }, }), ); const txs = await buildAndSignTransactionsFromIxWithSigners( transactions, sageGameTest.funder, ); for (const tx of txs) { const result = await sendTransaction(tx, connection, { sendOptions: { skipPreflight: false, }, }); if (result.value.isErr()) { throw result.value.error; } } return signedIxs; }; success = true; try { await withdrawCargoAtWrongStarbase(); } catch (err) { expect(JSON.stringify(err as Error)).toContain('StarbaseMismatch'); success = false; } expect(success).toBe(false); }); it('fleet can only add ships at the starbase where it is docked', async () => { if ( !sageGameTest.mints || !sageGameTest.sectors || !sageGameTest.cargoStatsDefinition || !sageGameTest.fleets || !sageGameTest.ships || !sageGameTest.gameState ) { throw 'test environment not ready'; } const oldSector = sageGameTest.sectors[0]; const fleet1 = sageGameTest.fleets[0]; let success = true; try { await sageGameTest.sendManyToChain( Fleet.addShipToFleet( sageGameTest.program, sageGameTest.profiles.player1.key, sageGameTest.profiles.player1.profile, sageGameTest.getProfileFactionAddress( sageGameTest.profiles.player1.profile, ), fleet1, sageGameTest.getShips()[0], oldSector.starbasePlayers[0].starbasePlayer, oldSector.starbases[0], sageGameTest.gameId, sageGameTest.gameState, { shipAmount: 1, shipEscrowIndex: 0, fleetShipInfoIndex: 0, keyIndex: sageGameTest.profiles.player1.index, }, ), ); } catch (err) { expect(JSON.stringify(err as Error)).toContain('StarbaseMismatch'); success = false; } expect(success).toBe(false); }); it('fleet can only disband at the starbase where it is docked', async () => { if ( !sageGameTest.mints || !sageGameTest.sectors || !sageGameTest.cargoStatsDefinition || !sageGameTest.fleets || !sageGameTest.ships || !sageGameTest.gameState ) { throw 'test environment not ready'; } const oldSector = sageGameTest.sectors[0]; const fleet1 = sageGameTest.fleets[0]; const fleetAccount = await sageGameTest.loadFleet(fleet1); const disband = Fleet.disbandFleet( sageGameTest.program, sageGameTest.cargoProgram, sageGameTest.profiles.player1.key, sageGameTest.profiles.player1.profile, sageGameTest.getProfileFactionAddress( sageGameTest.profiles.player1.profile, ), fleetAccount, oldSector.starbasePlayers[0].starbasePlayer, oldSector.starbases[0], sageGameTest.gameId, sageGameTest.gameState, { keyIndex: sageGameTest.profiles.player1.index, }, ); let success = true; try { await sageGameTest.sendManyToChain(disband.instructions); } catch (err) { expect(JSON.stringify(err as Error)).toContain('StarbaseMismatch'); success = false; } expect(success).toBe(false); }); it('fleet can only undock at the starbase where it is docked', async () => { if ( !sageGameTest.mints || !sageGameTest.sectors || !sageGameTest.cargoStatsDefinition || !sageGameTest.fleets || !sageGameTest.ships || !sageGameTest.gameState ) { throw 'test environment not ready'; } const oldSector = sageGameTest.sectors[0]; const fleet1 = sageGameTest.fleets[0]; let success = true; try { await sageGameTest.sendManyToChain( Fleet.loadingBayToIdle( sageGameTest.program, sageGameTest.profiles.player1.key, sageGameTest.profiles.player1.profile, sageGameTest.getProfileFactionAddress( sageGameTest.profiles.player1.profile, ), fleet1, oldSector.starbases[0], oldSector.starbasePlayers[0].starbasePlayer, sageGameTest.gameId, sageGameTest.gameState, sageGameTest.profiles.player1.index, ), ); } catch (err) { expect(JSON.stringify(err as Error)).toContain('StarbaseMismatch'); success = false; } expect(success).toBe(false); }); });