Crypto-Birdies/test/Unit_Testing.js
2021-12-07 15:36:30 +08:00

1247 lines
51 KiB
JavaScript

const Birdcontract = artifacts.require("CryptoBirdies");
const Marketcontract = artifacts.require("MarketPlace");
const TestBirdies = artifacts.require("TestBirdies");
const TestMarket = artifacts.require("TestMarket");
const TestErc = artifacts.require("TestErc");
const assert = require("chai").assert;
const truffleAssert = require("truffle-assertions");
const Web3 = require('web3');
const web3 = new Web3(Web3.givenProvider);
var testBirdiesInstance;
var testMarketInstance;
contract("CryptoBirdies", (accounts) => {
async function createParents() {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[0]);
}
it("should deploy correctly", async () => {
await truffleAssert.passes(Birdcontract.deployed("CryptoBird", "CBX"));
});
beforeEach(async () => {
testBirdiesInstance = await TestBirdies.new("CryptoBird", "CBX");
testMarketInstance = await TestMarket.new(testBirdiesInstance.address);
});
describe("constructor()", () =>{
it("should create and emit a correct birth event for bird0", async () => {
const zeroAddress = '0x0000000000000000000000000000000000000000';
var birdZero = await truffleAssert.createTransactionResult(
testBirdiesInstance, testBirdiesInstance.transactionHash);
truffleAssert.eventEmitted(birdZero, 'Birth', (ev) => {
return ev.owner == zeroAddress && ev.birdId == 0 && ev.mumId == 0 && ev.dadId == 0 &&
ev.genes == 115792089237316195423570985008687907853269984665640564039457584007913129639935;
}, "Event was NOT emitted with correct parameters");
});
});
describe("getContractOwner()", () =>{
it("should get the owner of the smart contract", async () => {
var owner = await testBirdiesInstance.getContractOwner();
assert.strictEqual(owner, accounts[0], "The owner was not returned correctly");
});
});
describe("breed()", () =>{
it("should pass, if both parents are owned by msg.sender", async () => {
await createParents();
await truffleAssert.passes(testBirdiesInstance.breed(1, 2));
});
it("should revert, if only the dad is owned by msg.sender", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[1]);
await truffleAssert.reverts(testBirdiesInstance.breed(1, 2));
});
it("should revert, if only the mum is owned by msg.sender", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[1]);
await truffleAssert.reverts(testBirdiesInstance.breed(2, 1));
});
it("should set the mum and dad IDs for the new bird correctly", async () => {
await createParents();
await testBirdiesInstance.breed(1, 2); // Bird3 = Baby
var baby = await testBirdiesInstance.getBird(3);
assert.equal(baby.dadId, 1, "IDs were not set correctly");
assert.equal(baby.mumId, 2, "IDs were not set correctly");
});
it("should return the child's generation correctly, if the dad's generation is less than mum's"
, async () => {
//the testfunction enables to use deterministic generations
//checking the baby for generation makes little sense
await testBirdiesInstance.testCreateGenXBird(5, 101, accounts[0]);//Bird1
await testBirdiesInstance.testCreateGenXBird(7, 202, accounts[0]);//Bird2
await testBirdiesInstance.breed(1, 2); //Bird3 - Bird1=dad, Bird2=mum
var baby = await testBirdiesInstance.getBird(3);
assert.equal(baby.generation, 6, "The generation was not set correctly")
});
it("should return the child's generation correctly, if the mum's generation is less than dad's"
, async () => {
await testBirdiesInstance.testCreateGenXBird(5, 101, accounts[0]);//Bird1
await testBirdiesInstance.testCreateGenXBird(7, 202, accounts[0]);//Bird2
await testBirdiesInstance.breed(2, 1); //Bird3 - Bird1=mum, Bird2=dad
var baby = await testBirdiesInstance.getBird(3);
assert.equal(baby.generation, 6, "The generation was not set correctly")
});
it("should return the child's generation correctly, if the parents have the same generation"
, async () => {
await testBirdiesInstance.testCreateGenXBird(4, 101, accounts[0]);//Bird1
await testBirdiesInstance.testCreateGenXBird(4, 202, accounts[0]);//Bird2
await testBirdiesInstance.breed(1, 2); //Bird3 - Bird1=dad, Bird2=mum
var baby = await testBirdiesInstance.getBird(3);
assert.equal(baby.generation, 5, "The generation was not set correctly")
});
it("should emit a birth event with correct parameters", async () => {
await createParents();
var birdy = await testBirdiesInstance.breed(1, 2);
truffleAssert.eventEmitted(birdy, 'Birth', (ev) => {
return ev.owner == accounts[0] && ev.birdId == 3 && ev.mumId == 2 && ev.dadId == 1;
}, "Event was NOT emitted with correct parameters");
});
});
describe("supportsInterface()", () =>{
it("should check, if the contract supports IERC721", async () => {
var testIERC721 = await testBirdiesInstance.supportsInterface("0x80ac58cd");
assert.strictEqual(testIERC721, true, "The contract does not support IERC721");
});
it("should check, if the contract supports IERC165", async () => {
var testIERC165 = await testBirdiesInstance.supportsInterface("0x01ffc9a7");
assert.strictEqual(testIERC165, true, "The contract does not support IERC721");
});
});
describe("createBirdGen0()", () =>{
it("should allow only the owner of the contract to create a bird", async () => {
await truffleAssert.reverts(testBirdiesInstance.createBirdGen0(101, { from: accounts[1] }));
});
it("should set the contract owner as owner of the new bird", async () => {
await testBirdiesInstance.createBirdGen0(101);
var owner = await testBirdiesInstance.ownerOf(1);
var contractOwner = await testBirdiesInstance.getContractOwner();
assert.strictEqual(owner, contractOwner, "The contract owner is not owner of this Gen0 bird");
});
it("should have generation 0", async () => {
await testBirdiesInstance.createBirdGen0(101);
var result = await testBirdiesInstance.getBird(1);
assert.equal(result.generation, 0, "The generation is incorrect");
});
it("should have the same DNA that is used when the function is called", async () => {
await testBirdiesInstance.createBirdGen0(101);
var result = await testBirdiesInstance.getBird(1);
assert.equal(result.genes, 101, "The genes are incorrect");
});
it("should add one to the Gen0 counter", async () => {
await testBirdiesInstance.createBirdGen0(101);
var result = await testBirdiesInstance.gen0Counter();
assert.equal(result, 1, "The counter is incorrect");
});
it("should only allow a maximum of 10 Gen0 birds to be created", async () => {
await testBirdiesInstance.testSetGenCounter(10);
//this should not work for an eleventh Gen0 bird.
await truffleAssert.reverts(testBirdiesInstance.createBirdGen0(111));
});
it("should call _createBird() and _transfer() and emit a birth event with correct parameters", async () => {
var birdy = await testBirdiesInstance.createBirdGen0(101);
truffleAssert.eventEmitted(birdy, 'Birth', (ev) => {
return ev.owner == accounts[0] && ev.birdId == 1 && ev.mumId == 0 && ev.dadId == 0 && ev.genes == 101;
}, "Event was NOT emitted with correct parameters");
});
});
describe("getBird()", () =>{
it("should return the correct genes of a bird", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var result = await testBirdiesInstance.getBird(1);
assert.equal(result.genes, 101, "The genes are incorrect");
});
it("should return the correct birthTime of a bird", async () => {
var startTime = Date.now();
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var endTime = Date.now();
var result = await testBirdiesInstance.getBird(1);
//it is not possible to test the exact birthtime, but this test
//makes sure the birthTime is within a narrow time window
assert(startTime/1000 <= result.birthTime <= endTime/1000, "The birthTime is incorrect");
});
it("should return the correct mumId of a bird", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var result = await testBirdiesInstance.getBird(1);
assert.equal(result.mumId, 0, "The id for the mum is incorrect");
});
it("should return the correct dadId of a bird", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var result = await testBirdiesInstance.getBird(1);
assert.equal(result.dadId, 0, "The id for the dad is incorrect");
});
it("should return the correct generation of a bird", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var result = await testBirdiesInstance.getBird(1);
assert.equal(result.generation, 0, "The generation is not zero");
});
it("should revert, if the bird does not exist", async () => {
await truffleAssert.reverts(testBirdiesInstance.getBird(5));
});
});
describe("getAllBirdsOfOwner()", () =>{
it("should return all birds owned by an address", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[1]);
var testAllBirdsOfOwner = await testBirdiesInstance.getAllBirdsOfOwner(accounts[1]);
assert.equal(testAllBirdsOfOwner.length, 1, "The number of birds was not returned correctly");
});
});
describe("balanceOf()", () =>{
it("should return the correct balance of tokens owned by an address", async () => {
var numberTokens = await testBirdiesInstance.balanceOf(accounts[0]);
//initally the balance should be 0
assert.equal(numberTokens, 0, "The record of tokens is flawed and can't be trusted");
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//after the creation of the test bird, the balance should be 1
numberTokens = await testBirdiesInstance.balanceOf(accounts[0]);
assert.equal(numberTokens, 1, "The record of tokens is flawed and can't be trusted");
});
});
describe("totalSupply()", () =>{
it("should record the total supply of tokens", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//the total supply should be Bird0 and the test bird created here.
var supply = await testBirdiesInstance.totalSupply();
assert.equal(supply, 2, "The record of total supply is flawed and can't be trusted");
});
});
describe("name()", () =>{
it("should return the name of the native token 'CryptoBird'", async () => {
var testName = await testBirdiesInstance.name();
assert.equal(testName, "CryptoBird", "Token wasn't constructed correctly");
});
});
describe("symbol()", () =>{
it("should return the ticker symbol 'CBX'", async () => {
var testSymbol = await testBirdiesInstance.symbol();
assert.equal(testSymbol, "CBX", "Symbol wasn't constructed correctly");
});
});
describe("ownerOf()", () =>{
it("should return the correct owner of the token", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var testOwner = await testBirdiesInstance.ownerOf(1);
assert.strictEqual(testOwner, accounts[0], "The owner of the token is incorrect");
});
it("should revert, if the ID does not exist", async () => {
await truffleAssert.reverts(testBirdiesInstance.ownerOf(5));
});
});
describe("transfer()", () =>{
it("should not allow transfer to the burn address", async () => {
const zeroAddress = '0x0000000000000000000000000000000000000000';
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await truffleAssert.reverts(testBirdiesInstance.transfer(zeroAddress, 0));
});
it("should not allow transfer to the contract address", async () => {
const contractAddress = await testBirdiesInstance.address;
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await truffleAssert.reverts(testBirdiesInstance.transfer(contractAddress, 0));
});
it("should emit a transfer event when a transfer was successful", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var testTransfer = await testBirdiesInstance.transfer(accounts[1], 1);
truffleAssert.eventEmitted(testTransfer, 'Transfer', (ev) => {
return ev.from == accounts[0] && ev.to == accounts[1] && ev.tokenId == 1;
}, "Transfer event should have been emitted with correct parameters");
});
it("should revert if msg.sender does not own the bird", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await truffleAssert.reverts(testBirdiesInstance.transfer(accounts[1], 1, { from: accounts[2] }));
});
it("should check balances and transfer a token from one account to another", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//sender initially has one bird
var numberTokensSender = await testBirdiesInstance.balanceOf(accounts[0]);
assert.equal(numberTokensSender, 1, "Token balance is incorrect");
//recipient initially has no bird
var numberTokensRecipient = await testBirdiesInstance.balanceOf(accounts[1]);
assert.equal(numberTokensRecipient, 0, "Token balance is incorrect");
await testBirdiesInstance.transfer(accounts[1], 1);
//sender has no more birds
numberTokensSender = await testBirdiesInstance.balanceOf(accounts[0]);
assert.equal(numberTokensSender, 0, "Token balance is incorrect");
//recipient now has one bird
numberTokensRecipient = await testBirdiesInstance.balanceOf(accounts[1]);
assert.equal(numberTokensRecipient, 1, "Token balance is incorrect");
});
});
describe("approve()", () =>{
it("should set operator approval for one bird and emit the correct event", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var marketAddress = await testMarketInstance.address;
var approval = await testBirdiesInstance.approve(marketAddress, 1);
truffleAssert.eventEmitted(approval, 'Approval', (ev) => {
return ev.owner == accounts[0] && ev.approved == marketAddress && ev.tokenId == 1;
}, "Event was NOT emitted with correct parameters");
});
it("should revert, if msg.sender is not the owner", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var marketAddress = await testMarketInstance.address;
await truffleAssert.reverts(testBirdiesInstance.approve(marketAddress, 1, { from: accounts[2] }));
});
});
describe("setApprovalForAll()", () =>{
it("should set operator approval for all birds of an owner and emit the correct event", async () => {
//create two birds
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[0]);
var marketAddress = await testMarketInstance.address;
//give market contract operator approval for both of them
var approvalForAll = await testBirdiesInstance.setApprovalForAll(marketAddress, true);
//ensure correct event is emitted
truffleAssert.eventEmitted(approvalForAll, 'ApprovalForAll', (ev) => {
return ev.owner == accounts[0] && ev.operator == marketAddress && ev.approved == true;
}, "Event was NOT emitted with correct parameters");
});
it("should remove operator approval for all birds of an owner and emit the correct event", async () => {
//create two birds
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[0]);
var marketAddress = await testMarketInstance.address;
//give market contract operator approval for both of them
var approvalForAll = await testBirdiesInstance.setApprovalForAll(marketAddress, true);
//remove market contract operator approval for both of them
var approvalForAll = await testBirdiesInstance.setApprovalForAll(marketAddress, false);
//check for the correct event
truffleAssert.eventEmitted(approvalForAll, 'ApprovalForAll', (ev) => {
return ev.owner == accounts[0] && ev.operator == marketAddress && ev.approved == false;
}, "Event was NOT emitted with correct parameters");
});
it("should allow more than one operator approval for all birds of an owner and emit the correct events",
async () => {
//create two test birds
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[0]);
var marketAddress = await testMarketInstance.address;
//give market contract operator approval
var approvalForMarketContract = await testBirdiesInstance.setApprovalForAll(marketAddress, true);
//check for correct event
truffleAssert.eventEmitted(approvalForMarketContract, 'ApprovalForAll', (ev) => {
return ev.owner == accounts[0] && ev.operator == marketAddress && ev.approved == true;
}, "Event was NOT emitted with correct parameters");
//additionally give accounts[1] the same approval
var approvalForAddress = await testBirdiesInstance.setApprovalForAll(accounts[1], true);
//this should work and emit the correct event
truffleAssert.eventEmitted(approvalForAddress, 'ApprovalForAll', (ev) => {
return ev.owner == accounts[0] && ev.operator == accounts[1] && ev.approved == true;
}, "Event was NOT emitted with correct parameters");
});
});
describe("getApproved()", () =>{
it("should return the approved operator for a bird", async () => {
//create test bird
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var marketAddress = await testMarketInstance.address;
//give market contract operator approval for this one bird
await testBirdiesInstance.approve(marketAddress, 1);
//return address of operator for bird 1
var approvedOperator = await testBirdiesInstance.getApproved(1);
//the approved operator should be the market contract
assert.strictEqual(approvedOperator, marketAddress, "The operator was NOT returned correctly");
});
});
describe("isApprovedForAll()", () =>{
it("should return approved status for an operator", async () => {
//create two test birds
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[0]);
var marketAddress = await testMarketInstance.address;
//set operator approval for the market contract for all birds
await testBirdiesInstance.setApprovalForAll(marketAddress, true);
//query for who has operator approval for all of accounts[0]'s birds
var hasApprovalForAll = await testBirdiesInstance.isApprovedForAll(accounts[0], marketAddress);
assert.strictEqual(hasApprovalForAll, true, "The status was NOT returned correctly");
});
});
describe("safeTransferFrom()", () =>{
it("should pass, if msg.sender is owner", async () => {
//create one test bird
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//the owner of the bird intiates the safe transfer successfully
await truffleAssert.passes(testBirdiesInstance.safeTransferFrom(
accounts[0], accounts[1], 1, { from: accounts[0]}));
});
it("should pass, if msg.sender has approval for this token", async () => {
//create test bird
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//set individual operator approval for this one bird for accounts[2]
await testBirdiesInstance.approve(accounts[2], 1);
//the operator should now be able to execute the safe transfer
await truffleAssert.passes(testBirdiesInstance.safeTransferFrom(
accounts[0], accounts[1], 1, {from: accounts[2]}));
});
it("should pass, if msg.sender has operator approval for this owner", async () => {
//create two test birds
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await testBirdiesInstance.testCreateBird(202, accounts[0]);
//give accounts[2] general operator approval for all birds
await testBirdiesInstance.setApprovalForAll(accounts[2], true);
//the operator should be able to transfer that bird
await truffleAssert.passes(testBirdiesInstance.safeTransferFrom(
accounts[0], accounts[1], 1, {from: accounts[2]}));
});
it("should revert, if msg.sender is not owner or operator for a token", async () => {
//create one test bird
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//let an address call the safe transfer function that is not owner, nor operator
//this operations must fail
await truffleAssert.reverts(testBirdiesInstance.safeTransferFrom(
accounts[0], accounts[1], 1, { from: accounts[2]}));
});
it("should revert, if msg.sender is has no approval for this token", async () => {
//create one test bird
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//make accounts[1] operator for this bird
await testBirdiesInstance.approve(accounts[1], 1);
//the transfer is expected to fail initiated by someone else
await truffleAssert.reverts(testBirdiesInstance.safeTransferFrom(
accounts[0], accounts[1], 1, { from: accounts[2]}));
});
it("should revert, if msg.sender has no operator rights for this owner", async () => {
//create one test bird
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//make accounts[2] general operator accounts[0]
await testBirdiesInstance.setApprovalForAll(accounts[2], true);
//the transfer is expected to fail initiated by someone else
await truffleAssert.reverts(testBirdiesInstance.safeTransferFrom(
accounts[0], accounts[1], 1, { from: accounts[1]}));
});
it("should revert, if from address is not the owner of the bird", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await truffleAssert.reverts(testBirdiesInstance.safeTransferFrom(
accounts[2], accounts[1], 1, {from: accounts[0]}));
});
it("should not allow a transfer to the zero address", async () => {
const zeroAddress = '0x0000000000000000000000000000000000000000';
await testBirdiesInstance.testCreateBird(101, accounts[0]);
await truffleAssert.reverts(testBirdiesInstance.safeTransferFrom(
accounts[0], zeroAddress, 1));
});
it("should not allow a transfer of a token that doesn't exist", async () => {
await truffleAssert.reverts(testBirdiesInstance.safeTransferFrom(
accounts[0], accounts[1], 1));
});
it("should revert, if the receiving contract does not support ERC721", async () => {
//this test contract does not support the ERC721 standard
testMarketInstance = await TestMarket.new(testBirdiesInstance.address);
//create one test bird
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//trying to safe transfer that bird to a contract without ERC721 support must fail
await truffleAssert.reverts(testBirdiesInstance.safeTransferFrom(
accounts[0], testMarketInstance.address, 1));
});
it("should execute, if the receiving contract supports ERC721", async () => {
//create instance of ERC721 compliant test contract
testErcInstance = await TestErc.new();
//create test bird
await testBirdiesInstance.testCreateBird(101, accounts[0]);
//safe transfer to the ERC721 test contract should work
await truffleAssert.passes(testBirdiesInstance.safeTransferFrom(
accounts[0], testErcInstance.address, 1));
});
});
describe("_mixDna()", () =>{
it("should create a deterministic DNA string", async () => {
//the function should create a DNA string that is exactly defined by
//four random parameters.
var newDna = await testBirdiesInstance.testMixDna(
"11223344556677889", "98877665544332211", "100", "0", "0", "63");
//using the four parameters the new DNA can be predicted as 63223365546632211
assert.equal(newDna, "63223365546632211", "DNA string was created incorrectly");
});
});
})
contract("MarketPlace", (accounts) => {
async function createBirdAndSetApproval(){
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var marketAddress = await testMarketInstance.address;
await testBirdiesInstance.setApprovalForAll(marketAddress, true, { from: accounts[0] });
}
it("should deploy correctly", async () => {
testBirdiesInstance = await TestBirdies.new("CryptoBird", "CBX");
await truffleAssert.passes(Marketcontract.deployed(testBirdiesInstance.address));
});
beforeEach(async () => {
testBirdiesInstance = await TestBirdies.new("CryptoBird", "CBX");
testMarketInstance = await TestMarket.new(testBirdiesInstance.address);
});
describe("setContract()", () =>{
it("should set the Birdcontract by address", async () => {
await truffleAssert.passes(testMarketInstance.setContract(testBirdiesInstance.address));
});
it("should revert, if not called by owner", async () => {
await truffleAssert.reverts(testMarketInstance.setContract(
testBirdiesInstance.address, { from: accounts[1] }));
});
});
describe("pause()", () =>{
it("should execute for the contract owner", async () => {
await truffleAssert.passes(testMarketInstance.pause( { from: accounts[0] }));
});
it("should not execute for other users", async () => {
await truffleAssert.reverts(testMarketInstance.pause( { from: accounts[1] }));
});
it("should prevent the execution of buyBird()", async () => {
await createBirdAndSetApproval();
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
await testMarketInstance.pause( { from: accounts[0] });
//it should not be possible to buy this bird after pause() is called
await truffleAssert.reverts(testMarketInstance.buyBird(1, { from: accounts[1], value: 1 }));
});
it("should prevent the execution of withdrawFunds()", async () => {
await createBirdAndSetApproval();
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
await testMarketInstance.buyBird(1, { from: accounts[1], value: 1 });
await testMarketInstance.pause( { from: accounts[0] });
//it should not be possible to withdraw that 1 ETH after pause() is called
await truffleAssert.reverts(testMarketInstance.withdrawFunds({ from: accounts[0] }));
});
it("should still allow the execution of getAllTokensOnSale()", async () => {
await createBirdAndSetApproval();
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
await testMarketInstance.pause( { from: accounts[0] });
await truffleAssert.passes(testMarketInstance.getAllTokensOnSale());
});
it("should still allow the execution of getBalance()", async () => {
await testMarketInstance.testSetBalance(5);
await testMarketInstance.pause( { from: accounts[0] });
await truffleAssert.passes(testMarketInstance.getBalance());
});
});
describe("resume()", () =>{
it("should execute for the contract owner", async () => {
//first pause the contract through test function
await testMarketInstance.testSetPause( { from: accounts[0] });
//owner should be able to resume
await truffleAssert.passes(testMarketInstance.resume( { from: accounts[0] }));
});
it("should not execute for other users", async () => {
//first pause the contract through test function
await testMarketInstance.testSetPause( { from: accounts[0] });
//another user shouldn't be able to resume contract execution
await truffleAssert.reverts(testMarketInstance.resume( { from: accounts[1] }));
});
it("should resume the execution of buyBird()", async () => {
await createBirdAndSetApproval();
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
// pause the contract through test function
await testMarketInstance.testSetPause( { from: accounts[0] });
//resume execution
await testMarketInstance.resume( { from: accounts[0] });
//buying the bird should work fine and emit the correct event
var purchase = await testMarketInstance.buyBird(1, { from: accounts[1], value: 1 });
truffleAssert.eventEmitted(purchase, 'MarketTransaction', (ev) => {
return ev.TxType == "Bird successfully purchased" && ev.owner == accounts[1] && ev.tokenId == 1;
}, "Event was NOT emitted with correct parameters");
});
it("should resume the execution of withdrawFunds()", async () => {
await createBirdAndSetApproval();
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//first pause the contract through test function
await testMarketInstance.testSetPause( { from: accounts[0] });
//it should not be possible to buy the bird at this point
await truffleAssert.reverts(testMarketInstance.buyBird(1, { from: accounts[1], value: 1 }));
//resume execution
await testMarketInstance.resume( { from: accounts[0] });
//purchase of the bird should now execute
await testMarketInstance.buyBird(1, { from: accounts[1], value: 1 });
//seller should be able to withdraw funds
var fundsReceived = await testMarketInstance.withdrawFunds({ from: accounts[0] });
truffleAssert.eventEmitted(fundsReceived, 'MonetaryTransaction', (ev) => {
return ev.message == "Funds successfully received" && ev.recipient == accounts[0] && ev.amount == 1;
}, "Event was NOT emitted with correct parameters");
});
});
describe("getOffer()", () =>{
it("should revert, if there is no active offer", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[1]);
//give market contract operator approval for all birds of accounts[1]
var marketAddress = await testMarketInstance.address;
await testBirdiesInstance.setApprovalForAll(marketAddress, true, { from: accounts[1] });
//as no offer was set, this assert statement is expected to revert
await truffleAssert.reverts(testMarketInstance.getOffer(1));
});
it("should return seller, price, index, tokenId, and status of an offer correctly",
async () => {
await createBirdAndSetApproval();
//set offer of 1 ETH for bird 1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
var offer = await testMarketInstance.getOffer(1);
//check offer parameters
assert.strictEqual(offer.seller, accounts[0], "Seller is wrong");
assert.equal(offer.price, 1, "Price is wrong");
assert.equal(offer.index, 0, "Index is wrong");
assert.equal(offer.tokenId, 1, "TokenId is wrong");
assert.strictEqual(offer.active, true, "Offer status is wrong");
});
});
describe("getAllTokensOnSale()", () =>{
it("should return an array with the correct length and ids of offers",
async () => {
//create 4 test birds for 3 different owners
await testBirdiesInstance.testCreateBird(706, accounts[0]); //Bird 1
await testBirdiesInstance.testCreateBird(707, accounts[0]); //Bird 2
await testBirdiesInstance.testCreateBird(101, accounts[1]); //Bird 3
await testBirdiesInstance.testCreateBird(202, accounts[2]); //Bird 4
var marketAddress = await testMarketInstance.address;
//all 3 owners give the market contract operator rights
await testBirdiesInstance.setApprovalForAll(marketAddress, true, { from: accounts[0] });
await testBirdiesInstance.setApprovalForAll(marketAddress, true, { from: accounts[1] });
await testBirdiesInstance.setApprovalForAll(marketAddress, true, { from: accounts[2] });
//create an offer for each of the 4 birds
await testMarketInstance.setOffer(10, 1);//10 ETH for Bird 1
await testMarketInstance.setOffer(100, 2);//100 ETH for Bird 1
await testMarketInstance.setOffer(1, 3, { from: accounts[1] });//1 ETH for Bird3
await testMarketInstance.setOffer(2, 4, { from: accounts[2] });//2 ETH for Bird4
//remove offer for Bird 1
await testMarketInstance.removeOffer(1);
//call for the array with the offers
var arrayOffers = await testMarketInstance.getAllTokensOnSale();
//make sure the array contains 3 offers
assert.equal(arrayOffers.length, 3, "The offer array does NOT have the correct length");
//make sure each of the 3 offers is assigned to the correct bird
//[0] is Bird2; [1] is Bird 3; [2] is Bird4
assert.equal(arrayOffers[0], 2, "Bird2 offer status is wrong");
assert.equal(arrayOffers[1], 3, "Bird3 offer status is wrong");
assert.equal(arrayOffers[2], 4, "Bird4 offer status is wrong");
});
});
describe("setOffer()", () =>{
it("should set an offer with a price and tokenId correctly and emit the correct event",
async () => {
await createBirdAndSetApproval();
//set offer for 1ETH for Bird1
var offer = await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//check for correct event parameters
await truffleAssert.eventEmitted(offer, 'MarketTransaction', (ev) => {
return ev.TxType == "Offer created" && ev.owner == accounts[0] && ev.tokenId == 1;
}, "Event was NOT emitted with correct parameters");
//return offer
var offer = await testMarketInstance.getOffer(1);
//make sure all details of the offer are correct
assert.strictEqual(offer.seller, accounts[0], "Seller is wrong");
assert.equal(offer.price, 1, "Price is wrong");
assert.equal(offer.index, 0, "Index is wrong");
assert.equal(offer.tokenId, 1, "TokenId is wrong");
assert.strictEqual(offer.active, true, "Offer status is wrong");
});
it("should only allow the owner to set an offer", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var marketAddress = await testMarketInstance.address;
//give market contract operator approval
await testBirdiesInstance.setApprovalForAll(marketAddress, true);
//accounts[1] should not be able to set an offer
await truffleAssert.reverts(testMarketInstance.setOffer(1, 1, { from: accounts[1] }));
});
it("should not allow to set a new offer when another already exists", async () => {
await createBirdAndSetApproval();
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//the second attempt to set an offer for Bird 1 should fail
await truffleAssert.reverts(testMarketInstance.setOffer(5, 1));//5 ETH for Bird1
});
it("should not allow to set an offer, if the Marketcontract is not an approved operator",
async () => {
await testBirdiesInstance.testCreateBird(101, accounts[0]);
var marketAddress = await testMarketInstance.address;
//make sure the market contract is NOT an approved operator
await testBirdiesInstance.setApprovalForAll(marketAddress, false);
//setting the offer should now fail
await truffleAssert.reverts(testMarketInstance.setOffer(5, 1));//5 ETH for Bird1
});
it("should emit a transaction event with correct parameters", async () => {
await createBirdAndSetApproval();
//set offer for 1ETH for Bird1
var offer = await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//a correctly created event should be emitted
truffleAssert.eventEmitted(offer, 'MarketTransaction', (ev) => {
return ev.TxType == "Offer created" && ev.owner == accounts[0] && ev.tokenId == 1;
}, "Event was NOT emitted with correct parameters");
});
});
describe("removeOffer()", () =>{
it("should only allow the owner to remove an offer", async () => {
await createBirdAndSetApproval();
//owner sets offer
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//someone else trying to remove the offer should fail
await truffleAssert.reverts(testMarketInstance.removeOffer(1, { from: accounts[1] }));
});
it("should set status in the offers array to false", async () => {
await createBirdAndSetApproval();
//create an offer of 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//this should set the status within the offers array to false
await testMarketInstance.removeOffer(1, { from: accounts[0] });
//get all 5 parameters from the offers array
var offer = await testMarketInstance.getOfferFromArray(0);
//owner, price, index, and tokenID are still in the array, but status must be false
assert.strictEqual(offer.seller, accounts[0], "The offer was not removed correctly");
assert.equal(offer.price, 1, "The offer was not removed correctly");
assert.equal(offer.index, 0, "The offer was not removed correctly");
assert.equal(offer.tokenId, 1, "The offer was not removed correctly");
assert.strictEqual(offer.active, false, "The offer was not removed correctly");
});
it("should delete the entry in the offer mapping", async () => {
await createBirdAndSetApproval();
//create an offer of 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//delete the entry in the mapping
await testMarketInstance.removeOffer(1, { from: accounts[0] });
//trying to get the offer from the mapping
var offer = await testMarketInstance.getOfferFromMapping(1);
//none of the 5 parameters should reflect the original offer
assert.notStrictEqual(offer.seller, accounts[0], "The offer was not removed correctly");
assert.notEqual(offer.price, 1, "The offer was not removed correctly");
assert.equal(offer.index, 0, "The offer was not removed correctly");
assert.notEqual(offer.tokenId, 1, "The offer was not removed correctly");
assert.notStrictEqual(offer.active, true, "The offer was not removed correctly");
});
it("should emit a MarketTransaction with correct parameters", async () => {
await createBirdAndSetApproval();
//create an offer of 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//offer removed
var removal = await testMarketInstance.removeOffer(1, { from: accounts[0] });
//check for removal event
truffleAssert.eventEmitted(removal, 'MarketTransaction', (ev) => {
return ev.TxType == "Offer removed" && ev.owner == accounts[0] && ev.tokenId == 1;
}, "Event was NOT emitted with correct parameters");
});
});
describe("buyBird()", () =>{
it("should only work, if there is an active offer", async () => {
await testBirdiesInstance.testCreateBird(101, accounts[1]);
var marketAddress = await testMarketInstance.address;
//after the Bird1 is created, the market contract receives operator rights
await testBirdiesInstance.setApprovalForAll(marketAddress, true, { from: accounts[1] });
//since no offer was created, the purchase of the bird must fail
await truffleAssert.reverts(testMarketInstance.buyBird(1, { from: accounts[2], value: 1 }));
});
it("should only work, if the value offered is equal to the asking price", async () => {
await createBirdAndSetApproval();
//set offer for 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//Trying to buy the bird for 2ETH must fail
await truffleAssert.reverts(testMarketInstance.buyBird(1, { from: accounts[1], value: 2 }));
});
it("should transfer ownership correctly", async () => {
await createBirdAndSetApproval();
//set offer for 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//accounts[1] buys Bird1 from accounts[0]
await testMarketInstance.buyBird(1, { from: accounts[1], value: 1 });
//get owner
var owner = await testBirdiesInstance.ownerOf(1);
//make sure the new owner is accounts[1]
assert.strictEqual(owner, accounts[1], "Ownership was not updated correctly");
});
it("should set the status in the offers array to false", async () => {
await createBirdAndSetApproval();
//set offer for 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//accounts[1] buys Bird1 for 1ETH from accounts[0]
await testMarketInstance.buyBird(1, { from: accounts[1], value: 1 });
//check array for offer parameters
var offer = await testMarketInstance.getOfferFromArray(0);
//make sure seller, price, index, and tokenId are still correct
//while offer status must now be false
assert.strictEqual(offer.seller, accounts[0], "The offer was not removed correctly");
assert.equal(offer.price, 1, "The offer was not removed correctly");
assert.equal(offer.index, 0, "The offer was not removed correctly");
assert.equal(offer.tokenId, 1, "The offer was not removed correctly");
assert.strictEqual(offer.active, false, "The offer was not removed correctly");
});
it("should delete the entry in the offer mapping", async () => {
await createBirdAndSetApproval();
//set offer for 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//accounts[1] buys Bird1 for 1ETH from accounts[0]
await testMarketInstance.buyBird(1, { from: accounts[1], value: 1 });
//return parameters from mapping
var offer = await testMarketInstance.getOfferFromMapping(1);
//all 5 parameters of the mapping are deleted and should not reflect the original offer
assert.notStrictEqual(offer.seller, accounts[0], "The offer was not removed correctly");
assert.notStrictEqual(offer.price, 1, "The offer was not removed correctly");
assert.notStrictEqual(offer.index, 0, "The offer was not removed correctly");
assert.notStrictEqual(offer.tokenId, 1, "The offer was not removed correctly");
assert.notStrictEqual(offer.active, true, "The offer was not removed correctly");
});
it("should transfer funds to the _fundsToBeCollected mapping", async () => {
var buyer = accounts[1];
await createBirdAndSetApproval();
//convert offer of 1ETH to BN
var price = web3.utils.toBN(1);
//convert offer to Wei
var inWei = web3.utils.toWei(price, "ether");
//set offer for 1ETH for Bird1
await testMarketInstance.setOffer(inWei, 1);
//check buyer's ETH balance before the purchase
var buyerStart = parseInt(await web3.eth.getBalance(buyer));
//purchase Bird1 for 1ETH
await testMarketInstance.buyBird(1, { from: buyer, value: inWei });
//check buyer's ETH balance after the purchase
var buyerEnd = parseInt(await web3.eth.getBalance(buyer));
//check the balance in the funds mapping
var fundsInMapping = parseInt(await testMarketInstance.getBalanceOfMapping(accounts[0]));
//convert the purchase price back into integer for comparison reasons
weiInt = parseInt(inWei);
//check that the same amount was added to the funds mapping that the buyer sent
assert.strictEqual(fundsInMapping, weiInt,
"Funds were not correctly added to the mapping");
//check that the buyer's balance is at most
//what he had before less the purchase price.
//it should be a little less, because of gas fees.
assert.isAtMost(buyerEnd, buyerStart - weiInt,
"Funds were not correctly subtracted from the buyer account");
//check that the buyer's balance is at least
//what he had before less twice the purchase price.
//this ensures that buyer wasn't double charged
assert.isAtLeast(buyerEnd, buyerStart - 2 * weiInt,
"Funds were not correctly subtracted from the buyer account");
});
it("should emit a MarketTransaction event with correct parameters", async () => {
await createBirdAndSetApproval();
//set offer of 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//purchase Bird1
var purchase = await testMarketInstance.buyBird(1, { from: accounts[1], value: 1 });
//check for all event parameters to be emitted correctly
truffleAssert.eventEmitted(purchase, 'MarketTransaction', (ev) => {
return ev.TxType == "Bird successfully purchased" && ev.owner == accounts[1] && ev.tokenId == 1;
}, "Event was NOT emitted with correct parameters");
});
});
describe("getBalance()", () =>{
it("should return the balance of msg.sender", async () => {
var amountToSet = 5;
//a test function was created to set test balances in the funds mapping
//create a balance of 5ETH for msg.sender
await testMarketInstance.testSetBalance(amountToSet);
//check mapping for balance
var amountToGet = await testMarketInstance.getBalance();
//ensure the mapping contains the same value as was set in the test
assert.equal(amountToSet, amountToGet,
"Balance was not returned correctly");
});
});
describe("withdrawFunds()", () =>{
it("should revert, if no funds are available to withdraw", async () => {
await truffleAssert.reverts(testMarketInstance.withdrawFunds({ from: accounts[0] }));
});
it("should revert, if the contract owner tries to withdraw user funds", async () => {
await testMarketInstance.testSetBalance(5, { from: accounts[2] });
await truffleAssert.reverts(testMarketInstance.withdrawFunds({ from: accounts[0] }));
});
it("should withdraw funds correctly", async () => {
var seller = accounts[1];
var buyer = accounts[2];
//create test bird for seller
await testBirdiesInstance.testCreateBird(101, seller);
var marketAddress = await testMarketInstance.address;
await testBirdiesInstance.setApprovalForAll(marketAddress, true, { from: seller });
//conversions of offer of 1ETH
var price = web3.utils.toBN(1);
var inWei = web3.utils.toWei(price, "ether");
//set offer for 1ETH for Bird1
await testMarketInstance.setOffer(inWei, 1, { from: seller });
//check buyer's ETH balance before purchase
var buyerStart = parseInt(await web3.eth.getBalance(buyer));
//correct purchase of Bird1
await testMarketInstance.buyBird(1, { from: buyer, value: inWei });
//check buyer's ETH balance after purchase
var buyerEnd = parseInt(await web3.eth.getBalance(buyer));
//check seller's ETH balance before withdrawal
var sellerStart = parseInt(await web3.eth.getBalance(seller));
//check funds in mapping
var fundsInMapping = await testMarketInstance.getBalanceOfMapping(seller);
assert.equal(fundsInMapping, parseInt(inWei),
"Funds were not correctly added to the mapping");
//seller should be able to withdraw 1ETH
await testMarketInstance.withdrawFunds({ from: seller });
//check seller's ETH balance after withdrawal
var sellerEnd = parseInt(await web3.eth.getBalance(seller));
//check mapping again
fundsInMapping = await testMarketInstance.getBalanceOfMapping(seller);
//convert back to integer
var weiInt = parseInt(inWei);
//make sure funds were removed from mapping
assert.equal(fundsInMapping, 0,
"Funds were not correctly removed from the mapping");
//make sure seller didn't receive any more funds than the proceedings from the purchase
assert.isAtMost(sellerEnd - sellerStart, weiInt,
"Funds were not correctly added to the seller account");
//check that the amount was correctly subtracted from buyer
//considering gas, at least purchase price and gas should have been removed
assert.isAtLeast(buyerStart - buyerEnd, weiInt,
"Funds were not correctly subtracted from the buyer account");
//check that the amount was correctly subtracted from buyer
//considering gas, at most double the purchase price should have been removed
//this ensures that there is no double spending
assert.isAtMost(buyerStart - buyerEnd, 2 * weiInt,
"Funds were not correctly subtracted from the buyer account");
});
it("should emit a MonetaryTransaction event with correct parameters", async () => {
await createBirdAndSetApproval();
//set offer for 1ETH for Bird1
await testMarketInstance.setOffer(1, 1, { from: accounts[0] });
//purchase of Bird1
await testMarketInstance.buyBird(1, { from: accounts[1], value: 1 });
//seller withdraws funds
var fundsReceived = await testMarketInstance.withdrawFunds({ from: accounts[0] });
//ensure correct event for withdrawal is emitted
truffleAssert.eventEmitted(fundsReceived, 'MonetaryTransaction', (ev) => {
return ev.message == "Funds successfully received" && ev.recipient == accounts[0] && ev.amount == 1;
}, "Event was NOT emitted with correct parameters");
});
it("should allow seller to access correct funds, if birds were sold to different buyers",
async () => {
var seller = accounts[1];
var buyer1 = accounts[2];
var buyer2 = accounts[3];
//create 2 test birds and give market contract approval as operator
await testBirdiesInstance.testCreateBird(101, seller);
await testBirdiesInstance.testCreateBird(202, seller);
var marketAddress = await testMarketInstance.address;
await testBirdiesInstance.setApprovalForAll(marketAddress, true, { from: seller });
//conversions
var price1 = web3.utils.toBN(5);
var inWei1 = web3.utils.toWei(price1, "ether");
var price2 = web3.utils.toBN(3);
var inWei2 = web3.utils.toWei(price2, "ether");
//create 2 offers
await testMarketInstance.setOffer(inWei1, 1, { from: seller }); //5 ETH for Bird1
await testMarketInstance.setOffer(inWei2, 2, { from: seller }); //3 ETH for Bird2
//purchase of the birds by two different buyers
await testMarketInstance.buyBird(1, { from: buyer1, value: inWei1 });
await testMarketInstance.buyBird(2, { from: buyer2, value: inWei2 });
//check seller's ETH balance before withdrawal
var sellerStart = parseInt(await web3.eth.getBalance(seller));
await testMarketInstance.withdrawFunds({ from: seller });
//check seller's ETH balance after withdrawal
var sellerEnd = parseInt(await web3.eth.getBalance(seller));
var weiInt = parseInt(inWei1 + inWei2);
//make sure seller didn't receive any more funds than the proceedings from the purchase
assert.isAtMost(sellerEnd - sellerStart, weiInt,
"Funds were not correctly added to the seller account");
});
});
})