Skip to content

Commit

Permalink
feat: complete the getApplications function
Browse files Browse the repository at this point in the history
  • Loading branch information
Behzad-rabiei committed Jul 19, 2024
1 parent fe83bcc commit b3f7169
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 56 deletions.
26 changes: 19 additions & 7 deletions contracts/ApplicationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,52 @@ contract ApplicationManager is IApplicationManager {
function applicationExists(uint id) internal view returns (bool) {
return applications[id].account != address(0);
}
function getNextApplicationId() external view override returns (uint) {
function getNextApplicationId() external view returns (uint) {
return nextApplicationId;
}

function createApplication(Application memory application) external override {
function createApplication(Application memory application) external {
require(!addressUsed[application.account], "Address already used for another application");
applications[nextApplicationId] = application;
addressUsed[application.account] = true;
emit ApplicationCreated(nextApplicationId, applications[nextApplicationId]);
nextApplicationId++;
}

function updateApplication(uint id, Application memory application) external override {
function updateApplication(uint id, Application memory application) external {
require(applicationExists(id), "Application does not exist");
require(!addressUsed[application.account] || applications[id].account == application.account,
"Address already used for another application");
applications[id] = application;
emit ApplicationUpdated(id, applications[id]);
}

function deleteApplication(uint id) external override {
function deleteApplication(uint id) external {
require(applicationExists(id), "Application does not exist");
addressUsed[applications[id].account] = false;
emit ApplicationDeleted(id, applications[id]);
delete applications[id];
}

function getApplication(uint id) external view override returns (Application memory) {
function getApplication(uint id) external view returns (Application memory) {
require(applicationExists(id), "Application does not exist");
return applications[id];
}

function getApplications(uint start, uint limit) external view override returns (Application[] memory) {
}
function getApplications(uint start, uint limit) external view returns (Application[] memory) {
Application[] memory result = new Application[](limit);
uint count = 0;
uint index = start;
while (count < limit && index < nextApplicationId) {
if (applicationExists(index)) {
result[count] = applications[index];
count++;
}
index++;
}

assembly { mstore(result, count) }

return result;
}
}
172 changes: 123 additions & 49 deletions test/ApplicationManager.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { loadFixture } from "@nomicfoundation/hardhat-toolbox-viem/network-helpers";
import { expect } from "chai";
import hre from "hardhat";
import { type Address, getAddress, parseUnits, } from "viem";
import { generatePrivateKey,privateKeyToAccount } from 'viem/accounts'

import { type Address, getAddress, parseUnits } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";

interface Application {
name: string;
Expand Down Expand Up @@ -33,10 +32,10 @@ describe("ApplicationManager", () => {
}

function generateRandomAddress(): string {
const randomKey = generatePrivateKey();
const account = privateKeyToAccount(randomKey);
return account.address;
}
const randomKey = generatePrivateKey();
const account = privateKeyToAccount(randomKey);
return account.address;
}

describe("Deployment", () => {
it("Should set the nextxApplicationId to 0", async () => {
Expand All @@ -51,7 +50,7 @@ describe("ApplicationManager", () => {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
let contract: any;
let app: Application;
let account= generateRandomAddress() as Address;
const account = generateRandomAddress() as Address;
beforeEach(async () => {
const fixture = await loadFixture(deploy);
contract = fixture.contract;
Expand All @@ -78,12 +77,12 @@ describe("ApplicationManager", () => {
app,
);
});
it("Should revert if the account address is already used", async () => {
await contract.write.createApplication([app]);
await expect(contract.write.createApplication([app])).to.be.rejectedWith(
"Address already used for another application"
);
});
it("Should revert if the account address is already used", async () => {
await contract.write.createApplication([app]);
await expect(contract.write.createApplication([app])).to.be.rejectedWith(
"Address already used for another application",
);
});
it("Should emit the ApplicationCreated event with the new application's details", async () => {
const applicationId = await contract.read.getNextApplicationId();
const txHash = await contract.write.createApplication([app]);
Expand All @@ -105,7 +104,7 @@ describe("ApplicationManager", () => {
let contract: any;
let app: Application;
let applicationId: string;
let account= generateRandomAddress() as Address;
const account = generateRandomAddress() as Address;

beforeEach(async () => {
const fixture = await loadFixture(deploy);
Expand Down Expand Up @@ -137,24 +136,29 @@ describe("ApplicationManager", () => {
it("Should revert if the account address is already used", async () => {
const t1 = await contract.read.getNextApplicationId();
expect(t1).to.equal(parseUnits("1", 0));
await contract.write.createApplication([{
account: generateRandomAddress() as Address,
name: "new app"
}]);

await expect(contract.write.updateApplication([t1, app])).to.be.rejectedWith(
"Address already used for another application"
);
});
it("Should revert if the application does not exist", async () => {
await contract.write.createApplication([
{
account: generateRandomAddress() as Address,
name: "new app",
},
]);

await expect(
contract.write.updateApplication([t1, app]),
).to.be.rejectedWith("Address already used for another application");
});
it("Should revert if the application does not exist", async () => {
const nonExistentId = parseUnits("999", 0);
await expect(contract.write.updateApplication([nonExistentId, app])).to.be.rejectedWith(
"Application does not exist"
);
});
await expect(
contract.write.updateApplication([nonExistentId, app]),
).to.be.rejectedWith("Application does not exist");
});

it("Should emit the ApplicationUpdated event with the updated application's details", async () => {
const txHash = await contract.write.updateApplication([applicationId, app]);
const txHash = await contract.write.updateApplication([
applicationId,
app,
]);
const events = await contract.getEvents.ApplicationUpdated();
expect(events.length).to.be.equal(1);

Expand All @@ -171,7 +175,7 @@ describe("ApplicationManager", () => {
let contract: any;
let app: Application;
let applicationId: string;
let account= privateKeyToAccount(generatePrivateKey()).address
const account = privateKeyToAccount(generatePrivateKey()).address;

beforeEach(async () => {
const fixture = await loadFixture(deploy);
Expand All @@ -192,20 +196,22 @@ describe("ApplicationManager", () => {
});
it("Should delete the application from the applications mapping with the provided applicationId", async () => {
await contract.write.deleteApplication([applicationId]);
await expect(contract.read.getApplication([applicationId])).to.be.rejectedWith("Application does not exist");
await expect(
contract.read.getApplication([applicationId]),
).to.be.rejectedWith("Application does not exist");
});
it("Should revert if the application does not exist", async () => {
it("Should revert if the application does not exist", async () => {
const nonExistentId = parseUnits("999", 0);
await expect(contract.write.deleteApplication([nonExistentId])).to.be.rejectedWith(
"Application does not exist"
);
});
await expect(
contract.write.deleteApplication([nonExistentId]),
).to.be.rejectedWith("Application does not exist");
});

it("Should emit the ApplicationDeleted event with the deleted application's details", async () => {
const txHash = await contract.write.deleteApplication([applicationId]);

const events = await contract.getEvents.ApplicationDeleted();
expect(events.length).to.be.equal(1);
const events = await contract.getEvents.ApplicationDeleted();
expect(events.length).to.be.equal(1);

const event = events[0];
expect(event.eventName).to.be.equal("ApplicationDeleted");
Expand All @@ -215,13 +221,12 @@ describe("ApplicationManager", () => {
});
});


describe("getApplication", () => {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
let contract: any;
let app: Application;
let applicationId: string;
let account= privateKeyToAccount(generatePrivateKey()).address
const account = privateKeyToAccount(generatePrivateKey()).address;

beforeEach(async () => {
const fixture = await loadFixture(deploy);
Expand All @@ -237,17 +242,86 @@ describe("ApplicationManager", () => {
});

it("Should accept id as input", async () => {
expect(await contract.write.getApplication([applicationId])).to.be
.string;
expect(await contract.write.getApplication([applicationId])).to.be.string;
});
it("Should return the application from the applications mapping with the provided applicationId", async () => {
expect(await contract.read.getApplication([applicationId])).to.contains(app);
expect(await contract.read.getApplication([applicationId])).to.contains(
app,
);
});
it("Should revert if the application does not exist", async () => {
it("Should revert if the application does not exist", async () => {
const nonExistentId = parseUnits("999", 0);
await expect( contract.read.getApplication([nonExistentId])).to.be.rejectedWith(
"Application does not exist"
);
});
await expect(
contract.read.getApplication([nonExistentId]),
).to.be.rejectedWith("Application does not exist");
});
});

describe("getApplications", () => {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
let contract: any;
let apps: Application[];
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
let fixture: any;

beforeEach(async () => {
fixture = await loadFixture(deploy);
contract = fixture.contract;
apps = [];
for (let i = 0; i < 8; i++) {
const app = {
name: `app${i}`,
account: generateRandomAddress() as Address,
};
await contract.write.createApplication([app]);
apps.push(app);
}
});

it("Should accept start and limit as inputs", async () => {
const start = parseUnits("0", 0);
const limit = parseUnits("7", 0);

expect(await contract.write.getApplications([start, limit])).to.be.string;
});

it("Should return an array of Application structs starting from the start index and containing up to limit entries.", async () => {
const start = parseUnits("0", 0);
const limit = parseUnits("6", 0);
const returnedApps = await contract.read.getApplications([start, limit]);
expect(returnedApps.length).to.equal(6);
expect(returnedApps).to.deep.include.members([
apps[0],
apps[1],
apps[2],
apps[3],
apps[4],
apps[5],
]);
});

it("Should return fewer than limit entries if there are not enough applications", async () => {
await contract.write.deleteApplication([parseUnits("7", 0)]);
const start = parseUnits("6", 0);
const limit = parseUnits("2", 0);
const returnedApps = await contract.read.getApplications([start, limit]);
expect(returnedApps.length).to.equal(1);
expect(returnedApps).to.deep.include.members([apps[6]]);
});

it("Should return no applications if all within range are deleted", async () => {
await contract.write.deleteApplication([parseUnits("0", 0)]);
await contract.write.deleteApplication([parseUnits("2", 0)]);
await contract.write.deleteApplication([parseUnits("1", 0)]);
await contract.write.deleteApplication([parseUnits("3", 0)]);
await contract.write.deleteApplication([parseUnits("4", 0)]);
await contract.write.deleteApplication([parseUnits("5", 0)]);
await contract.write.deleteApplication([parseUnits("6", 0)]);
await contract.write.deleteApplication([parseUnits("7", 0)]);
const start = parseUnits("0", 0);
const limit = parseUnits("7", 0);
const returnedApps = await contract.read.getApplications([start, limit]);
expect(returnedApps.length).to.equal(0);
});
});
});

0 comments on commit b3f7169

Please sign in to comment.