import * as anchor from "@coral-xyz/anchor"; import { Transaction } from "@solana/web3.js"; import { expect } from "chai"; import fetch from "node-fetch"; import { getTestContext } from "../setup"; describe("Join and Leave Auction (API)", () => { it("Company 1 (BMW) Joins Active Auction", async () => { const ctx = getTestContext(); const { connection, program, axumBaseUrl, company1Admin, company1Pda, auctionPda, } = ctx; console.log("Company 1 joining auction:", auctionPda.toBase58()); // Step 1: Verify company is not in auction yet const companyBefore = await program.account.company.fetch(company1Pda); expect(companyBefore.inAuction).to.be.false; expect(companyBefore.placedBid).to.be.false; console.log("✓ Company 1 is not in auction yet"); // Step 2: Verify auction is active const auction = await program.account.auction.fetch(auctionPda); expect(auction.status).to.deep.equal({ active: {} }); expect(auction.isActive).to.be.true; console.log("✓ Auction is active"); // Step 3: Create API request payload const request = { auction: auctionPda.toBase58(), company: company1Pda.toBase58(), company_admin: company1Admin.publicKey.toBase58(), }; console.log("Calling API to create join auction transaction..."); // Step 4: Call Axum API to get unsigned transaction const createTxResponse = await fetch( `${axumBaseUrl}/api/primary-market/join-auction`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), } ); if (!createTxResponse.ok) { const error = await createTxResponse.text(); throw new Error(`Failed to create transaction: ${error}`); } const { transaction_base64, message } = await createTxResponse.json(); console.log(`API Response: ${message}`); // Step 5: Deserialize and sign transaction const txBuffer = Buffer.from(transaction_base64, "base64"); const transaction = Transaction.from(txBuffer); console.log("Transaction signer (expected):"); console.log(" Company 1 Admin:", company1Admin.publicKey.toBase58()); // Sign with company1Admin transaction.sign(company1Admin); console.log("Transaction signed locally"); // Step 6: Submit signed transaction to backend const signedTxBase64 = transaction.serialize().toString("base64"); const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedTxBase64 }), }); if (!submitTxResponse.ok) { const error = await submitTxResponse.text(); throw new Error(`Failed to submit transaction: ${error}`); } const { signature } = await submitTxResponse.json(); console.log(`✓ Transaction signature: ${signature}`); // Step 7: Verify on-chain state - COMPANY JOINED const companyAfter = await program.account.company.fetch(company1Pda); expect(companyAfter.inAuction).to.be.true; expect(companyAfter.placedBid).to.be.false; console.log("✓ Company 1 state verified"); console.log(` - In Auction: ${companyAfter.inAuction}`); console.log(` - Placed Bid: ${companyAfter.placedBid}`); // Step 8: Verify events/logs const txDetails = await connection.getTransaction(signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0, }); if (txDetails?.meta?.logMessages) { const logs = txDetails.meta.logMessages; const hasJoinLog = logs.some((log) => log.includes("Company joined auction") ); if (hasJoinLog) { console.log("✓ Company joined auction log message found"); } const hasEventLog = logs.some((log) => log.includes("CompanyJoinedAuction") ); if (hasEventLog) { console.log("✓ CompanyJoinedAuction event emitted"); } } console.log("✓ Company 1 joined auction successfully via API"); }); it("Company 2 (Total) Joins Active Auction", async () => { const ctx = getTestContext(); const { connection, program, axumBaseUrl, company2Admin, company2Pda, auctionPda, } = ctx; console.log("Company 2 joining auction:", auctionPda.toBase58()); // Verify company is not in auction yet const companyBefore = await program.account.company.fetch(company2Pda); expect(companyBefore.inAuction).to.be.false; const request = { auction: auctionPda.toBase58(), company: company2Pda.toBase58(), company_admin: company2Admin.publicKey.toBase58(), }; const createTxResponse = await fetch( `${axumBaseUrl}/api/primary-market/join-auction`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), } ); if (!createTxResponse.ok) { const error = await createTxResponse.text(); throw new Error(`Failed to create transaction: ${error}`); } const { transaction_base64 } = await createTxResponse.json(); const txBuffer = Buffer.from(transaction_base64, "base64"); const transaction = Transaction.from(txBuffer); transaction.sign(company2Admin); const signedTxBase64 = transaction.serialize().toString("base64"); const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedTxBase64 }), }); if (!submitTxResponse.ok) { const error = await submitTxResponse.text(); throw new Error(`Failed to submit transaction: ${error}`); } const { signature } = await submitTxResponse.json(); console.log(`✓ Transaction signature: ${signature}`); // Verify company joined const companyAfter = await program.account.company.fetch(company2Pda); expect(companyAfter.inAuction).to.be.true; console.log("✓ Company 2 joined auction successfully"); }); it("Should Reject Company Joining Twice", async () => { const ctx = getTestContext(); const { axumBaseUrl, company1Admin, company1Pda, auctionPda, } = ctx; const request = { auction: auctionPda.toBase58(), company: company1Pda.toBase58(), company_admin: company1Admin.publicKey.toBase58(), }; // Try to join again const createTxResponse = await fetch( `${axumBaseUrl}/api/primary-market/join-auction`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), } ); expect(createTxResponse.ok).to.be.false; const errorText = await createTxResponse.text(); console.log("✓ Correctly rejected duplicate join:", errorText); }); it("Company 2 Leaves Auction Before Placing Bid", async () => { const ctx = getTestContext(); const { connection, program, axumBaseUrl, company2Admin, company2Pda, auctionPda, } = ctx; console.log("Company 2 leaving auction..."); // Verify company is in auction and has not placed bid const companyBefore = await program.account.company.fetch(company2Pda); expect(companyBefore.inAuction).to.be.true; expect(companyBefore.placedBid).to.be.false; const request = { auction: auctionPda.toBase58(), company: company2Pda.toBase58(), company_admin: company2Admin.publicKey.toBase58(), }; console.log("Calling API to create leave auction transaction..."); const createTxResponse = await fetch( `${axumBaseUrl}/api/primary-market/leave-auction`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), } ); if (!createTxResponse.ok) { const error = await createTxResponse.text(); throw new Error(`Failed to create transaction: ${error}`); } const { transaction_base64, message } = await createTxResponse.json(); console.log(`API Response: ${message}`); const txBuffer = Buffer.from(transaction_base64, "base64"); const transaction = Transaction.from(txBuffer); transaction.sign(company2Admin); const signedTxBase64 = transaction.serialize().toString("base64"); const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedTxBase64 }), }); if (!submitTxResponse.ok) { const error = await submitTxResponse.text(); throw new Error(`Failed to submit transaction: ${error}`); } const { signature } = await submitTxResponse.json(); console.log(`✓ Transaction signature: ${signature}`); // Verify company left const companyAfter = await program.account.company.fetch(company2Pda); expect(companyAfter.inAuction).to.be.false; console.log("✓ Company 2 left auction successfully"); // Verify events/logs const txDetails = await connection.getTransaction(signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0, }); if (txDetails?.meta?.logMessages) { const logs = txDetails.meta.logMessages; const hasLeaveLog = logs.some((log) => log.includes("Company left auction") ); if (hasLeaveLog) { console.log("✓ Company left auction log message found"); } } }); it("Company 2 Rejoins Auction", async () => { const ctx = getTestContext(); const { program, axumBaseUrl, company2Admin, company2Pda, auctionPda, } = ctx; // Rejoin for subsequent tests const request = { auction: auctionPda.toBase58(), company: company2Pda.toBase58(), company_admin: company2Admin.publicKey.toBase58(), }; const createTxResponse = await fetch( `${axumBaseUrl}/api/primary-market/join-auction`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), } ); if (!createTxResponse.ok) { const error = await createTxResponse.text(); throw new Error(`Failed to create transaction: ${error}`); } const { transaction_base64 } = await createTxResponse.json(); const txBuffer = Buffer.from(transaction_base64, "base64"); const transaction = Transaction.from(txBuffer); transaction.sign(company2Admin); const signedTxBase64 = transaction.serialize().toString("base64"); const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedTxBase64 }), }); if (!submitTxResponse.ok) { const error = await submitTxResponse.text(); throw new Error(`Failed to submit transaction: ${error}`); } const { signature } = await submitTxResponse.json(); console.log(`✓ Company 2 rejoined: ${signature}`); // Verify const companyAfter = await program.account.company.fetch(company2Pda); expect(companyAfter.inAuction).to.be.true; }); });