Skip to content

Commit 87878fe

Browse files
committed
fix(allowlist.test): allowlist upload unit tests
Fixes the allowlist upload unit test flows. The current test suite was breaking on missing imports and the tested flow and types were updated to the latest implementation
1 parent 5fb0830 commit 87878fe

File tree

3 files changed

+130
-92
lines changed

3 files changed

+130
-92
lines changed

src/controllers/AllowListController.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,35 @@ export class AllowListController extends Controller {
3838
): Promise<StorageResponse> {
3939
const storage = await StorageService.init();
4040

41-
const result = parseAndValidateMerkleTree(requestBody);
41+
try {
42+
const result = parseAndValidateMerkleTree(requestBody);
4243

43-
if (!result.valid || !result.data) {
44-
this.setStatus(422);
44+
if (!result.valid || !result.data) {
45+
this.setStatus(422);
46+
return {
47+
success: false,
48+
message: "Errors while validating allow list",
49+
errors: result.errors,
50+
};
51+
}
52+
53+
const cid = await storage.uploadFile({
54+
file: jsonToBlob(requestBody.allowList),
55+
});
56+
this.setStatus(201);
57+
58+
return {
59+
success: true,
60+
data: cid,
61+
};
62+
} catch (error) {
63+
this.setStatus(500);
4564
return {
4665
success: false,
47-
message: "Errors while validating allow list",
48-
errors: result.errors,
66+
message: "Error uploading data",
67+
errors: { allowList: "Error uploading data" },
4968
};
5069
}
51-
52-
const cid = await storage.uploadFile({
53-
file: jsonToBlob(requestBody.allowList),
54-
});
55-
this.setStatus(201);
56-
57-
return {
58-
success: true,
59-
data: cid,
60-
};
6170
}
6271

6372
/**

test/api/v1/allowlist.test.ts

Lines changed: 101 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,126 @@
1-
import { describe, it, afterEach, afterAll } from "vitest";
1+
import { describe, test, vi } from "vitest";
22
import { expect } from "chai";
3-
import { createMocks, RequestMethod } from "node-mocks-http";
4-
import { Request, Response } from "express";
5-
6-
import sinon from "sinon";
3+
import {
4+
incorrectMerkleTree,
5+
mockMerkleTree,
6+
} from "../../test-utils/mockMerkleTree.js";
7+
import { mock } from "vitest-mock-extended";
8+
import { AllowListController } from "../../../src/controllers/AllowListController.js";
9+
import { StorageService } from "../../../src/services/StorageService.js";
10+
11+
const mocks = vi.hoisted(() => {
12+
return {
13+
init: vi.fn(),
14+
};
15+
});
716

8-
import { data } from "../../test-utils";
9-
import { Client } from "@web3-storage/w3up-client";
10-
import { allowlistHandler } from "@/handlers/v1/web3up/allowlist";
11-
import { Link } from "@web3-storage/access";
17+
vi.mock("../../../src/services/StorageService", async () => {
18+
return {
19+
StorageService: { init: mocks.init },
20+
};
21+
});
1222

13-
describe("W3Up Client allowlist", async () => {
14-
const { metadata, merkleTree, someData } = data;
23+
describe("Allow list upload at v1/allowlists", async () => {
24+
const controller = new AllowListController();
25+
const mockStorage = mock<StorageService>();
1526

16-
const storeBlobMock = sinon
17-
.stub(Client.prototype, "uploadFile")
18-
.resolves({ "/": merkleTree.cid } as unknown as Link); //TODO better Link object creation
27+
test("Stores a new allowlist and returns CID", async () => {
28+
mocks.init.mockResolvedValue(mockStorage);
1929

20-
const mockRequestResponse = (method: RequestMethod = "POST") => {
21-
const { req, res }: { req: Request; res: Response } = createMocks({
22-
method,
23-
});
24-
req.headers = {
25-
"Content-Type": "application/json",
30+
mockStorage.uploadFile.mockResolvedValue({ cid: "TEST_CID" });
31+
const requestBody = {
32+
allowList: mockMerkleTree,
33+
totalUnits: "100000000",
2634
};
27-
req.body = { allowList: merkleTree.data, totalUnits: 100n };
28-
return { req, res };
29-
};
30-
31-
afterEach(() => {
32-
sinon.resetHistory();
33-
});
35+
const response = await controller.storeAllowList(requestBody);
3436

35-
afterAll(() => {
36-
sinon.resetBehavior();
37+
expect(response.success).to.be.true;
38+
expect(response.data).to.not.be.undefined;
39+
expect(response.data?.cid).to.eq("TEST_CID");
3740
});
3841

39-
it("POST valid allowList - 200", async () => {
40-
const { req, res } = mockRequestResponse();
41-
await allowlistHandler(req, res);
42+
test("Returns errors and message when allowlist is invalid", async () => {
43+
mocks.init.mockResolvedValue(mockStorage);
4244

43-
expect(res.statusCode).to.eq(200);
44-
expect(res.getHeaders()).to.deep.eq({ "content-type": "application/json" });
45-
expect(res.statusMessage).to.eq("OK");
46-
47-
//TODO better typing and check on returned CID
48-
// @ts-ignore
49-
expect(res._getJSONData().message).to.eq("Data uploaded succesfully");
50-
// @ts-ignore
51-
expect(res._getJSONData().cid).to.not.be.undefined;
45+
mockStorage.uploadFile.mockResolvedValue({ cid: "TEST_CID" });
46+
const requestBody = {
47+
allowList: incorrectMerkleTree,
48+
totalUnits: "100000000",
49+
};
50+
const response = await controller.storeAllowList(requestBody);
5251

53-
expect(storeBlobMock.callCount).to.eq(1);
52+
expect(response.success).to.be.false;
53+
expect(response.data).to.be.undefined;
54+
expect(response.message).to.eq("Errors while validating allow list");
55+
expect(response.errors).to.deep.eq({
56+
allowListData: "Data could not be parsed to OpenZeppelin MerkleTree",
57+
});
5458
});
5559

56-
it("GET allowlist not allowed - 405", async () => {
57-
const { req, res } = mockRequestResponse();
58-
req.method = "GET";
59-
await allowlistHandler(req, res);
60+
test("Handles errors during upload", async () => {
61+
mocks.init.mockResolvedValue(mockStorage);
6062

61-
expect(res.statusCode).to.eq(405);
62-
expect(res.getHeaders()).to.deep.eq({ "content-type": "application/json" });
63-
expect(res.statusMessage).to.eq("OK");
63+
const mockError = new Error("Error uploading data");
64+
mockStorage.uploadFile.mockRejectedValue(mockError);
6465

65-
//TODO better typing and check on returned CID
66-
// @ts-ignore
67-
expect(res._getJSONData().message).to.eq("Not allowed");
66+
const requestBody = {
67+
allowList: mockMerkleTree,
68+
totalUnits: "100000000",
69+
};
70+
const response = await controller.storeAllowList(requestBody);
6871

69-
expect(storeBlobMock.callCount).to.eq(0);
72+
expect(response.success).to.be.false;
73+
expect(response.data).to.be.undefined;
74+
expect(response.errors).to.deep.eq({
75+
allowList: "Error uploading data",
76+
});
7077
});
78+
});
7179

72-
it("POST incorrect allowlist - 400", async () => {
73-
const { req, res } = mockRequestResponse();
74-
req.body = { allowList: data.someData.data, totalUnits: 100n };
75-
await allowlistHandler(req, res);
76-
77-
expect(res.statusCode).to.eq(400);
78-
expect(res.getHeaders()).to.deep.eq({ "content-type": "application/json" });
79-
expect(res.statusMessage).to.eq("OK");
80+
describe("Allow list validation at v1/allowlists/validate", async () => {
81+
const controller = new AllowListController();
8082

81-
//TODO better typing and check on returned CID
82-
// @ts-ignore
83-
expect(res._getJSONData().message).to.eq("Not a valid merkle tree object");
83+
test("Validates correctness of allowlist and returns results", async () => {
84+
const requestBody = {
85+
allowList: mockMerkleTree,
86+
totalUnits: "100",
87+
};
88+
const response = await controller.validateAllowList(requestBody);
89+
expect(response.valid).to.be.true;
90+
expect(response.success).to.be.true;
91+
expect(response.message).to.be.eq(
92+
"Allowlist is a valid hypercerts allowlist object.",
93+
);
8494
});
8595

86-
it("POST upload allowlist fails - 500", async () => {
87-
const { req, res } = mockRequestResponse();
88-
storeBlobMock.rejects();
89-
await allowlistHandler(req, res);
90-
91-
expect(res.statusCode).to.eq(500);
92-
expect(res.getHeaders()).to.deep.eq({ "content-type": "application/json" });
93-
expect(res.statusMessage).to.eq("OK");
96+
test("Returns errors and message when allowlist is invalid", async () => {
97+
const requestBody = {
98+
allowList: incorrectMerkleTree,
99+
totalUnits: "100",
100+
};
101+
const response = await controller.validateAllowList(requestBody);
94102

95-
//TODO better typing and check on returned CID
96-
// @ts-ignore
97-
expect(res._getJSONData().message).to.eq("Error uploading data");
103+
expect(response.success).to.be.true;
104+
expect(response.valid).to.be.false;
105+
expect(response.message).to.eq("Errors while validating allow list");
106+
expect(response.errors).to.deep.eq({
107+
allowListData: "Data could not be parsed to OpenZeppelin MerkleTree",
108+
});
109+
});
98110

99-
expect(storeBlobMock.callCount).to.eq(1);
111+
test("Returns errors and message when total units doesn't match allow list", async () => {
112+
const requestBody = {
113+
allowList: mockMerkleTree,
114+
totalUnits: "99",
115+
};
116+
const response = await controller.validateAllowList(requestBody);
117+
118+
expect(response.success).to.be.true;
119+
expect(response.valid).to.be.false;
120+
expect(response.message).to.eq("Errors while validating allow list");
121+
expect(response.errors).to.deep.eq({
122+
units:
123+
"Total units in allowlist must match total units [expected: 99, got: 100]",
124+
});
100125
});
101126
});

test/test-utils/mockMerkleTree.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
const stringContent =
2-
'{"format":"standard-v1","tree":["0xe582f894ed4599f64aed0c4f6ea1ed2d2f7bca47a11c984e63d823a38d4f43b6"],"values":[{"value":["0x59266D85D94666D037C1e32dAa8FaC9E95CdaFEf",100],"treeIndex":0}],"leafEncoding":["address","uint256"]}';
2+
'{"format":"standard-v1","tree":["0xe582f894ed4599f64aed0c4f6ea1ed2d2f7bca47a11c984e63d823a38d4f43b6"],"values":[{"value":["0x59266D85D94666D037C1e32dAa8FaC9E95CdaFEf",100000000],"treeIndex":0}],"leafEncoding":["address","uint256"]}';
33

44
export const mockMerkleTree = stringContent;
5+
export const incorrectMerkleTree = stringContent.replace(
6+
'"leafEncoding":["address","uint256"]',
7+
'"leafEncoding":[null,null]',
8+
);

0 commit comments

Comments
 (0)