Skip to content

Commit

Permalink
moved provider utils tests to their own file
Browse files Browse the repository at this point in the history
  • Loading branch information
lucianHymer committed Aug 10, 2023
1 parent df917e0 commit 0dd74c2
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ jest.mock("../../procedures/twitterOauth", () => ({
getAuthClient: jest.fn(),
}));

jest.spyOn(console, "error").mockImplementation(() => {});

describe("TwitterAccountAgeProvider", function () {
beforeEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -193,43 +191,4 @@ describe("TwitterAccountAgeProvider", function () {
record: undefined,
});
});

it("should report unhandled error if result is invalid but no errors provided", async () => {
(getTwitterUserData as jest.MockedFunction<typeof getTwitterUserData>).mockImplementation(() => {
return Promise.reject({ valid: false });
});
const provider = new TwitterAccountAgeProvider({ threshold: "730" });
const providers = new Providers([provider]);

const unknownErrorOne = "UNHANDLED ERROR: for type twitterAccountAgeGte#730 and address 0x0 -";
const unknownErrorTwo = "unable to parse, not derived from Error";

const result = await providers._updatedVerify(mockPayload.type, mockPayload, mockContext);

expect(console.error).toHaveBeenCalledWith(unknownErrorOne, unknownErrorTwo);
expect(result.error[0]).toEqual("There was an unexpected error during verification.");
});

it("should handle unexpected errors", async () => {
const unexpectedError = new Error("Unexpected error.");
(getTwitterUserData as jest.MockedFunction<typeof getTwitterUserData>).mockImplementation(() => {
return Promise.reject({ valid: false, error: unexpectedError });
});
const provider = new TwitterAccountAgeProvider({ threshold: "730" });
const providers = new Providers([provider]);

const result = await providers._updatedVerify(mockPayload.type, mockPayload, mockContext);

expect(result.valid).toEqual(false);
expect(result.error[0]).toContain("There was an unexpected error during verification.");
});

it("should return missing provider error if type doesn\"t exist", async () => {
const provider = new TwitterAccountAgeProvider({ threshold: "730" });
const providers = new Providers([provider]);

const result = await providers._updatedVerify("nonExistentType", mockPayload, mockContext);
expect(result.valid).toEqual(false);
expect(result.error).toEqual(["Missing provider"]);
});
});
99 changes: 99 additions & 0 deletions platforms/src/utils/__tests__/providers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* eslint-disable */
import { RequestPayload, ProviderContext } from "@gitcoin/passport-types";
import { ProviderExternalVerificationError } from "../../types";
import { Providers } from "../providers";
import { SimpleProvider, verifySimpleProvider } from "../simpleProvider";

jest.spyOn(console, "error").mockImplementation(() => {});

describe("Providers", function () {
beforeEach(() => {
jest.clearAllMocks();
});

const mockContext: ProviderContext = {};

const mockPayload: RequestPayload = {
address: "0x0",
proofs: {
username: "test",
valid: "true",
},
type: "Simple",
version: "",
};

it("should report unexpected errors even if not derived from Error", async () => {
(verifySimpleProvider as jest.MockedFunction<typeof verifySimpleProvider>) = jest.fn().mockImplementation(() => {
throw "I don't have an error type";
});

const unknownErrorMessagePartOne = "UNHANDLED ERROR: for type Simple and address 0x0 -";
const unknownErrorMessagePartTwo = "unable to parse, not derived from Error";

const provider = new SimpleProvider();
const providers = new Providers([provider]);
const result = await providers.verify(mockPayload.type, mockPayload, mockContext);

expect(console.error).toHaveBeenCalledWith(unknownErrorMessagePartOne, unknownErrorMessagePartTwo);
expect(result).toEqual({
valid: false,
error: ["There was an unexpected error during verification."],
});
});

it("should report unexpected error details if derived from Error", async () => {
(verifySimpleProvider as jest.MockedFunction<typeof verifySimpleProvider>) = jest.fn().mockImplementation(() => {
class MyError extends Error {
constructor(message: string) {
super(message);
this.name = "MyError";
}
}
throw new MyError("I'm an unhandled error");
});

const unknownErrorMessagePartOne = "UNHANDLED ERROR: for type Simple and address 0x0 -";

const provider = new SimpleProvider();
const providers = new Providers([provider]);
const result = await providers.verify(mockPayload.type, mockPayload, mockContext);

expect(console.error).toHaveBeenCalledWith(unknownErrorMessagePartOne, expect.stringContaining("MyError at"));

expect(result).toEqual({
valid: false,
error: [
expect.stringContaining(
"There was an unexpected error during verification. MyError: I'm an unhandled error at"
),
],
});
});

it("should not report expected errors", async () => {
(verifySimpleProvider as jest.MockedFunction<typeof verifySimpleProvider>) = jest.fn().mockImplementation(() => {
throw new ProviderExternalVerificationError("I'm an expected error");
});

const provider = new SimpleProvider();
const providers = new Providers([provider]);
const result = await providers.verify(mockPayload.type, mockPayload, mockContext);

expect(console.error).not.toHaveBeenCalled();

expect(result).toEqual({
valid: false,
error: ["I'm an expected error"],
});
});

it("should return missing provider error if type doesn't exist", async () => {
const provider = new SimpleProvider();
const providers = new Providers([provider]);

const result = await providers.verify("nonExistentType", mockPayload, mockContext);
expect(result.valid).toEqual(false);
expect(result.error).toEqual(["Missing provider"]);
});
});
7 changes: 6 additions & 1 deletion platforms/src/utils/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
import { Provider, ProviderExternalVerificationError, ProviderInternalVerificationError } from "../types";
import type { RequestPayload, VerifiedPayload, ProviderContext } from "@gitcoin/passport-types";

const UPDATED_PROVIDERS = ["twitterAccountAgeGte#180", "twitterAccountAgeGte#365", "twitterAccountAgeGte#730"];
const UPDATED_PROVIDERS = [
"twitterAccountAgeGte#180",
"twitterAccountAgeGte#365",
"twitterAccountAgeGte#730",
"Simple",
];

function reportUnhandledError(type: string, address: string, e: unknown) {
if (process.env.EXIT_ON_UNHANDLED_ERROR === "true" && process.env.NODE_ENV === "development") {
Expand Down
20 changes: 14 additions & 6 deletions platforms/src/utils/simpleProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,19 @@ export class SimpleProvider implements Provider {

// verify that the proof object contains valid === "true"
verify(payload: RequestPayload): Promise<VerifiedPayload> {
return Promise.resolve({
valid: payload?.proofs?.valid === this._options.valid,
record: {
username: payload?.proofs?.username || "",
},
});
return Promise.resolve(verifySimpleProvider(payload));
}
}

// This is pulled out to allow easier mocking in tests
export const verifySimpleProvider = (payload: RequestPayload): VerifiedPayload => {
const valid = payload?.proofs?.valid === "true";
const errors = valid ? [] : ["Proof is not valid"];
return {
valid,
errors,
record: {
username: payload?.proofs?.username || "",
},
};
};

0 comments on commit 0dd74c2

Please sign in to comment.