Skip to content

E2E tests for all workflow combinations#3456

Open
pepeladeira wants to merge 10 commits intomainfrom
feat/workflow-e2e-tests
Open

E2E tests for all workflow combinations#3456
pepeladeira wants to merge 10 commits intomainfrom
feat/workflow-e2e-tests

Conversation

@pepeladeira
Copy link
Collaborator

@pepeladeira pepeladeira commented Feb 11, 2026

Add E2E tests covering all three workflow types (AwardBounty, SendCampaign, MoveGroup) across both trigger mechanisms (partnerMetricsUpdated, partnerEnrolled).

Summary by CodeRabbit

  • Tests

    • Added comprehensive end-to-end coverage for AwardBounty, MoveGroup, and SendCampaign workflows with helpers to simulate activity, prevent duplicates, and cleanup.
    • Added polling utilities to verify bounty submissions, campaign sends, and partner group moves.
    • Updated test utilities to use a dedicated E2E Prisma client and required E2E_DATABASE_URL; simplified commission verifier to use Prisma (removed HTTP arg).
  • Chores

    • Updated development fixture data for workspaces, users, groups, partners, programs, and integrations.

@vercel
Copy link
Contributor

vercel bot commented Feb 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dub Ready Ready Preview Feb 13, 2026 10:15am

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Adds comprehensive end-to-end workflow tests (AwardBounty, MoveGroup, SendCampaign), new Prisma-backed test utilities and E2E Prisma client, updates dev seed data, and refactors test helpers to use Prisma instead of HTTP. No runtime/public API changes.

Changes

Cohort / File(s) Summary
Test Data
apps/web/scripts/dev/data.json
Replaced and updated development seed records: workspace/defaultProgram IDs, users, groups, partners, emails, domains/URLs and related mappings. Data-only changes.
Workflow Test Suites
apps/web/tests/workflows/award-bounty-workflow.test.ts, apps/web/tests/workflows/move-group-workflow.test.ts, apps/web/tests/workflows/send-campaign-workflow.test.ts
Added end-to-end tests covering workflow creation, trigger/condition evaluation, disabled workflows, deduplication, cron executions, enrollment scenarios, and cleanup; tests exercise HTTP endpoints and assert Prisma state.
Workflow Test Utilities
apps/web/tests/workflows/utils/verify-bounty-submission.ts, apps/web/tests/workflows/utils/verify-campaign-sent.ts, apps/web/tests/workflows/utils/verify-partner-group-move.ts
New polling helpers that query Prisma for bountySubmission, notificationEmail, and programEnrollment.groupId with timeouts and assertions; exported input types added.
E2E Test Infra
apps/web/tests/utils/env.ts, apps/web/tests/utils/prisma.ts
Added required env E2E_DATABASE_URL and a dedicated PrismaClient export configured with that datasource and omitting user.passwordHash for E2E tests.
Verify Commission Refactor
apps/web/tests/utils/verify-commission.ts, apps/web/tests/rewards/..., apps/web/tests/tracks/...
Replaced HTTP-based customer/commission polling with Prisma queries; removed http param from verifyCommission calls and updated its signature/return value; adjusted dependent tests accordingly.
Misc / Manifest
manifest_file, package.json
Metadata/packages touched (reported lines) to support new tests; no public API changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • devkiran
  • steven-tey

Poem

🐰
I hopped through tests both wide and deep,
Chased leads and bounties before I’d sleep.
Groups moved, campaigns took flight,
Prisma hummed through day and night.
Seed data refreshed — the repo cheered!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'E2E tests for all workflow combinations' accurately summarizes the main changes, which add comprehensive end-to-end test suites for three workflow types (AwardBounty, SendCampaign, MoveGroup) across different trigger mechanisms.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/workflow-e2e-tests

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
apps/web/tests/utils/verify-commission.ts (1)

37-42: findFirst without orderBy may match a stale commission; empty where clause is also possible.

Two concerns with this query:

  1. No orderBy: Without ordering, findFirst returns an arbitrary matching record. If the test DB has residual commissions from prior runs matching the same customerId/invoiceId, this could match a stale record and produce flaky tests or false positives.

  2. Empty where clause: Both customerId and invoiceId can be undefined, making the effective query where: {} — which matches any commission in the database.

Proposed fix
     const commission = await prisma.commission.findFirst({
       where: {
         ...(customerId && { customerId }),
         ...(invoiceId && { invoiceId }),
       },
+      orderBy: { createdAt: "desc" },
     });

For the empty-where guard, consider either requiring at least one identifier at the type level or adding a runtime check:

+  if (!customerId && !invoiceId) {
+    throw new Error(
+      "verifyCommission requires at least one of customerExternalId or invoiceId",
+    );
+  }
+
   while (Date.now() - startTime < TIMEOUT_MS) {

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/web/tests/workflows/send-campaign-workflow.test.ts`:
- Around line 274-349: The test "Cron processes eligible partner enrollment"
incorrectly sets the partner enrollment time to 18 hours ago
(prisma.programEnrollment.update with subHours) while the campaign trigger
expects partnerEnrolledDays >= 1, and it never asserts that an email was sent;
update the test to set a createdAt >24 hours ago (e.g., use subHours(..., 25) or
subDays(..., 2)) so the triggerCondition will be met, then add a post-cron
verification call such as verifyCampaignSent({ campaignId, partnerId: partner.id
}) after calling callCronWorkflow to assert the campaign email was actually
sent; ensure you reference the workflow from prisma.workflow.findFirst and keep
the existing cleanup onTestFinished.
- Around line 488-498: The test sets the enrollment time with
prisma.programEnrollment.update to subHours(new Date(), 18), which may be less
than the required partnerEnrolledDays >= 1 and cause the cron to skip sending
(making the dedup assertion vacuous); update the test to set the enrollment
earlier (e.g., subDays(new Date(), 1) or subHours(new Date(), 25)) so the
partner meets the 1-day threshold before pre-creating the notificationEmail and
running the dedup scenario involving E2E_PROGRAM and the existing
notificationEmail record.
🧹 Nitpick comments (6)
apps/web/tests/workflows/utils/verify-campaign-sent.ts (1)

27-32: Assertions are tautological given the query's where clause.

The findFirst query already filters by campaignId, type: "Campaign", and partnerId, so asserting those same fields on the result will always pass. The only meaningful check is that the record exists (which the if (emailSent) guard already covers). Consider removing lines 28–31 to reduce noise, or keep them intentionally as documentation of the expected shape.

Simplified return
     if (emailSent) {
-      expect(emailSent).toBeDefined();
-      expect(emailSent.type).toBe("Campaign");
-      expect(emailSent.campaignId).toBe(campaignId);
-      expect(emailSent.partnerId).toBe(partnerId);
       return emailSent;
     }
apps/web/tests/workflows/utils/verify-bounty-submission.ts (1)

22-22: Consider typing lastSubmission instead of any.

Using the Prisma-generated type (e.g., BountySubmission | null) would give you compile-time safety on the error-message field access (line 59: lastSubmission.status, lastSubmission.performanceCount).

Type-safe alternative
+import { BountySubmission } from "@dub/prisma/client";
 ...
-  let lastSubmission: any = null;
+  let lastSubmission: BountySubmission | null = null;
apps/web/tests/workflows/award-bounty-workflow.test.ts (2)

10-42: trackLeads is duplicated verbatim in move-group-workflow.test.ts.

This helper appears identically in both award-bounty-workflow.test.ts (here) and move-group-workflow.test.ts (lines 12–44). Extract it to a shared utility under tests/workflows/utils/ to keep both files DRY and ensure future changes (e.g., adding delays, changing the tracking payload) are applied consistently.

Extract to shared utility

Create apps/web/tests/workflows/utils/track-leads.ts:

import { expect } from "vitest";
import { E2E_TRACK_CLICK_HEADERS } from "../../utils/resource";

export async function trackLeads(
  http: any,
  partnerLink: { domain: string; key: string },
  count: number,
) {
  for (let i = 0; i < count; i++) {
    const { status: clickStatus, data: clickData } = await http.post({
      path: "/track/click",
      headers: E2E_TRACK_CLICK_HEADERS,
      body: {
        domain: partnerLink.domain,
        key: partnerLink.key,
      },
    });

    expect(clickStatus).toEqual(200);
    expect(clickData.clickId).toBeDefined();

    const { status: leadStatus } = await http.post({
      path: "/track/lead",
      body: {
        clickId: clickData.clickId,
        eventName: `Signup-${i}-${Date.now()}`,
        customerExternalId: `e2e-customer-${i}-${Date.now()}`,
        customerEmail: `customer${i}@example.com`,
      },
    });

    expect(leadStatus).toEqual(200);

    await new Promise((resolve) => setTimeout(resolve, 200));
  }
}

104-160: Hardcoded 10-second sleep is fragile.

Line 147 uses a fixed 10-second wait to give the workflow time to not execute. If the system is slow, the workflow might still be processing, causing a false pass (draft found before workflow completes). If it's fast, 10 seconds is wasted CI time. Consider polling briefly (e.g., 2–3 iterations) and asserting draft status holds, or document why 10 seconds is a safe upper bound.

apps/web/tests/workflows/move-group-workflow.test.ts (2)

170-176: sourceGroup selection via existingGroups[0] is order-dependent and fragile.

Multiple tests (lines 176, 262, 336, 420) grab existingGroups[0] as the source group. The order of results from GET /groups may not be deterministic, and if another test creates a group before this one runs (even with sequential execution), the first element could change. Consider filtering by the known default group ID or slug instead:

Pin to the known default group
-    const sourceGroup = existingGroups[0];
+    const sourceGroup = existingGroups.find(g => g.slug === "default");
+    expect(sourceGroup).toBeDefined();

492-540: AND-operator test validates configuration but not execution.

This test verifies the workflow's triggerConditions are stored correctly with two rules, but doesn't test that both conditions must be satisfied for the move to occur (i.e., no execution test with one condition met and one not). Consider adding a follow-up test that exercises the AND semantics end-to-end.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🤖 Fix all issues with AI agents
In `@apps/web/tests/workflows/move-group-workflow.test.ts`:
- Around line 394-399: The test currently silently returns when enrollment is
falsy which masks E2E seed/configuration failures; replace the early-return
block that checks enrollment (the conditional using enrollment, partner.id and
programId) with an explicit assertion such as expect(enrollment).not.toBeNull()
(or expect(enrollment).toBeDefined()) so the test fails loudly if enrollment is
missing, or if the intent is truly to skip, convert the test to use test.skip
with a clear reason referencing the missing enrollment.
🧹 Nitpick comments (2)
apps/web/tests/workflows/utils/verify-bounty-submission.ts (1)

34-52: Redundant assertions inside the already-matched condition block.

Lines 40–46 re-assert conditions that were already verified by the if guard on lines 34–39. These expect calls will always pass at this point. They add noise without catching new failures. Consider removing them and keeping only the completedAt assertion (line 48–50), which checks something not in the guard.

♻️ Suggested simplification
     if (
       submission &&
       submission.status === expectedStatus &&
       (minPerformanceCount === undefined ||
         (submission.performanceCount ?? 0) >= minPerformanceCount)
     ) {
-      expect(submission.status).toBe(expectedStatus);
-
-      if (minPerformanceCount !== undefined) {
-        expect(submission.performanceCount).toBeGreaterThanOrEqual(
-          minPerformanceCount,
-        );
-      }
-
       if (expectedStatus === "submitted") {
         expect(submission.completedAt).not.toBeNull();
       }

       return submission;
     }
apps/web/tests/workflows/move-group-workflow.test.ts (1)

12-13: Consider typing the http parameter.

http: any loses type safety. If the IntegrationHarness exposes a type for its HTTP client, using it here would catch mistakes at compile time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant