[Ethereum] テストプロジェクト FundraiserFactory

fundraiser_factory_test.js

const FundraiserFactoryContract = artifacts.require("FundraiserFactory");
const FundraiserContract = artifacts.require("Fundraiser");

contract("FundraiserFactory: deployment", ()=> {
	it("has been deployed", async() => {
		const fundraiserFactory = FundraiserFactoryContract.deployed();
		assert(fundraiserFactory, "fundraiser factory was not deployed");
	})
});

contract("FundraiserFactory: createFundraiser", (accounts) => {
	let fundraiserFactory;
	const name = "Beneficiary Name";
	const url = "beneficiaryname.org";
	const imageURL = "https://placekitten.com/600/350"
	const description = "Beneficiary Description"
	const beneficiary = accounts[1];

	it("increments the fundraisersCount", async() => {
		fundraiserFactory = await FundraiserFactoryContract.deployed();
		const currentFundraisersCount = fundraiserFactory.fundraisersCount();
		await fundraiserFactory.createFundraiser(
			name,
			url,
			imageURL,
			description,
			beneficiary
		);
		const newFundraisersCount = await fundraiserFactory.fundraisersCount();

		// assert.equal(
		// 	newFundraisersCount - currentFundraisersCount,
		// 	1,
		// 	"should increment by 1"
		// );
	});

	it("emits the FundraiserCreated event", async() => {
		fundraiserFactory = await FundraiserFactoryContract.deployed();
		const tx = await fundraiserFactory.createFundraiser(
			name,
			url,
			imageURL,
			description,
			beneficiary
		);
		const expectedEvent = "FundraiserCreated";
		const actualEvent = tx.logs[0].event;

		assert.equal(actualEvent, expectedEvent, "event should match");
	})

});

contract("FundraiserFactory: fundraisers", (accounts) => {
	async function createFundraiserFactory(fundraiserCount, accounts){
		const factory = await FundraiserFactoryContract.new();
		await addFundraisers(factory, fundraiserCount, accounts);
		return factory;
	}

	async function addFundraisers(factory, count, accounts){
		const name = "Beneficiary";
		const lowerCaseName = name.toLowerCase();
		const beneficiary = accounts[1];

		for (let i = 0; i < count; i++){
			await factory.createFundraiser(
				`${name} ${i}`,
				`${lowerCaseName}${i}.com`,
				`${lowerCaseName}${i}.png`,
				`Description for ${name} ${i}`,
				beneficiary
			);
		}
	}

	describe("when fundraisers collection is empty", () => {
		it("returns an empty collection", async() => {
			const factory = await createFundraiserFactory(0, accounts);
			const fundraisers = await factory.fundraisers(10, 0);
			assert.equal(
				fundraisers.length,
				0,
				"collection should be empty"
			);
		});
	});

	describe("varying limits", async() => {
		let factory;
		beforeEach(async() => {
			factory = await createFundraiserFactory(30, accounts);
		});

		it("returns 10 results when limit requested is 10", async()=> {
			const fundraisers = await factory.fundraisers(10, 0);
			assert.equal(
				fundraisers.length,
				10,
				"results size should be 10"
			);
		});
		xit("returns 20 results when limit requested is 20", async() => {
			const fundraisers = await factory.fundraisers(20, 0);
			assert.equal(
				fundraisers.length,
				20,
				"results size should be 20"
			);
		});
		xit("returns 20 results when limit requested is 30", async() => {
			const fundraisers = await factory.fundraisers(30, 0);
			assert.equal(
				fundraisers.length,
				20,
				"results size should be 20"
			);
		});

	});

	describe("varying offset", () => {
		let factory;
		beforeEach(async() => {
			factory = await createFundraiserFactory(10, accounts);
		});

		it("contains the fundraiser with the appropriate offset", async() => {
			const fundraisers = await factory.fundraisers(1, 0);
			const fundraiser = await FundraiserContract.at(fundraisers[0]);
			const name = await fundraiser.name();
			assert.ok(await name.includes(0), `${name} did not include the offset`);
		});

		it("contains the fundraiser with the appropriate offset", async() => {
			const fundraisers = await factory.fundraisers(1, 7);
			const fundraiser = await FundraiserContract.at(fundraisers[0]);
			const name = await fundraiser.name();
			assert.ok(await name.includes(7), `${name} did not include the offset`);
		})
	});

	describe("boundary conditions", ()=> {
		let factory;
		beforeEach(async() => {
			factory = await createFundraiserFactory(10, accounts);
		});

		it("raises out of bounds error", async () => {
			try {
				await factory.fundraisers(1, 11);
				assert.fail("error was not raised")
			} catch(err) {
				const expected = "offset out of bounds";
				assert.ok(err.message.includes(expected), `${err.message}`);
			}
		});

		it("adjusts return size to prevent out of bounds error", async() => {
			try {
				const fundraisers = await factory.fundraisers(10, 5);
				assert.equal(
					fundraisers.length,
					5,
					"collection adjusted"
				);
			} catch(err) {
				assert.fail("limit and offset exceeded bounds");
			}
		})
	})
})

contracts

pragma solidity >0.4.23 <=0.8.0;

import "./Fundraiser.sol";

contract FundraiserFactory {
	uint256 constant maxLimit = 20;
	Fundraiser[] private _fundraisers;

	event FundraiserCreated(Fundraiser indexed fundraiser, address indexed owner);

	function createFundraiser(
		string memory name,
		string memory url,
		string memory imageURL,
		string memory description,
		address payable beneficiary
	)

	public {
		Fundraiser fundraiser = new Fundraiser(
			name,
			url,
			imageURL,
			description,
			beneficiary,
			msg.sender
		);
		_fundraisers.push(fundraiser);
		emit FundraiserCreated(fundraiser, fundraiser.owner());
	}

	function fundraisersCount() public view returns(uint256) {
		return _fundraisers.length;
	}

	function fundraisers(uint256 limit, uint256 offset) public view returns(Fundraiser[] memory coll){

		require(offset <= fundraisersCount(), "offset out of bounds");

		uint256 size = fundraisersCount() - offset;
		size = size < limit ? size : limit;
		size = size < maxLimit ? size : maxLimit;
		coll = new Fundraiser[](size);

		for(uint256 i = 0; i < size; i++){
			coll[i] = _fundraisers[offset + i];
		}

		return coll;
	}
}

関数とその機能を覚えていけば、行けそうな気がして来た