-
Notifications
You must be signed in to change notification settings - Fork 16
chore(ens-referrals): Update referral program edition defaults #1849
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f69570f
f02d544
1f3e88c
621556e
a9af00b
03dc906
08dca2d
221abd6
8b750bc
2091e38
b350e0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@namehash/ens-referrals": minor | ||
| --- | ||
|
|
||
| Added unit tests for Referral Program edition defaults. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@namehash/ens-referrals": minor | ||
| --- | ||
|
|
||
| Updated the Referral Program edition defaults to match the [production configuration](https://ensawards.org/production-editions.json). | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,208 @@ | ||
| import { beforeAll, describe, expect, it } from "vitest"; | ||
|
|
||
| import { ENSNamespaceIds, priceUsdc } from "@ensnode/ensnode-sdk"; | ||
|
|
||
| import { deserializeReferralProgramEditionConfigSetArray } from "./api"; | ||
| import type { ReferralProgramRulesPieSplit } from "./award-models/pie-split/rules"; | ||
| import type { ReferralProgramRulesRevShareLimit } from "./award-models/rev-share-limit/rules"; | ||
| import { ReferralProgramAwardModels } from "./award-models/shared/rules"; | ||
| import { | ||
| REFERRAL_PROGRAM_EDITION_SLUG_PATTERN, | ||
| type ReferralProgramEditionConfig, | ||
| } from "./edition"; | ||
| import { getDefaultReferralProgramEditionConfigSet } from "./edition-defaults"; | ||
|
|
||
| const PRODUCTION_EDITIONS_URL = "https://ensawards.org/production-editions.json"; | ||
|
|
||
| async function fetchProductionEditions(): Promise<ReferralProgramEditionConfig[] | null> { | ||
| let response: Response; | ||
| try { | ||
| response = await fetch(PRODUCTION_EDITIONS_URL); | ||
| } catch { | ||
| return null; | ||
| } | ||
Y3drk marked this conversation as resolved.
Show resolved
Hide resolved
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (!response.ok) return null; | ||
| // Intentionally let deserialize errors throw so parity regressions fail the suite. | ||
| return deserializeReferralProgramEditionConfigSetArray(await response.json()); | ||
Y3drk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| describe("getDefaultReferralProgramEditionConfigSet", () => { | ||
| const configSet = getDefaultReferralProgramEditionConfigSet(ENSNamespaceIds.Mainnet); | ||
|
|
||
| describe("Should match production editions", () => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't this fail every time we introduce some changes to default editions or even the edition model? My concern is that for these tests to pass we need to modify the production setup. Or is it intended to be handled backwards? We first modify the production, and only then modify the defaults? What is the strategy here? |
||
| let productionEditions: ReferralProgramEditionConfig[] | null = null; | ||
|
|
||
| beforeAll(async () => { | ||
| productionEditions = await fetchProductionEditions(); | ||
| }); | ||
|
|
||
| it("Returns the expected number of editions", ({ skip }) => { | ||
| if (!productionEditions) | ||
| return skip(`Could not fetch production editions from ${PRODUCTION_EDITIONS_URL}`); | ||
| expect(configSet.size).toBe(productionEditions.length); | ||
| }); | ||
|
|
||
| it("Contains all expected edition `slug`s", ({ skip }) => { | ||
| if (!productionEditions) | ||
| return skip(`Could not fetch production editions from ${PRODUCTION_EDITIONS_URL}`); | ||
| expect(new Set(configSet.keys())).toStrictEqual( | ||
| new Set(productionEditions.map((e) => e.slug)), | ||
| ); | ||
| }); | ||
|
|
||
| it("Each edition matches its production counterpart", ({ skip }) => { | ||
| if (!productionEditions) | ||
| return skip(`Could not fetch production editions from ${PRODUCTION_EDITIONS_URL}`); | ||
|
|
||
| for (const expected of productionEditions) { | ||
| const edition = configSet.get(expected.slug); | ||
| expect(edition, `edition "${expected.slug}" should exist`).toBeDefined(); | ||
|
|
||
| if (edition === undefined) continue; | ||
|
|
||
| const rules = edition.rules; | ||
|
|
||
| expect( | ||
| edition.displayName, | ||
| `edition "${expected.slug}" should have the correct <displayName>. Expected "${expected.displayName}", got "${edition.displayName}"`, | ||
| ).toBe(expected.displayName); | ||
| expect( | ||
| rules.awardModel, | ||
| `edition "${expected.slug}" should have the correct <awardModel>. Expected "${expected.rules.awardModel}", got "${rules.awardModel}"`, | ||
| ).toBe(expected.rules.awardModel); | ||
| expect( | ||
| rules.startTime, | ||
| `edition "${expected.slug}" should have the correct <startTime>. Expected "${expected.rules.startTime}", got "${rules.startTime}"`, | ||
| ).toBe(expected.rules.startTime); | ||
| expect( | ||
| rules.endTime, | ||
| `edition "${expected.slug}" should have the correct <endTime>. Expected "${expected.rules.endTime}", got "${rules.endTime}"`, | ||
| ).toBe(expected.rules.endTime); | ||
| expect( | ||
| rules.subregistryId, | ||
| `edition "${expected.slug}" should have the correct <subregistryId>. Expected "${expected.rules.subregistryId}", got "${rules.subregistryId}"`, | ||
| ).toStrictEqual(expected.rules.subregistryId); | ||
| expect( | ||
| rules.rulesUrl.href, | ||
| `edition "${expected.slug}" should have the correct <rulesUrl>. Expected "${expected.rules.rulesUrl.href}", got "${rules.rulesUrl.href}"`, | ||
| ).toStrictEqual(expected.rules.rulesUrl.href); | ||
| expect( | ||
| rules.areAwardsDistributed, | ||
| `edition "${expected.slug}" should have the correct <areAwardsDistributed>. Expected "${expected.rules.areAwardsDistributed}", got "${rules.areAwardsDistributed}"`, | ||
| ).toBe(expected.rules.areAwardsDistributed); | ||
|
|
||
| if ( | ||
| rules.awardModel !== ReferralProgramAwardModels.Unrecognized && | ||
| expected.rules.awardModel !== ReferralProgramAwardModels.Unrecognized | ||
| ) { | ||
| expect( | ||
| rules.totalAwardPoolValue, | ||
| `edition "${expected.slug}" should have the correct <totalAwardPoolValue>. Expected "${priceUsdc(expected.rules.totalAwardPoolValue.amount)}", got "${rules.totalAwardPoolValue}"`, | ||
| ).toStrictEqual(priceUsdc(expected.rules.totalAwardPoolValue.amount)); | ||
| } | ||
|
|
||
| // If statements required for type-safety. | ||
| // The equality of the `awardModel` field is guaranteed by the test above | ||
| if ( | ||
| rules.awardModel === ReferralProgramAwardModels.PieSplit && | ||
| expected.rules.awardModel === ReferralProgramAwardModels.PieSplit | ||
| ) { | ||
| const expectedPieSplitRules = expected.rules as ReferralProgramRulesPieSplit; | ||
| expect( | ||
| rules.maxQualifiedReferrers, | ||
| `edition "${expected.slug}" should have the correct <maxQualifiedReferrers>. Expected "${expectedPieSplitRules.maxQualifiedReferrers}", got "${rules.maxQualifiedReferrers}"`, | ||
| ).toBe(expectedPieSplitRules.maxQualifiedReferrers); | ||
| } | ||
|
|
||
| if ( | ||
| rules.awardModel === ReferralProgramAwardModels.RevShareLimit && | ||
| expected.rules.awardModel === ReferralProgramAwardModels.RevShareLimit | ||
| ) { | ||
| const expectedRevShareLimitRules = expected.rules as ReferralProgramRulesRevShareLimit; | ||
| expect( | ||
| rules.minQualifiedRevenueContribution, | ||
| `edition "${expected.slug}" should have the correct <minQualifiedRevenueContribution>. Expected "${priceUsdc(expectedRevShareLimitRules.minQualifiedRevenueContribution.amount)}", got "${rules.minQualifiedRevenueContribution}"`, | ||
| ).toStrictEqual( | ||
| priceUsdc(expectedRevShareLimitRules.minQualifiedRevenueContribution.amount), | ||
| ); | ||
| expect( | ||
| rules.qualifiedRevenueShare, | ||
| `edition "${expected.slug}" should have the correct <qualifiedRevenueShare>. Expected "${expectedRevShareLimitRules.qualifiedRevenueShare}", got "${rules.qualifiedRevenueShare}"`, | ||
| ).toBe(expectedRevShareLimitRules.qualifiedRevenueShare); | ||
|
|
||
| // Skipping the test of `disqualifications` intentionally due to high volatility of the field. | ||
| // Additionally it should not be expected of edition defaults to provide up-to-date disqualifications info. | ||
| } | ||
Y3drk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| }); | ||
| }); | ||
|
|
||
| describe("Should have a valid structure", () => { | ||
| it("Maintains the config set invariant (map key === config.slug)", () => { | ||
| for (const [key, config] of configSet) { | ||
| expect(key, `config key "${key}" should match config.slug "${config.slug}"`).toBe( | ||
| config.slug, | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it("All editions have valid `slug` format", () => { | ||
| for (const [slug] of configSet) { | ||
| expect(slug, `edition "${slug}" should match the slug pattern`).toMatch( | ||
| REFERRAL_PROGRAM_EDITION_SLUG_PATTERN, | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it("All editions have non-empty `displayName`s", () => { | ||
| for (const [, config] of configSet) { | ||
| expect( | ||
| config.displayName.length, | ||
| `edition "${config.slug}" should have a non-empty <displayName>`, | ||
| ).toBeGreaterThan(0); | ||
| } | ||
| }); | ||
|
|
||
| it("All editions have `endTime` >= `startTime`", () => { | ||
| for (const [, config] of configSet) { | ||
| expect( | ||
| config.rules.endTime, | ||
| `edition "${config.slug}" should have <endTime> >= <startTime>. Got endTime: "${config.rules.endTime}", startTime: "${config.rules.startTime}"`, | ||
| ).toBeGreaterThanOrEqual(config.rules.startTime); | ||
| } | ||
| }); | ||
|
|
||
| it("All editions have a recognized `awardModel`", () => { | ||
| const recognizedModels: string[] = [ | ||
| ReferralProgramAwardModels.PieSplit, | ||
| ReferralProgramAwardModels.RevShareLimit, | ||
| ]; | ||
| for (const [, config] of configSet) { | ||
| expect( | ||
| recognizedModels, | ||
| `edition "${config.slug}" should have a recognized <awardModel>`, | ||
| ).toContain(config.rules.awardModel); | ||
| } | ||
| }); | ||
|
|
||
| it("All editions have valid `rulesUrl`s", () => { | ||
| for (const [, config] of configSet) { | ||
| expect( | ||
| config.rules.rulesUrl, | ||
| `edition "${config.slug}" should have a valid <rulesUrl>`, | ||
| ).toBeInstanceOf(URL); | ||
| expect( | ||
| config.rules.rulesUrl.protocol, | ||
| `edition "${config.slug}" should have a valid <rulesUrl> protocol`, | ||
| ).toBe("https:"); | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| describe("Error handling", () => { | ||
| it("Throws for an unsupported namespace", () => { | ||
| expect(() => getDefaultReferralProgramEditionConfigSet("invalid-namespace" as any)).toThrow(); | ||
| }); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.