Skip to content

feat(tests): add integration tests for triggers, webhooks, credentials, and personas#551

Merged
zbigniewsobiecki merged 4 commits intodevfrom
feature/integration-tests-699f2e4
Feb 25, 2026
Merged

feat(tests): add integration tests for triggers, webhooks, credentials, and personas#551
zbigniewsobiecki merged 4 commits intodevfrom
feature/integration-tests-699f2e4

Conversation

@aaight
Copy link
Copy Markdown
Collaborator

@aaight aaight commented Feb 25, 2026

Summary

Adds 6 new integration test files covering key areas of the CASCADE platform that lacked integration test coverage. These tests complement the existing 9 DB repository tests and bring the total integration test count from 155 to 267.

  • tests/integration/trigger-registry.test.ts (17 tests): TriggerRegistry core operations, Trello card-moved/label triggers with real DB config, and config toggle behavior
  • tests/integration/webhook-logging.test.ts (21 tests): WebhookLog CRUD, filtering, stats aggregation, and pruning
  • tests/integration/multi-provider-credentials.test.ts (14 tests): Per-project credential isolation, PM/SCM isolation, dual-persona token resolution, resolveAllIntegrationCredentials, and encryption round-trips
  • tests/integration/db/repositories-edge-cases.test.ts (22 tests): 4-level agent config cascade, credential CRUD, upsertProjectIntegration, updateProjectIntegrationTriggers deep merge, cascade deletes, and upsertCascadeDefaults
  • tests/integration/github-personas.test.ts (26 tests): Persona token resolution from DB, isCascadeBot() with [bot] suffix variants, getPersonaForLogin(), getPersonaForAgentType(), PRReviewSubmittedTrigger loop prevention, and ReviewRequestedTrigger modes
  • tests/integration/pm-provider-switching.test.ts (12 tests): createPMProvider provider type detection, Trello→JIRA provider switching, Trello and JIRA trigger dispatch with real DB config, and pmRegistry operations

Key decisions

  • Used resolveIntegrationCredential and resolveOrgCredential (DB-direct) instead of getIntegrationCredential/getOrgCredential which check process.env first, to avoid env var interference in tests
  • Used assertFound<T>() helper instead of non-null assertions (!) to comply with Biome's noNonNullAssertion rule
  • Tested PM/SCM category isolation instead of Trello/JIRA isolation in the same PM category (unique constraint uq_project_integrations_project_category prevents two PM integrations per project)
  • Used unique envVarKeys (e.g., MY_UNIQUE_CUSTOM_TEST_KEY_XYZ) to avoid collision with environment variables present in the test environment

Test plan

  • All 267 integration tests pass (npm run test:integration)
  • Lint passes (npm run lint)
  • TypeScript typecheck passes (npm run typecheck)

Card: https://trello.com/c/699f2e4ad32b60324c8a9d01

🤖 Generated with Claude Code

@aaight
Copy link
Copy Markdown
Collaborator Author

aaight commented Feb 25, 2026

🤖 Just a sec, looking into that PR for you


Progress: [██░░░░░░░░] 16% (iteration 11/70)

🔧 CI Fix Update (1 min)

I've identified that the CI failures are due to a 403 Forbidden error from the npm registry when attempting to download the zod package, which is an infrastructure issue rather than a code problem. I've confirmed this by reviewing the CI logs for both failed runs. I'm now verifying that the project builds and tests successfully locally to ensure there are no underlying code issues.

Last updated: iteration 11 · respond-to-ci

@aaight
Copy link
Copy Markdown
Collaborator Author

aaight commented Feb 25, 2026

CI Failure Analysis

Root Cause

Both failing CI checks (integration-tests and lint-and-test) failed at the npm ci step with a transient npm registry error:

npm error 403 403 Forbidden - GET https://registry.npmjs.org/zod

This is an npm registry infrastructure issue, not related to the code changes in this PR.

Verification

All checks pass locally:

  • Lint (Biome): 665 files checked, no issues
  • TypeScript typecheck: No errors
  • Unit tests: 189 test files, 3259 tests passed

Action Taken

  • Re-triggered the failed CI checks via gh run rerun --failed
  • No code changes needed — the PR code is correct

@nhopeatall
Copy link
Copy Markdown
Collaborator

nhopeatall commented Feb 25, 2026

🤖 Just a sec, looking into PR #551 now


Progress: [███░░░░░░░] 34% (iteration 24/70)

🔍 Code Review Update (1 min)

I've completed a thorough review of the provided code, focusing on identifying unused imports and ensuring proper test cleanup. I've noted that getDb, eq, and organizations are imported but unused in repositories-edge-cases.test.ts, and the assertFound helper is duplicated across multiple test files. I've also confirmed that vitest.config.ts is correctly configured with unstubEnvs: true and clearMocks: true to handle vi.stubEnv and mock cleanup globally. I'm now ready to approve the pull request.

Last updated: iteration 24 · review

Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

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

Summary

LGTM — Well-structured integration test suite covering important areas (triggers, webhooks, credentials, personas, PM provider switching, and DB edge cases). Tests are thorough, properly isolated via truncateAll() in beforeEach, and CI is green.

Should Fix (non-blocking)

  • Unused imports in repositories-edge-cases.test.ts: eq (from drizzle-orm), getDb, and organizations (from schema) are imported but never used. These are likely leftover from an earlier iteration and will be flagged by stricter lint rules if enabled. Not blocking since lint CI passes.

  • assertFound<T>() duplicated across 3 files: The identical helper function is copy-pasted in trigger-registry.test.ts, github-personas.test.ts, and pm-provider-switching.test.ts. Consider extracting it to tests/integration/helpers/ alongside the existing db.ts and seed.ts helpers, especially since the PR description mentions it as a deliberate pattern for Biome compliance.

import { eq } from 'drizzle-orm';
import { beforeEach, describe, expect, it } from 'vitest';
import { getDb } from '../../../src/db/client.js';
import { loadConfigFromDb } from '../../../src/db/repositories/configRepository.js';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: getDb, eq (line 8), and organizations (line 31) are imported but never used in this file. These are likely leftovers from development.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — removed unused eq, getDb, and organizations imports in e7c6f7e.

} from '../../src/db/repositories/configRepository.js';

/** Assert that a value is defined (not null/undefined), throwing if not. */
function assertFound<T>(value: T | null | undefined, msg = 'Expected value to be defined'): T {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: assertFound is duplicated identically in 3 test files (here, github-personas.test.ts:30, and pm-provider-switching.test.ts:26). Consider extracting it to tests/integration/helpers/ alongside seed.ts and db.ts.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — extracted assertFound<T>() to tests/integration/helpers/assert.ts and updated all 3 test files to import from the shared module in e7c6f7e.

@zbigniewsobiecki
Copy link
Copy Markdown
Member

@aaight address code review concerns

@aaight
Copy link
Copy Markdown
Collaborator Author

aaight commented Feb 25, 2026

Request Addressed

What was asked

Address code review concerns from the PR review.

Changes Made

  • Removed unused imports in repositories-edge-cases.test.ts: removed eq (drizzle-orm), getDb (db/client), and organizations (db/schema) that were leftover from development
  • Extracted shared assertFound<T>() helper from 3 test files into tests/integration/helpers/assert.ts, alongside the existing db.ts and seed.ts helpers
  • Updated trigger-registry.test.ts, github-personas.test.ts, and pm-provider-switching.test.ts to import from the shared module

Verification

  • Biome lint passes (0 issues)
  • TypeScript typecheck passes (0 errors)
  • All unit tests passing (pre-push hook)
  • Commit: e7c6f7e

Remove unused `eq`, `getDb`, and `organizations` imports from
repositories-edge-cases.test.ts. Extract duplicated `assertFound<T>()`
helper from 3 test files into tests/integration/helpers/assert.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nhopeatall
Copy link
Copy Markdown
Collaborator

nhopeatall commented Feb 25, 2026

🤖 On it — checking the integration tests for PR 551


Progress: [███░░░░░░░] 29% (iteration 20/70)

🔍 Code Review Update (1 min)

I've completed my review of PR #551, focusing on the integration tests for triggers. I examined existing integration test patterns and compared the new webhook-logging test with webhookLogsRepository.test.ts and credentialResolution.test.ts to ensure no overlap and proper coverage. I've approved the PR, confirming the integration tests are well-structured and correct.

Last updated: iteration 20 · review

Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

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

Summary

LGTM — Well-structured integration tests that meaningfully extend coverage across triggers, webhooks, credentials, and personas. Tests follow established patterns (seed helpers, truncateAll, beforeEach isolation) and CI is green.

Observations

Minor duplication with existing tests (not blocking)

The new webhook-logging.test.ts has significant overlap with the existing tests/integration/db/webhookLogsRepository.test.ts — both test insertWebhookLog, listWebhookLogs filtering/pagination, pruneWebhookLogs, getWebhookLogStats, and getWebhookLogById. The new file adds a few extra cases (per-source logs for GitHub/JIRA, bodyRaw, seedWebhookLog helper) but ~70% of the test surface is duplicated. Similarly, some credential resolution tests overlap with credentialResolution.test.ts. This isn't harmful — it just adds execution time without proportional coverage gain. Could be consolidated in a future cleanup pass.

Test quality

  • Good use of assertFound<T>() helper to avoid non-null assertions — clean alternative to ! that complies with Biome rules
  • vi.stubEnv() for encryption tests is the right approach (scoped to test, auto-restored)
  • Dynamic import() in encryption tests is a reasonable workaround to pick up the stubbed env var
  • The pm-provider-switching.test.ts "defaults to Trello" test (line ~140) manually reimplements the default logic instead of calling createPMProvider — it's technically testing its own code rather than the actual registry, but the intent is documented in comments and it's a minor observation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

@aaight The new webhook-logging.test.ts has significant overlap with the existing tests/integration/db/webhookLogsRepository.test.ts — both test insertWebhookLog, listWebhookLogs filtering/pagination, pruneWebhookLogs, getWebhookLogStats, and getWebhookLogById. The new file adds a few extra cases (per-source logs for GitHub/JIRA, bodyRaw, seedWebhookLog helper) but ~70% of the test surface is duplicated. Similarly, some credential resolution tests overlap with credentialResolution.test.ts. This isn't harmful — it just adds execution time without proportional coverage gain. Could be consolidated

@aaight
Copy link
Copy Markdown
Collaborator Author

aaight commented Feb 25, 2026

🤔 Let me dig into that test duplication issue


Progress: [██████░░░░] 57% (iteration 40/70)

💬 PR Comment Response Update (4 min)

I've successfully consolidated duplicate integration tests, removing 449 lines of code and adding 17 lines for file headers/comments. All unit tests and pre-commit hooks passed. I've pushed the changes to the repository and am now updating the PR comments to reflect the completed task.

Last updated: iteration 40 · respond-to-pr-comment

Remove ~70% duplicated test surface from webhook-logging.test.ts and
multi-provider-credentials.test.ts that overlapped with existing
webhookLogsRepository.test.ts, credentialsRepository.test.ts, and
credentialResolution.test.ts. Kept only the unique value-add tests:
- webhook-logging: per-source GitHub/JIRA logs, bodyRaw, project resolution
- multi-provider-credentials: multi-project isolation, PM/SCM isolation,
  dual-persona tokens, 3-project cross-contamination checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aaight
Copy link
Copy Markdown
Collaborator Author

aaight commented Feb 25, 2026

Consolidated in e28342a. Removed ~70% of duplicated test surface (449 lines removed):

webhook-logging.test.ts (21 → 6 tests): Removed all tests already covered by tests/integration/db/webhookLogsRepository.test.ts (insertWebhookLog basics, listWebhookLogs filtering/pagination, getWebhookLogById, getWebhookLogStats, pruneWebhookLogs). Kept only the unique value-adds: per-source GitHub/JIRA log storage, bodyRaw field, project resolution recording, and seedWebhookLog helper.

multi-provider-credentials.test.ts (14 → 4 tests): Removed all tests already covered by credentialsRepository.test.ts and credentialResolution.test.ts (resolveIntegrationCredential basics, resolveAllIntegrationCredentials, resolveOrgCredential, encryption round-trips, agent-type-to-persona mapping). Kept only the unique value-adds: multi-project credential isolation (2-project and 3-project), PM/SCM category isolation, and dual-persona token resolution.

@nhopeatall
Copy link
Copy Markdown
Collaborator

nhopeatall commented Feb 25, 2026

🤖 On it — checking the integration tests for PR 551


Progress: [████░░░░░░] 40% (iteration 28/70)

🔍 Code Review Update (review)

[████░░░░░░] 40% (iteration 28/70)

Last updated: iteration 28 · review

Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

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

Summary

LGTM — Well-structured integration test suite that covers important untested areas (triggers, credentials, personas, PM switching, webhook logging) with real DB-backed configurations. Tests are passing, CI is green, and the code follows existing patterns.

Notes

A few minor observations (not blocking):

  1. repositories-edge-cases.test.ts — Some overlap with existing tests: The "organization settings" and "cascade defaults upsert" sections partially duplicate what's already tested in settingsRepository.test.ts (updateOrganization, getOrganization, upsertCascadeDefaults). The edge-case framing (e.g., testing via loadConfigFromDb rather than getCascadeDefaults) adds value, but worth noting the overlap.

  2. pm-provider-switching.test.ts:140-152 — "defaults to Trello" test doesn't exercise createPMProvider: The test manually replicates the pm.type ?? 'trello' logic inline rather than calling createPMProvider() or pmRegistry.createProvider(). This makes the test a tautology — it's testing its own implementation rather than the production code. A real integration test would construct a minimal ProjectConfig and pass it to createPMProvider().

  3. github-personas.test.ts:289-304 & pm-provider-switching.test.ts:315-329 — Conditional assertions: Several tests use if (trigger.matches(ctx)) { ... } else { expect(trigger.matches(ctx)).toBe(false); } which always passes regardless of whether matches() returns true or false. This weakens the assertion — if the behavior changes, the test silently takes the other branch rather than failing. In cases where you expect a specific outcome, assert it directly.

  4. assertFound helper is clean: Good approach to comply with Biome's noNonNullAssertion rule while maintaining type narrowing. The error messages could be more specific (e.g., assertFound(project, 'Project not found for board-123')) but the default is fine for test code.

Overall, the test coverage additions are valuable and well-organized. The resolveIntegrationCredential usage (DB-direct, bypassing env vars) is a smart choice for test isolation, and the credential isolation/cross-contamination tests cover important security-relevant behavior.

@zbigniewsobiecki
Copy link
Copy Markdown
Member

@aaight address the minor observations

@aaight
Copy link
Copy Markdown
Collaborator Author

aaight commented Feb 25, 2026

Request Addressed

What was asked

Address the minor observations from the code review.

Changes Made

  • pm-provider-switching.test.ts: Replaced manual pm.type ?? 'trello' reimplementation with actual createPMProvider() call — test now exercises production code instead of being a tautology
  • github-personas.test.ts (2 locations): Replaced if/else conditional assertions with direct expect() calls — implementer loop prevention and non-persona reviewer tests now assert specific outcomes
  • pm-provider-switching.test.ts: Replaced conditional assertion in JIRA non-matching status test with direct expect() calls

Not changed (informational observations)

  • Overlap between repositories-edge-cases.test.ts and settingsRepository.test.ts — reviewer noted as non-blocking, edge-case framing adds value
  • assertFound error messages — reviewer confirmed defaults are fine for test code

Verification

  • All 38 integration tests passing (github-personas + pm-provider-switching)
  • Biome lint: 0 issues
  • TypeScript typecheck: 0 errors
  • Pre-commit hooks (lint + typecheck) passed
  • Pre-push hooks (unit tests) passed
  • Commit: 0c5042c

…ercise createPMProvider

- Replace if/else conditional assertions in github-personas and
  pm-provider-switching tests with direct expect() calls that fail
  if behavior changes (addresses review observation #3)
- Replace manual pm.type ?? 'trello' reimplementation with actual
  createPMProvider() call in "defaults to Trello" test (addresses
  review observation #2)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aaight
Copy link
Copy Markdown
Collaborator Author

aaight commented Feb 25, 2026

Addressed in 0c5042c. Two changes:

  1. "defaults to Trello" test now exercises createPMProvider() instead of manually reimplementing the pm.type ?? 'trello' logic inline. The test now actually calls the production code path.

  2. Replaced conditional assertions with direct expect() calls in 3 locations:

    • github-personas.test.ts: implementer loop prevention test — matches() returns true (persona check is in handle()), now asserted directly
    • github-personas.test.ts: non-persona reviewer test — same pattern, now asserts matches() === true then handle() === null
    • pm-provider-switching.test.ts: non-matching JIRA status test — matches() returns true (status matching is in handle()), now asserted directly

These changes ensure tests will actually fail if the behavior changes, rather than silently taking the other branch.

@aaight
Copy link
Copy Markdown
Collaborator Author

aaight commented Feb 25, 2026

🤖 On it — checking the CI failures for PR 551


Progress: [███░░░░░░░] 26% (iteration 18/70)

🔧 CI Fix Update (1 min)

I've identified that the CI failure was due to a transient npm error 403 during dependency installation in the integration-tests job. I've successfully triggered a re-run of the failed checks and confirmed locally that typecheck and lint pass. I'm currently monitoring the status of the re-run and will investigate the ci.yml workflow file if the issue persists.

Last updated: iteration 18 · respond-to-ci

@nhopeatall
Copy link
Copy Markdown
Collaborator

nhopeatall commented Feb 25, 2026

🤖 On it — checking the integration tests for PR 551


Progress: [████░░░░░░] 40% (iteration 28/70)

🔍 Code Review Update (1 min)
I've been reviewing the pull request, focusing on how trigger configurations are handled. I've examined several source files, including triggerConfig.ts, configRepository.ts, and schema.ts, to understand the data flow. My current focus is on clarifying the mapping between database integrations and the ProjectConfig type, specifically how github.triggers is resolved.

Last updated: iteration 28 · review

Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

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

Summary

LGTM — Well-structured integration tests covering important areas (triggers, webhooks, credentials, personas, PM provider switching). The tests use real DB-backed configs, follow existing patterns, and CI passes cleanly.

Notes

  • The assertFound<T>() helper is a clean approach to satisfy Biome's noNonNullAssertion rule while providing better error messages than !.
  • Tests correctly trace the full data flow: seed integration → DB-backed ProjectConfig (via configMapper) → trigger matches()/handle() — validating the real resolution pipeline rather than just mocking.
  • The decision to use resolveIntegrationCredential (DB-direct) instead of getIntegrationCredential (checks env first) is the right call for test isolation.
  • Minor: repositories-edge-cases.test.ts:278 uses a dynamic import() for resolveIntegrationCredential even though a static import at the top of the file would be cleaner. Not blocking.
  • Some tests in repositories-edge-cases.test.ts (getOrganization null check, updateOrganization) overlap with existing settingsRepository.test.ts, but the new file's value is in the edge cases (4-level config cascade, deep merge, cascade deletes) not the basic CRUD tests.

@zbigniewsobiecki zbigniewsobiecki merged commit 6a0bfe4 into dev Feb 25, 2026
10 of 11 checks passed
@zbigniewsobiecki zbigniewsobiecki deleted the feature/integration-tests-699f2e4 branch March 16, 2026 16:43
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.

3 participants