-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Reward modifiers tests #3020
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
Reward modifiers tests #3020
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughConsolidates commission checks into a new shared test helper, updates test resources (rewards, partners, customers, modifiers), replaces inline commission assertions in track tests, and adds E2E lead and sale reward test suites exercising country-conditioned and percentage/flat reward logic. Changes
Sequence Diagram(s)sequenceDiagram
participant Test as Test Suite
participant ClickAPI as /track/click
participant LeadAPI as /track/lead
participant SaleAPI as /track/sale
participant CustAPI as /customers
participant CommAPI as /commissions
participant Verify as verifyCommission
rect rgb(230,245,255)
Test->>ClickAPI: POST click (partner data)
ClickAPI-->>Test: clickId
end
rect rgb(230,255,230)
Test->>LeadAPI: POST lead (customer, clickId)
LeadAPI-->>Test: 200 OK
end
rect rgb(255,245,230)
Test->>SaleAPI: POST sale (invoiceId, amount, customer, paymentProcessor)
SaleAPI-->>Test: 200 OK
end
rect rgb(245,245,255)
Test->>Verify: call verifyCommission({http, customerExternalId?, invoiceId?, expectedAmount?, expectedEarnings})
Verify->>CustAPI: GET /customers?externalId=...
CustAPI-->>Verify: customerId (if requested)
Verify->>CommAPI: GET /commissions?customerId=...&invoiceId=...
CommAPI-->>Verify: [commission]
Verify->>Verify: assert single result & fields (invoiceId, customerId, amount, earnings)
Verify-->>Test: verification result
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/tests/tracks/track-lead.test.ts (1)
220-258: Fix partner/modifier mismatch - test validates wrong country.The test title indicates it should verify the US partner reward, but the code uses partner key
"marvin"which is the CA partner according toE2E_PARTNERS[1]. Additionally, it verifies againstmodifiers[0].amountInCents(200 cents for US) instead ofmodifiers[1].amountInCents(300 cents for CA).This will either cause the test to fail or validate the wrong reward amount.
Apply this diff to fix the mismatch:
- test("track a lead and verify the reward based on the partner.country (US)", async () => { + test("track a lead and verify the reward based on the partner.country (CA)", async () => { const clickResponse = await http.post<{ clickId: string }>({ path: "/track/click", headers: E2E_TRACK_CLICK_HEADERS, body: { domain: "getacme.link", key: "marvin", }, }); const trackedClickId = clickResponse.data.clickId; const customer = randomCustomer(); const response = await http.post<TrackLeadResponse>({ path: "/track/lead", body: { clickId: trackedClickId, customerId: customer.externalId, eventName: "Signup", customerName: customer.name, customerEmail: customer.email, customerAvatar: customer.avatar, }, }); expectValidLeadResponse({ response, customer: customer, clickId: trackedClickId, }); await new Promise((resolve) => setTimeout(resolve, 2000)); await verifyCommission({ http, customerExternalId: customer.externalId, - expectedEarnings: E2E_LEAD_REWARD.modifiers[0].amountInCents, + expectedEarnings: E2E_LEAD_REWARD.modifiers[1].amountInCents, }); });Alternatively, if the intent is to test US, use partner key
"derek"instead:test("track a lead and verify the reward based on the partner.country (US)", async () => { const clickResponse = await http.post<{ clickId: string }>({ path: "/track/click", headers: E2E_TRACK_CLICK_HEADERS, body: { domain: "getacme.link", - key: "marvin", + key: "derek", }, });
🧹 Nitpick comments (2)
apps/web/tests/utils/verify-commission.ts (1)
24-24: Consider making the delay configurable.The hard-coded 2-second delay may cause flaky tests if the system is slow, or waste time if the system is fast. Consider making it configurable or using a retry mechanism with polling.
Example with configurable delay:
+ const COMMISSION_PROCESSING_DELAY = parseInt(process.env.E2E_COMMISSION_DELAY || "2000"); + export const verifyCommission = async ({ http, customerExternalId, expectedEarnings, }: { http: HttpClient; customerExternalId: string; expectedEarnings: number; }) => { // Find the customer first const { data: customers } = await http.get<Customer[]>({ path: "/customers", query: { externalId: customerExternalId, }, }); const customer = customers[0]; - await new Promise((resolve) => setTimeout(resolve, 2000)); + await new Promise((resolve) => setTimeout(resolve, COMMISSION_PROCESSING_DELAY));apps/web/tests/rewards/index.test.ts (1)
16-128: Reduce test duplication using parameterized tests.The three test cases follow an identical structure with only the partner data and expected earnings changing. Consider refactoring using
test.eachto reduce duplication and improve maintainability.Apply this refactor:
- test("when {Partner} {Country} is {US}", async () => { - // Track the click - const clickResponse = await http.post<{ clickId: string }>({ - path: "/track/click", - headers: E2E_TRACK_CLICK_HEADERS, - body: { - ...E2E_PARTNERS[0].shortLink, - }, - }); - - expect(clickResponse.status).toEqual(200); - - const clickId = clickResponse.data.clickId; - const customer = randomCustomer(); - - // Track the lead - const trackLeadResponse = await http.post<TrackLeadResponse>({ - path: "/track/lead", - body: { - clickId, - eventName: "Signup", - customerExternalId: customer.externalId, - customerName: customer.name, - customerEmail: customer.email, - customerAvatar: customer.avatar, - }, - }); - - expect(trackLeadResponse.status).toEqual(200); - - // Verify the commission - await verifyCommission({ - http, - customerExternalId: customer.externalId, - expectedEarnings: E2E_LEAD_REWARD.modifiers[0].amountInCents, - }); - }); - - test("when {Partner} {Country} is {CA}", async () => { - // Track the click - const clickResponse = await http.post<{ clickId: string }>({ - path: "/track/click", - headers: E2E_TRACK_CLICK_HEADERS, - body: { - ...E2E_PARTNERS[1].shortLink, - }, - }); - - expect(clickResponse.status).toEqual(200); - - const clickId = clickResponse.data.clickId; - const customer = randomCustomer(); - - // Track the lead - const trackLeadResponse = await http.post<TrackLeadResponse>({ - path: "/track/lead", - body: { - clickId, - eventName: "Signup", - customerExternalId: customer.externalId, - customerName: customer.name, - customerEmail: customer.email, - customerAvatar: customer.avatar, - }, - }); - - expect(trackLeadResponse.status).toEqual(200); - - // Verify the commission - await verifyCommission({ - http, - customerExternalId: customer.externalId, - expectedEarnings: E2E_LEAD_REWARD.modifiers[1].amountInCents, - }); - }); - - test("when {Partner} {Country} is not {US} or {CA}", async () => { - // Track the click - const clickResponse = await http.post<{ clickId: string }>({ - path: "/track/click", - headers: E2E_TRACK_CLICK_HEADERS, - body: { - ...E2E_PARTNERS[2].shortLink, - }, - }); - - expect(clickResponse.status).toEqual(200); - - const clickId = clickResponse.data.clickId; - const customer = randomCustomer(); - - // Track the lead - const trackLeadResponse = await http.post<TrackLeadResponse>({ - path: "/track/lead", - body: { - clickId, - eventName: "Signup", - customerExternalId: customer.externalId, - customerName: customer.name, - customerEmail: customer.email, - customerAvatar: customer.avatar, - }, - }); - - expect(trackLeadResponse.status).toEqual(200); - - // Verify the commission - await verifyCommission({ - http, - customerExternalId: customer.externalId, - expectedEarnings: E2E_LEAD_REWARD.amountInCents, - }); - }); + test.each([ + { + country: "US", + partnerIndex: 0, + expectedEarnings: E2E_LEAD_REWARD.modifiers[0].amountInCents, + }, + { + country: "CA", + partnerIndex: 1, + expectedEarnings: E2E_LEAD_REWARD.modifiers[1].amountInCents, + }, + { + country: "IN", + partnerIndex: 2, + expectedEarnings: E2E_LEAD_REWARD.amountInCents, + }, + ])("when {Partner} {Country} is {$country}", async ({ partnerIndex, expectedEarnings }) => { + // Track the click + const clickResponse = await http.post<{ clickId: string }>({ + path: "/track/click", + headers: E2E_TRACK_CLICK_HEADERS, + body: { + ...E2E_PARTNERS[partnerIndex].shortLink, + }, + }); + + expect(clickResponse.status).toEqual(200); + + const clickId = clickResponse.data.clickId; + const customer = randomCustomer(); + + // Track the lead + const trackLeadResponse = await http.post<TrackLeadResponse>({ + path: "/track/lead", + body: { + clickId, + eventName: "Signup", + customerExternalId: customer.externalId, + customerName: customer.name, + customerEmail: customer.email, + customerAvatar: customer.avatar, + }, + }); + + expect(trackLeadResponse.status).toEqual(200); + + // Verify the commission + await verifyCommission({ + http, + customerExternalId: customer.externalId, + expectedEarnings, + }); + });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/web/tests/rewards/index.test.ts(1 hunks)apps/web/tests/tracks/track-lead.test.ts(1 hunks)apps/web/tests/utils/resource.ts(3 hunks)apps/web/tests/utils/verify-commission.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web/tests/utils/verify-commission.ts (1)
apps/web/lib/types.ts (2)
Customer(416-416)CommissionResponse(433-433)
apps/web/tests/rewards/index.test.ts (4)
apps/web/tests/utils/integration.ts (1)
IntegrationHarness(14-118)apps/web/tests/utils/resource.ts (3)
E2E_TRACK_CLICK_HEADERS(10-14)E2E_PARTNERS(147-172)E2E_LEAD_REWARD(76-111)apps/web/lib/types.ts (1)
TrackLeadResponse(412-412)apps/web/tests/utils/verify-commission.ts (1)
verifyCommission(5-38)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (3)
apps/web/tests/utils/resource.ts (2)
107-107: Verify the semantic meaning ofmaxDuration: 0.The CA modifier uses
maxDuration: 0while the US modifier usesmaxDuration: null. Ensure this difference is intentional and verify whether0means "no duration limit" or "instant expiry" to prevent unexpected reward behavior.
147-172: LGTM!The E2E_PARTNERS test fixture is well-structured and provides clear country-specific test data for US, CA, and IN partners.
apps/web/tests/tracks/track-lead.test.ts (1)
1-4: LGTM!The import refactoring to use the external
verifyCommissionutility simplifies the test file and promotes code reuse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (12)
apps/web/tests/utils/verify-commission.ts (4)
32-34: Replace fixed sleep with polling + timeout to de-flake tests.A hard 2s wait may be too short or excessive. Poll /commissions with a timeout instead.
Apply this diff:
- // Small delay if necessary for async commission processing - await new Promise((resolve) => setTimeout(resolve, 2000)); + // Defer commission check; actual waiting handled by polling belowAnd replace the single GET + assertions with polling:
- const { status, data: commissions } = await http.get<CommissionResponse[]>({ - path: "/commissions", - query, - }); - - expect(status).toEqual(200); - expect(commissions).toHaveLength(1); - - const commission = commissions[0]; + // Poll until exactly one commission is available (or timeout) + const maxAttempts = 10; + const intervalMs = 500; + let commission: CommissionResponse | undefined; + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + const { status, data } = await http.get<CommissionResponse[]>({ + path: "/commissions", + query, + }); + expect(status).toEqual(200); + if (data.length === 1) { + commission = data[0]; + break; + } + await new Promise((r) => setTimeout(r, intervalMs)); + } + if (!commission) { + throw new Error( + `Timed out waiting for a single commission with query=${JSON.stringify( + query + )}` + ); + }Also applies to: 46-53
24-31: Make customer resolution deterministic and error-friendly.Strengthen the check to fail fast if multiple or zero customers are returned. Include the externalId in the error for debugging.
Apply this diff:
- expect(customers.length).toBeGreaterThan(0); - customerId = customers[0].id; + if (customers.length !== 1) { + throw new Error( + `Expected exactly 1 customer for externalId=${customerExternalId}, got ${customers.length}` + ); + } + customerId = customers[0].id;
64-69: Optional: allow tolerance for computed percentages.If earnings can be percentage-based, rounding may differ. Consider allowing a tolerance or use integer math upstream. If you keep exact equality, ensure all callers compute integers (e.g., Math.round(amount * pct / 100)).
13-19: Add types to function return for clarity.Explicitly annotate the promise:
: Promise<void>on verifyCommission for consistency with exported signature.apps/web/tests/tracks/track-sale.test.ts (4)
111-113: Centralize waiting in verifyCommission (avoid fixed sleeps in tests).If you adopt polling in verifyCommission, drop this 2s pause here to speed up and deflake the suite.
114-119: Avoid brittle index access for modifiers (use find).Accessing
modifiers[0]assumes order. Prefer finding the premium product modifier by condition.Example:
const premiumFlat = E2E_SALE_REWARD.modifiers.find( (m) => m.type === "flat" && m.conditions.some( c => c.entity === "sale" && c.attribute === "productId" && c.value === "premiumProductId" ) )!; await verifyCommission({ http, invoiceId: premiumInvoiceId, expectedAmount: response2.data.sale!.amount!, expectedEarnings: premiumFlat.amountInCents! });
121-126: Same as above for premium case.Replace
modifiers[0]with a condition-based lookup to avoid future order changes breaking tests.
296-308: Find the “large amount” modifier by rule, not index.Replace
modifiers[1]with a lookup where sale.amount > 15000 condition is present.apps/web/tests/rewards/sale-reward.test.ts (2)
31-36: Derive the 10% from the resource to prevent drift.Avoid hardcoding
0.1. Read the percentage from E2E_SALE_REWARD to keep tests aligned with data.Example:
const pct = E2E_SALE_REWARD.modifiers.find( (m) => m.type === "percentage" && m.conditions.some(c => c.entity === "customer" && c.attribute === "country" && c.value === "US") )!.amountInPercentage!; const expected = Math.round((saleAmount * pct) / 100); await verifyCommission({ http, invoiceId, expectedEarnings: expected });
39-65: Clarify reward semantics before unskipping.CA has a flat 50 modifier in resources, but the test expects base amount. Confirm whether modifiers override, add to, or replace base, then update assertion and unskip accordingly.
apps/web/tests/rewards/lead-reward.test.ts (1)
46-51: LGTM; will benefit from helper polling.The flow is sound. Once verifyCommission polls, no extra waits needed here.
apps/web/tests/utils/resource.ts (1)
47-60: Export named modifier handles to avoid index coupling.Tests refer to
modifiers[0/1]. Provide named exports (e.g., E2E_SALE_REWARD_PREMIUM_PRODUCT_FLAT, E2E_SALE_REWARD_LARGE_AMOUNT_FLAT, E2E_SALE_REWARD_US_PERCENT, E2E_SALE_REWARD_CA_FLAT) or attach keyed properties to E2E_SALE_REWARD to stabilize references.Also applies to: 62-74, 75-88, 89-102
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/web/tests/rewards/lead-reward.test.ts(1 hunks)apps/web/tests/rewards/sale-reward.test.ts(1 hunks)apps/web/tests/tracks/track-lead.test.ts(1 hunks)apps/web/tests/tracks/track-sale.test.ts(3 hunks)apps/web/tests/utils/resource.ts(3 hunks)apps/web/tests/utils/verify-commission.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
apps/web/tests/rewards/sale-reward.test.ts (4)
apps/web/tests/utils/integration.ts (1)
IntegrationHarness(14-118)apps/web/lib/types.ts (1)
TrackSaleResponse(414-414)apps/web/tests/utils/resource.ts (2)
E2E_CUSTOMERS(182-193)E2E_SALE_REWARD(41-104)apps/web/tests/utils/verify-commission.ts (1)
verifyCommission(13-69)
apps/web/tests/utils/verify-commission.ts (1)
apps/web/lib/types.ts (2)
Customer(416-416)CommissionResponse(433-433)
apps/web/tests/tracks/track-sale.test.ts (2)
apps/web/tests/utils/verify-commission.ts (1)
verifyCommission(13-69)apps/web/tests/utils/resource.ts (1)
E2E_SALE_REWARD(41-104)
apps/web/tests/rewards/lead-reward.test.ts (4)
apps/web/tests/utils/integration.ts (1)
IntegrationHarness(14-118)apps/web/tests/utils/resource.ts (3)
E2E_TRACK_CLICK_HEADERS(10-14)E2E_PARTNERS(163-180)E2E_LEAD_REWARD(106-127)apps/web/lib/types.ts (1)
TrackLeadResponse(412-412)apps/web/tests/utils/verify-commission.ts (1)
verifyCommission(13-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (4)
apps/web/tests/tracks/track-lead.test.ts (1)
1-1: Import cleanup looks good.Matches the file’s current usage and removes unused types.
Also applies to: 3-3
apps/web/tests/utils/resource.ts (2)
182-193: LGTM on E2E_CUSTOMERS.Country codes align with reward conditions; as const enforces immutability.
152-157: The E2E_PARTNER constant is actively used throughout the codebase.The verification shows E2E_PARTNER is imported and referenced in multiple test files:
apps/web/tests/partners/list-partners.test.ts(4 usages)apps/web/tests/partners/resource.ts(2 usages)apps/web/tests/partners/upsert-partner-link.test.ts(2 usages)apps/web/tests/partners/analytics.test.ts(1 usage)apps/web/tests/partners/create-partner-link.test.ts(1 usage)apps/web/tests/embed-tokens/referrals.test.ts(1 usage)The singular E2E_PARTNER and plural E2E_PARTNERS serve distinct purposes: the former represents a single partner entity used in tests, while the latter is an array of multiple partners. The naming is clear and intentional.
Likely an incorrect or invalid review comment.
apps/web/tests/utils/verify-commission.ts (1)
13-19: All verifyCommission calls pass at least one of invoiceId or customerExternalId.Verified across all 8 usages in the test suite (track-sale.test.ts, sale-reward.test.ts, lead-reward.test.ts). No issues found.
Summary by CodeRabbit
Tests
Chores