Skip to content

Commit

Permalink
feat: refactored provider filtering, added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lucianHymer committed Dec 4, 2024
1 parent ed9a75d commit 9aee965
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 40 deletions.
105 changes: 101 additions & 4 deletions app/__tests__/components/StampSelector.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { vi, describe, it, expect, Mock } from "vitest";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render, screen } from "@testing-library/react";
import { screen } from "@testing-library/react";
import { PROVIDER_ID } from "@gitcoin/passport-types";
import { useCustomization } from "../../hooks/useCustomization";
import { platforms } from "@gitcoin/passport-platforms";
Expand All @@ -20,7 +19,7 @@ describe("<StampSelector />", () => {
});

describe("exclusion tests", () => {
const renderTestComponent = (customization: any) => {
const renderTestComponent = (customization: any, stampScores = {}) => {
(useCustomization as Mock).mockReturnValue(customization);
renderWithContext(
testCeramicContext,
Expand All @@ -40,6 +39,7 @@ describe("<StampSelector />", () => {
BeginnerCommunityStaker: "1",
ExperiencedCommunityStaker: "1",
TrustedCitizen: "1",
...stampScores,
},
}
);
Expand Down Expand Up @@ -92,10 +92,107 @@ describe("<StampSelector />", () => {
},
},
});
expect(screen.queryByTestId("checkbox-SelfStakingBronze")).not.toBeInTheDocument();
expect(screen.queryByTestId("indicator-SelfStakingBronze")).not.toBeInTheDocument();
expect(screen.queryByText("Self GTC Staking")).not.toBeInTheDocument();
});

it("should hide deprecated stamps with zero score", () => {
const customization = {
useCustomDashboard: true,
dashboardPanel: {},
scorer: {
weights: {
SelfStakingBronze: "1",
SelfStakingSilver: "1",
},
},
};

// Mock a deprecated provider
const originalProviders = GtcStaking.ProviderConfig[0].providers;
GtcStaking.ProviderConfig[0].providers = [
{ ...originalProviders[0], isDeprecated: true, name: "SelfStakingBronze" },
{ ...originalProviders[1], isDeprecated: false, name: "SelfStakingSilver" },
];

renderTestComponent(customization, {
SelfStakingBronze: "0",
SelfStakingSilver: "1",
});

// The group header should be present
expect(screen.queryByText("Self GTC Staking")).toBeInTheDocument();
expect(screen.queryByTestId("indicator-SelfStakingBronze")).not.toBeInTheDocument();
expect(screen.queryByTestId("indicator-SelfStakingSilver")).toBeInTheDocument();

// Restore original providers
GtcStaking.ProviderConfig[0].providers = originalProviders;
});

it("should hide group when all stamps are deprecated with zero scores", () => {
const customization = {
useCustomDashboard: true,
dashboardPanel: {},
scorer: {
weights: {
SelfStakingBronze: "1",
SelfStakingSilver: "1",
},
},
};

// Mock all providers in a group as deprecated
const originalProviders = GtcStaking.ProviderConfig[0].providers;
GtcStaking.ProviderConfig[0].providers = [
{ ...originalProviders[0], isDeprecated: true, name: "SelfStakingBronze" },
{ ...originalProviders[1], isDeprecated: true, name: "SelfStakingSilver" },
];

renderTestComponent(customization, {
SelfStakingBronze: "0",
SelfStakingSilver: "0",
});

// The group header should not be present
expect(screen.queryByText("Self GTC Staking")).not.toBeInTheDocument();
expect(screen.queryByTestId("indicator-SelfStakingBronze")).not.toBeInTheDocument();
expect(screen.queryByTestId("indicator-SelfStakingSilver")).not.toBeInTheDocument();

// Restore original providers
GtcStaking.ProviderConfig[0].providers = originalProviders;
});

it("should show deprecated stamps with non-zero score", () => {
const customization = {
useCustomDashboard: true,
dashboardPanel: {},
scorer: {
weights: {
SelfStakingBronze: "1",
SelfStakingSilver: "1",
},
},
};

// Mock a deprecated provider
const originalProviders = GtcStaking.ProviderConfig[0].providers;
GtcStaking.ProviderConfig[0].providers = [
{ ...originalProviders[0], isDeprecated: true, name: "SelfStakingBronze" },
{ ...originalProviders[1], isDeprecated: false, name: "SelfStakingSilver" },
];

renderTestComponent(customization, {
SelfStakingBronze: "1",
SelfStakingSilver: "1",
});

expect(screen.queryByTestId("indicator-SelfStakingBronze")).toBeInTheDocument();
expect(screen.queryByTestId("indicator-SelfStakingSilver")).toBeInTheDocument();

// Restore original providers
GtcStaking.ProviderConfig[0].providers = originalProviders;
});

it("include platform if customization doesn't specify custom weights", () => {
renderTestComponent({});
expect(screen.queryByText("Self GTC Staking")).toBeInTheDocument();
Expand Down
31 changes: 2 additions & 29 deletions platforms/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
// Provider Utils
import { Providers } from "./utils/providers";
import { SimpleProvider } from "./utils/simpleProvider";
import { SimpleEvmProvider } from "./utils/simpleEvmProvider";
import { ClearTextSimpleProvider } from "./utils/clearTextSimpleProvider";
import { ClearTextTwitterProvider, ClearTextGithubOrgProvider } from "./ClearText";

import platforms from "./platforms";
import { keccak256, toUtf8Bytes } from "ethers";
import { PROVIDER_ID } from "@gitcoin/passport-types";
import { createProviders } from "utils/createProviders";

// Check that all platforms have a ProviderConfig, PlatformDetails, and providers
Object.entries(platforms).map(([platformName, platform]) => {
Expand All @@ -17,10 +11,6 @@ Object.entries(platforms).map(([platformName, platform]) => {
if (!providers?.length) throw new Error(`No providers defined in ${platformName}/Providers-config.ts`);
});

const platformProviders = Object.values(platforms)
.map((platform) => platform.providers)
.flat();

// Set hash on each provider spec
Object.values(platforms).map(({ ProviderConfig }) => {
ProviderConfig.map(({ providers }) => {
Expand All @@ -30,24 +20,7 @@ Object.values(platforms).map(({ ProviderConfig }) => {
});
});

const deprecatedProviderIds = Object.values(platforms)
.map(({ ProviderConfig }) =>
ProviderConfig.map(({ providers }) => providers.filter(({ isDeprecated }) => isDeprecated))
)
.flat(3)
.map(({ name }) => name);

export const providers = new Providers(
[
// Example provider which verifies the payload when `payload.proofs.valid === "true"`
new SimpleProvider(),
new SimpleEvmProvider(),
new ClearTextSimpleProvider(),
new ClearTextTwitterProvider(),
new ClearTextGithubOrgProvider(),
...platformProviders,
].filter(({ type }) => !deprecatedProviderIds.includes(type as PROVIDER_ID))
);
export const providers = createProviders(platforms);

export * from "./types";
export { Platform as PlatformClass } from "./utils/platform";
Expand Down
2 changes: 1 addition & 1 deletion platforms/src/platforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import * as Binance from "./Binance";
import * as CustomGithub from "./CustomGithub";
import { PlatformSpec, PlatformGroupSpec, Provider } from "./types";

type PlatformConfig = {
export type PlatformConfig = {
PlatformDetails: PlatformSpec;
ProviderConfig: PlatformGroupSpec[];
providers: Provider[];
Expand Down
72 changes: 72 additions & 0 deletions platforms/src/utils/__tests__/createProviders.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-disable */
import { PLATFORM_ID, PROVIDER_ID, ProviderContext, RequestPayload } from "@gitcoin/passport-types";
import { SimpleProvider } from "../simpleProvider";
import { createProviders } from "../createProviders";
import { PlatformConfig } from "platforms";

jest.useFakeTimers(); // Use Jest's timer mocks

const makeSimplePlatforms = ({
stampIsDeprecated,
}: {
stampIsDeprecated: boolean;
}): Record<string, PlatformConfig> => ({
Simple: {
providers: [new SimpleProvider()],
PlatformDetails: {
connectMessage: "",
description: "",
enablePlatformCardUpdate: false,
icon: "",
isEVM: false,
name: "Simple",
platform: "Simple" as PLATFORM_ID,
website: "",
},
ProviderConfig: [
{
platformGroup: "Simple",
providers: [
{
title: "Simple",
name: "Simple" as PROVIDER_ID,
isDeprecated: stampIsDeprecated,
},
],
},
],
},
});

describe("createProviders", () => {
const mockContext: ProviderContext = {};

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

test.each([
{
stampIsDeprecated: true,
},
{
stampIsDeprecated: false,
},
])("should filter out deprecated providers(deprecated=$stampIsDeprecated)", async ({ stampIsDeprecated }) => {
const platforms = makeSimplePlatforms({ stampIsDeprecated });
const providers = createProviders(platforms);

const result = await providers.verify("Simple", mockPayload, mockContext);

expect(result.valid).toEqual(!stampIsDeprecated);
if (stampIsDeprecated) {
expect(result.errors).toEqual(["Missing provider"]);
}
});
});
17 changes: 11 additions & 6 deletions platforms/src/utils/__tests__/providers.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
/* eslint-disable */
import { RequestPayload, ProviderContext, VerifiedPayload } from "@gitcoin/passport-types";
import { RequestPayload, ProviderContext, VerifiedPayload, PROVIDER_ID } from "@gitcoin/passport-types";
import { ProviderExternalVerificationError } from "../../types";
import { Providers, withTimeout } from "../providers";
import { SimpleProvider, verifySimpleProvider } from "../simpleProvider";

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


jest.useFakeTimers(); // Use Jest's timer mocks

describe("withTimeout", () => {
beforeAll(() => {
jest.spyOn(global, 'clearTimeout');
jest.spyOn(global, "clearTimeout");
});
it("should resolve with the correct value if the promise resolves before the timeout", async () => {
const expectedValue = { valid: true };
const fastPromise = new Promise((resolve) => setTimeout(() => resolve(expectedValue), 1000)) as Promise<VerifiedPayload>;
const fastPromise = new Promise((resolve) =>
setTimeout(() => resolve(expectedValue), 1000)
) as Promise<VerifiedPayload>;

const resultPromise = withTimeout(3000, fastPromise, "testType");
jest.advanceTimersByTime(1000); // Fast-forward until all timers are executed
Expand All @@ -25,13 +26,17 @@ describe("withTimeout", () => {
});

it("should reject with a timeout error if the promise does not resolve in time", async () => {
const slowPromise = new Promise((resolve) => setTimeout(() => resolve({ valid: true }), 5000)) as Promise<VerifiedPayload>;
const slowPromise = new Promise((resolve) =>
setTimeout(() => resolve({ valid: true }), 5000)
) as Promise<VerifiedPayload>;

const resultPromise = withTimeout(3000, slowPromise, "testType");
jest.advanceTimersByTime(3001); // Fast-forward until the timeout should occur

await expect(resultPromise).rejects.toThrow(ProviderExternalVerificationError);
await expect(resultPromise).rejects.toThrow("Request timeout while verifying testType. It took over 3000 ms to complete.");
await expect(resultPromise).rejects.toThrow(
"Request timeout while verifying testType. It took over 3000 ms to complete."
);
expect(clearTimeout).toHaveBeenCalledTimes(1);
});
});
Expand Down
37 changes: 37 additions & 0 deletions platforms/src/utils/createProviders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Provider Utils
import { Providers } from "./providers";
import { Provider } from "../types";
import { SimpleProvider } from "./simpleProvider";
import { SimpleEvmProvider } from "./simpleEvmProvider";
import { ClearTextSimpleProvider } from "./clearTextSimpleProvider";
import { ClearTextTwitterProvider, ClearTextGithubOrgProvider } from "../ClearText";

import { PlatformConfig } from "../platforms";
import { PROVIDER_ID } from "@gitcoin/passport-types";

export const createProviders = (platforms: Record<string, PlatformConfig>): Providers => {
const platformProviders = Object.values(platforms)
.map((platform) => platform.providers)
.flat();

const deprecatedProviderIds = Object.values(platforms)
.map(({ ProviderConfig }) =>
ProviderConfig.map(({ providers }) => providers.filter(({ isDeprecated }) => isDeprecated))
)
.flat(3)
.map(({ name }) => name);

const providerIsNotDeprecated = ({ type }: Provider) => !deprecatedProviderIds.includes(type as PROVIDER_ID);

return new Providers(
[
// Example provider which verifies the payload when `payload.proofs.valid === "true"`
new SimpleProvider(),
new SimpleEvmProvider(),
new ClearTextSimpleProvider(),
new ClearTextTwitterProvider(),
new ClearTextGithubOrgProvider(),
...platformProviders,
].filter(providerIsNotDeprecated)
);
};

0 comments on commit 9aee965

Please sign in to comment.