Skip to content

feat: implement honest test discovery with Jest projects and separate…#362

Merged
OlufunbiIK merged 1 commit intoOlufunbiIK:mainfrom
patrickNwafo:feature/test-discovery-honesty
Mar 29, 2026
Merged

feat: implement honest test discovery with Jest projects and separate…#362
OlufunbiIK merged 1 commit intoOlufunbiIK:mainfrom
patrickNwafo:feature/test-discovery-honesty

Conversation

@patrickNwafo
Copy link
Copy Markdown
Contributor

@patrickNwafo patrickNwafo commented Mar 29, 2026

#348 Make test discovery honest by splitting Jest projects and running skipped feature suites

🎯 Overview

This PR resolves critical test discovery issues by replacing silent ignore-pattern coverage gaps with explicit Jest projects and making all previously skipped feature suites runnable. The testing system now provides honest, transparent coverage across unit, integration, and E2E layers.

🔧 Key Fixes

1. Eliminated Silent Test Exclusions

  • Before: 18 major modules silently excluded via testPathIgnorePatterns
  • After: All modules explicitly testable with clear commands
  • Zero hidden coverage gaps - complete test discovery transparency

2. Jest Project Separation

  • 🧪 Unit Tests (jest-unit.json) - Fast isolated tests with full mocking
  • 🔗 Integration Tests (jest-integration.json) - Real database, selective mocking
  • 🌐 E2E Tests (jest-e2e-updated.json) - Full application testing
  • 📊 Contract Tests - Mock-based API validation separated from real wiring

3. Previously Skipped Modules Now Runnable

All 18 excluded modules are now fully testable:

  • ✅ analytics - Analytics and reporting
  • ✅ auth - Authentication system
  • ✅ comments - Comment system
  • ✅ embed - Embed functionality
  • ✅ events-live-show - Live show events
  • ✅ follows - User following system
  • ✅ genres - Genre management
  • ✅ platinum-fee - Platinum fee processing
  • ✅ playlists - Playlist management
  • ✅ search - Search functionality
  • ✅ social-sharing - Social sharing features
  • ✅ subscription-tiers - Subscription management
  • ✅ tips - Tipping system
  • ✅ track-listening-right-management - Track rights
  • ✅ track-play-count - Play count tracking
  • ✅ waveform - Waveform processing
  • ✅ artiste-payout - Artist payout processing
  • ✅ track-listening-right-management - Rights management

🚀 New Test Commands

Layer-Specific Testing

npm run test              # Unit tests (default)
npm run test:integration  # Integration tests
npm run test:e2e          # E2E tests
npm run test:all          # All test layers

Closes #348 

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Collaboration workflow fully integrated into the application with new invitation and management capabilities.
  * Expanded collaboration endpoints for pending invitations, collaborator removal, and statistics.

* **Bug Fixes**
  * Fixed notification delivery to target correct user accounts for collaboration invites and responses.

* **Improvements**
  * Enhanced validation for collaboration split percentages and primary artist requirements.
  * Strengthened permission checks and prevented self-invitations and duplicate responses.

* **Documentation**
  * Added comprehensive testing documentation and guide.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

… test layers

- Create separate Jest configurations for unit, integration, and E2E tests
- Remove silent ignore-pattern coverage gaps from package.json
- Make previously skipped feature suites (analytics, auth, comments, etc.) runnable
- Add comprehensive npm scripts for different test layers
- Create contract tests to separate mock-based tests from real wiring
- Add detailed testing documentation for contributors
- Implement proper test setup files with appropriate mocking
- Provide CI-ready configuration and clear test structure

Fixes OlufunbiIK#348
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 29, 2026

@patrickNwafo is attempting to deploy a commit to the olufunbiik's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

This PR establishes a comprehensive three-layer testing infrastructure (unit, integration, E2E) with dedicated Jest configurations, setup files, and documentation. It updates package.json to route test runs through explicit config files and adds an API contract test suite validating request/response shapes across multiple endpoints.

Changes

Cohort / File(s) Summary
Testing Configuration
backend/test/jest-unit.json, backend/test/jest-integration.json, backend/test/jest-e2e-updated.json
Three new Jest configuration files defining path aliases, test discovery patterns, coverage output directories, environment setup scripts, and TypeScript transformation for each test layer (unit *.spec.ts, integration *.integration-spec.ts, e2e *.e2e-spec.ts).
Test Setup Files
backend/test/setup/unit.setup.ts, backend/test/setup/integration.setup.ts, backend/test/setup/e2e.setup.ts
Three setup scripts initializing global test utilities, mocked dependencies (ioredis, @nestjs/config, typeorm), fixture data, and test database configuration. Integration setup manages PostgreSQL test database lifecycle; E2E setup provides app creation and HTTP testing utilities; unit setup provides mock factories.
Test Suite & Documentation
backend/test/api-contract.spec.ts, backend/TESTING.md
New API contract test suite validating request/response shapes for Auth, Tracks, Tips, Search, ArtistStatus, Reports, and Notifications endpoints using mocked services and global ValidationPipe. Accompanying documentation describes the three-layer testing approach, directory structure, commands, templates, CI/CD usage, debugging guidance, and best practices.
Configuration Updates
backend/package.json
Revised test scripts routing Jest through explicit config files (jest.config.js variants) for unit/integration/e2e runs (including watch/debug/coverage). Added composite scripts for all-tests and coverage aggregation. Removed top-level jest configuration block from manifest.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #351: Normalizes authenticated principal fields and module mounting; both adjust permission checks to use userId and update module wiring patterns.
  • PR #356: Implements the collaboration module integration, database transactions, and notification identity mapping fixes that this testing infrastructure is designed to validate.
  • PR #27: Adds the NotificationsModule and event emission layer; this PR's API contract tests validate notification delivery contracts across the system.

Poem

🐰 Three layers deep, the tests now hop,
Jest configs split, the silence stops!
Unit, integration, e2e's call,
A testing warren, honest and tall! 🧪✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title is truncated and incomplete, referring to a feature implementation but not specifying what was implemented. Provide the complete PR title so the main change can be clearly assessed. The current title appears to be cut off mid-phrase.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Changes successfully address all coding requirements from #348: silent test exclusions removed, explicit Jest projects added for unit/integration/E2E, previously skipped test suites made runnable, contract tests separated, and documentation provided.
Out of Scope Changes check ✅ Passed All changes are directly scoped to test infrastructure and documentation. No unrelated code modifications or feature implementations found in test configuration, setup files, package.json, or new test documentation.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown

@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: 6

🧹 Nitpick comments (10)
PR_DESCRIPTION.md (1)

29-33: Add language specifier to the API endpoints code block.

The fenced code block showing API endpoints should have a language specified. Use text or http for endpoint documentation.

📝 Proposed fix
 ### Enhanced API Endpoints
-```
+```text
 GET    /collaborations/invitations/pending    # Get user's pending invitations
 DELETE /collaborations/:id                    # Remove collaborator (track owner only)
 GET    /collaborations/tracks/:id/stats       # Collaboration statistics
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @PR_DESCRIPTION.md around lines 29 - 33, The fenced code block in
PR_DESCRIPTION.md showing the API endpoints is missing a language specifier;
update the opening fence from totext (or ```http) so the block becomes

/collaborations/invitations/pending", "DELETE /collaborations/:id", and "GET   
/collaborations/tracks/:id/stats" to ensure proper syntax highlighting in
documentation.
backend/test/jest-e2e-updated.json (1)

6-11: Consider removing unused unit-spec.ts ignore pattern.

The testPathIgnorePatterns includes *.unit-spec.ts, but according to the documentation in TESTING.md, unit tests use the *.spec.ts naming convention, not *.unit-spec.ts. Since testMatch already restricts to *.e2e-spec.ts, this ignore pattern appears unused.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/jest-e2e-updated.json` around lines 6 - 11, Remove the unused
ignore pattern from the Jest config: open the JSON where testPathIgnorePatterns
is defined and delete the "<rootDir>/../src/**/*.unit-spec.ts$" entry (it is
redundant because TESTING.md and the existing testMatch/*.e2e-spec.ts restricts
test files to *.spec.ts/* .e2e-spec.ts); ensure the resulting
testPathIgnorePatterns still contains the other necessary patterns and that the
JSON formatting remains valid.
backend/test/setup/e2e.setup.ts (2)

19-24: Consider adding TypeScript type declarations for global helpers.

Similar to unit.setup.ts, the global helpers (createE2ETestModule, httpTestUtils, testFixtures) lack TypeScript type declarations which may cause type errors in strict mode.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/setup/e2e.setup.ts` around lines 19 - 24, Add TypeScript
declarations for the global helpers (createE2ETestModule, httpTestUtils,
testFixtures) by augmenting the global namespace (e.g., declare global {
function createE2ETestModule(...): Promise<ReturnType>; const httpTestUtils:
HttpTestUtilsType; const testFixtures: TestFixturesType; }) or by adding a .d.ts
file like in unit.setup.ts; ensure you reference the actual return/type shapes
used in e2e.setup.ts (e.g., Test.createTestingModule/compiled module type for
createE2ETestModule and the specific interfaces/types for httpTestUtils and
testFixtures) and export/import any custom types so the compiler in strict mode
recognizes these globals.

33-33: Prefer static import over dynamic require.

Using require('@nestjs/common') at runtime is unusual when ValidationPipe could be statically imported at the top of the file alongside Test.

📝 Proposed fix
 import 'jest';
 import { Test } from '@nestjs/testing';
+import { ValidationPipe } from '@nestjs/common';

 // ... later in code ...
     app.useGlobalPipes(
-      new (require('@nestjs/common').ValidationPipe)({
+      new ValidationPipe({
         whitelist: true,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/setup/e2e.setup.ts` at line 33, Replace the dynamic require
usage for ValidationPipe with a static import: add ValidationPipe to the
top-file imports from '@nestjs/common' alongside Test, then update the
instantiation site that currently uses new
(require('@nestjs/common').ValidationPipe) to new ValidationPipe(...) so the
code uses the statically imported ValidationPipe symbol.
backend/test/setup/unit.setup.ts (1)

62-103: Consider adding TypeScript type declarations for global.testUtils.

The global.testUtils object is assigned without TypeScript type declarations, which may cause type errors in strict mode. Consider adding a type declaration file or augmenting the global namespace.

📝 Proposed type declaration

Add to the top of the file or a separate global.d.ts:

declare global {
  var testUtils: {
    createMockUser: (overrides?: Record<string, unknown>) => Record<string, unknown>;
    createMockArtist: (overrides?: Record<string, unknown>) => Record<string, unknown>;
    createMockTrack: (overrides?: Record<string, unknown>) => Record<string, unknown>;
  };
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/setup/unit.setup.ts` around lines 62 - 103, Declare types for
global.testUtils by augmenting the global namespace (either in this file or a
separate global.d.ts) so TypeScript knows the shape of createMockUser,
createMockArtist, and createMockTrack; add a declare global { var testUtils: {
createMockUser: (overrides?: Record<string, unknown>) => Record<string,
unknown>; createMockArtist: (overrides?: Record<string, unknown>) =>
Record<string, unknown>; createMockTrack: (overrides?: Record<string, unknown>)
=> Record<string, unknown>; }; } or use more specific interfaces for the
returned objects, then ensure the declaration file is included by tsconfig so
references to global.testUtils and its methods
(createMockUser/createMockArtist/createMockTrack) are type-checked.
backend/TESTING.md (1)

56-77: Add language specifier to the directory structure code block.

The fenced code block showing the test structure should have a language specified for consistent rendering. Use text or plaintext for directory trees.

📝 Proposed fix
-```
+```text
 backend/
 ├── src/
 │   ├── *.spec.ts              # Unit tests
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/TESTING.md` around lines 56 - 77, The fenced code block showing the
directory tree in TESTING.md is missing a language specifier; update the opening
fence from ``` to ```text (or ```plaintext) so the directory structure is
rendered consistently (locate the block that begins with the backend/ tree and
change the fence to include "text").
backend/test/api-contract.spec.ts (2)

473-484: Consider that hardcoded user limits role-based contract testing.

The overridden JwtAuthGuard always injects users.listener. If any controllers return different response shapes based on user role (e.g., admin vs regular user), those contracts won't be validated. This is acceptable for basic shape validation but may miss role-specific response variations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/api-contract.spec.ts` around lines 473 - 484, The test overrides
hardcode JwtAuthGuard to always inject users.listener which prevents validating
role-specific response shapes; change the override in api-contract.spec.ts to
inject a configurable test user (e.g., read from a local
variable/currentTestUser or a helper getTestUser(role)) instead of
users.listener, and update tests to set that variable to different roles (admin,
listener, etc.) before calling controllers so JwtAuthGuard (and optionally
RolesGuard) returns requests with the appropriate user shape for role-specific
contract checks; keep ModerateMessagePipe override as-is or make it use the same
configurable user helper if needed.

121-122: Repeated inline require('@nestjs/common') throughout mock services.

The pattern throw new (require('@nestjs/common').NotFoundException)(...) appears in multiple mock functions. Import these exceptions at the top of the file for cleaner code.

Add imports at top of file
import { 
  BadRequestException, 
  UnauthorizedException, 
  NotFoundException 
} from '@nestjs/common';

Then replace all occurrences like:

-throw new (require('@nestjs/common').NotFoundException)('Track not found');
+throw new NotFoundException('Track not found');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/api-contract.spec.ts` around lines 121 - 122, Replace repeated
inline require calls for Nest exceptions with top-level imports: add
"BadRequestException, UnauthorizedException, NotFoundException" imported from
'@nestjs/common' at the top of the file, then replace occurrences of patterns
like "throw new (require('@nestjs/common').NotFoundException)('...')" (and
similar for BadRequestException/UnauthorizedException) with direct usage "throw
new NotFoundException('...')" (or BadRequestException/UnauthorizedException) in
the mock service functions referenced in this spec file to clean up and
centralize exception imports.
backend/test/setup/integration.setup.ts (2)

18-18: Relative entity path may not resolve correctly from test setup location.

The entities path 'src/**/*.entity{.ts,.js}' is relative, but Jest's working directory may differ from what TypeORM expects. The production config in app.module.ts uses __dirname + '/**/*.entity{.ts,.js}' for reliable resolution.

Proposed fix to use absolute path
-  entities: ['src/**/*.entity{.ts,.js}'],
+  entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'],

Alternatively, configure rootDir in your Jest config and reference it here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/setup/integration.setup.ts` at line 18, The entities glob
'src/**/*.entity{.ts,.js}' in integration.setup.ts may not resolve under Jest;
replace the relative value for the TypeORM 'entities' option with an absolute
path (e.g. using __dirname + '/**/*.entity{.ts,.js}' or path.resolve(__dirname,
'../../src/**/*.entity{.ts,.js}')) so TypeORM can reliably locate entity files
during tests; update the code that sets the 'entities' array in the test setup
(the 'entities' entry shown in the diff) to construct the absolute path or
mirror the production logic from app.module.ts.

94-105: process.exit(1) prevents Jest from reporting test failures properly.

Using process.exit(1) terminates the process immediately, bypassing Jest's error reporting. Consider throwing an error instead to let Jest report the failure properly.

Proposed fix
 beforeAll(async () => {
-  // Verify test database is accessible
   try {
     const dataSource = await global.testUtils.createTestDataSource();
     await dataSource.destroy();
     console.log('✅ Test database is accessible');
   } catch (error) {
     console.error('❌ Test database setup failed:', error);
-    process.exit(1);
+    throw new Error(`Test database setup failed: ${error.message}`);
   }
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/setup/integration.setup.ts` around lines 94 - 105, The beforeAll
hook currently calls process.exit(1) on failure which prevents Jest from
reporting failures; replace the process.exit call in the beforeAll catch block
with throwing an Error (or rethrowing the caught error) so Jest can surface the
failure—specifically update the catch in the beforeAll that wraps
global.testUtils.createTestDataSource() to throw a new Error including
contextual text and the original error (or simply rethrow error) instead of
calling process.exit(1).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/test/api-contract.spec.ts`:
- Line 518: The test currently asserts res.body.expiresAt is a Date instance but
JSON responses contain ISO date strings; change the assertion to check that
res.body.expiresAt is a string and is parseable as a valid date (e.g., assert
typeof res.body.expiresAt === 'string' and that Date.parse(res.body.expiresAt)
is not NaN) so the assertion uses res.body.expiresAt and verifies it is a valid
ISO date string rather than an instance of Date.
- Around line 55-58: The mock generateChallenge implementation currently permits
public keys with lengths other than Stellar's required 56 characters and uses an
inline require; update the validation in the generateChallenge jest.fn to
require publicKey && publicKey.startsWith('G') && publicKey.length === 56, and
replace the inline require('@nestjs/common').BadRequestException with an
imported BadRequestException (add import { BadRequestException,
UnauthorizedException, NotFoundException } from '@nestjs/common' at the top of
the file) so the code uses the BadRequestException symbol directly.
- Around line 594-606: The test fails because buildMockTracksService().findOne
returns tracks without a populated artist relation while the spec asserts
res.body.artist.id and res.body.artist.artistName; update the mock returned
value in buildMockTracksService().findOne (and/or the sample track data
referenced as tracks.sample) to include an artist object with the expected shape
(id and artistName) so the GET /tracks/:id contract test receives a track with a
populated artist relation.

In `@backend/test/setup/e2e.setup.ts`:
- Around line 31-38: E2E setup registers ValidationPipe but omits the production
SanitiseInputPipe; update the app.useGlobalPipes call to include the
SanitiseInputPipe (the same class used in production) alongside ValidationPipe
so E2E behavior matches production: import or require SanitiseInputPipe and pass
new SanitiseInputPipe() as the first argument to app.useGlobalPipes followed by
the existing new ValidationPipe({...}), ensuring you reference the
SanitiseInputPipe symbol and leave ValidationPipe configuration unchanged.

In `@backend/test/setup/integration.setup.ts`:
- Around line 39-60: The Test.createTestingModule call currently spreads
...overrides (which may include imports and providers) causing duplicate imports
and double-handling of providers; change createTestingModule to exclude
overrides.imports and overrides.providers when spreading (e.g., const { imports,
providers, ...rest } = overrides) and pass rest into Test.createTestingModule
while still explicitly merging the default imports with (imports || []), and
keep the existing overrideProvider loop to apply providers (do not also include
providers in the module metadata).
- Around line 62-71: global.cleanupTestDatabase currently deletes rows by
iterating entityMetadatas which can fail on FK constraints; change it to run
TRUNCATE with CASCADE (or temporarily disable referential integrity) instead of
DELETE for each entity.tableName using the DataSource.query or a QueryRunner so
FK dependencies are handled atomically; use entity.tableName from
dataSource.entityMetadatas and execute e.g. TRUNCATE TABLE "schema"."table"
CASCADE (or appropriate DB-specific disable/enable constraint commands) and
ensure the DataSource/QueryRunner is initialized and released after the
operation in global.cleanupTestDatabase.

---

Nitpick comments:
In `@backend/test/api-contract.spec.ts`:
- Around line 473-484: The test overrides hardcode JwtAuthGuard to always inject
users.listener which prevents validating role-specific response shapes; change
the override in api-contract.spec.ts to inject a configurable test user (e.g.,
read from a local variable/currentTestUser or a helper getTestUser(role))
instead of users.listener, and update tests to set that variable to different
roles (admin, listener, etc.) before calling controllers so JwtAuthGuard (and
optionally RolesGuard) returns requests with the appropriate user shape for
role-specific contract checks; keep ModerateMessagePipe override as-is or make
it use the same configurable user helper if needed.
- Around line 121-122: Replace repeated inline require calls for Nest exceptions
with top-level imports: add "BadRequestException, UnauthorizedException,
NotFoundException" imported from '@nestjs/common' at the top of the file, then
replace occurrences of patterns like "throw new
(require('@nestjs/common').NotFoundException)('...')" (and similar for
BadRequestException/UnauthorizedException) with direct usage "throw new
NotFoundException('...')" (or BadRequestException/UnauthorizedException) in the
mock service functions referenced in this spec file to clean up and centralize
exception imports.

In `@backend/test/jest-e2e-updated.json`:
- Around line 6-11: Remove the unused ignore pattern from the Jest config: open
the JSON where testPathIgnorePatterns is defined and delete the
"<rootDir>/../src/**/*.unit-spec.ts$" entry (it is redundant because TESTING.md
and the existing testMatch/*.e2e-spec.ts restricts test files to *.spec.ts/*
.e2e-spec.ts); ensure the resulting testPathIgnorePatterns still contains the
other necessary patterns and that the JSON formatting remains valid.

In `@backend/test/setup/e2e.setup.ts`:
- Around line 19-24: Add TypeScript declarations for the global helpers
(createE2ETestModule, httpTestUtils, testFixtures) by augmenting the global
namespace (e.g., declare global { function createE2ETestModule(...):
Promise<ReturnType>; const httpTestUtils: HttpTestUtilsType; const testFixtures:
TestFixturesType; }) or by adding a .d.ts file like in unit.setup.ts; ensure you
reference the actual return/type shapes used in e2e.setup.ts (e.g.,
Test.createTestingModule/compiled module type for createE2ETestModule and the
specific interfaces/types for httpTestUtils and testFixtures) and export/import
any custom types so the compiler in strict mode recognizes these globals.
- Line 33: Replace the dynamic require usage for ValidationPipe with a static
import: add ValidationPipe to the top-file imports from '@nestjs/common'
alongside Test, then update the instantiation site that currently uses new
(require('@nestjs/common').ValidationPipe) to new ValidationPipe(...) so the
code uses the statically imported ValidationPipe symbol.

In `@backend/test/setup/integration.setup.ts`:
- Line 18: The entities glob 'src/**/*.entity{.ts,.js}' in integration.setup.ts
may not resolve under Jest; replace the relative value for the TypeORM
'entities' option with an absolute path (e.g. using __dirname +
'/**/*.entity{.ts,.js}' or path.resolve(__dirname,
'../../src/**/*.entity{.ts,.js}')) so TypeORM can reliably locate entity files
during tests; update the code that sets the 'entities' array in the test setup
(the 'entities' entry shown in the diff) to construct the absolute path or
mirror the production logic from app.module.ts.
- Around line 94-105: The beforeAll hook currently calls process.exit(1) on
failure which prevents Jest from reporting failures; replace the process.exit
call in the beforeAll catch block with throwing an Error (or rethrowing the
caught error) so Jest can surface the failure—specifically update the catch in
the beforeAll that wraps global.testUtils.createTestDataSource() to throw a new
Error including contextual text and the original error (or simply rethrow error)
instead of calling process.exit(1).

In `@backend/test/setup/unit.setup.ts`:
- Around line 62-103: Declare types for global.testUtils by augmenting the
global namespace (either in this file or a separate global.d.ts) so TypeScript
knows the shape of createMockUser, createMockArtist, and createMockTrack; add a
declare global { var testUtils: { createMockUser: (overrides?: Record<string,
unknown>) => Record<string, unknown>; createMockArtist: (overrides?:
Record<string, unknown>) => Record<string, unknown>; createMockTrack:
(overrides?: Record<string, unknown>) => Record<string, unknown>; }; } or use
more specific interfaces for the returned objects, then ensure the declaration
file is included by tsconfig so references to global.testUtils and its methods
(createMockUser/createMockArtist/createMockTrack) are type-checked.

In `@backend/TESTING.md`:
- Around line 56-77: The fenced code block showing the directory tree in
TESTING.md is missing a language specifier; update the opening fence from ``` to
```text (or ```plaintext) so the directory structure is rendered consistently
(locate the block that begins with the backend/ tree and change the fence to
include "text").

In `@PR_DESCRIPTION.md`:
- Around line 29-33: The fenced code block in PR_DESCRIPTION.md showing the API
endpoints is missing a language specifier; update the opening fence from ``` to
```text (or ```http) so the block becomes ```text and preserves the three lines
starting with "GET    /collaborations/invitations/pending", "DELETE
/collaborations/:id", and "GET    /collaborations/tracks/:id/stats" to ensure
proper syntax highlighting in documentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a341464-5ef6-4a44-80eb-e08db08d92f7

📥 Commits

Reviewing files that changed from the base of the PR and between 67b593e and 0aed466.

📒 Files selected for processing (10)
  • PR_DESCRIPTION.md
  • backend/TESTING.md
  • backend/package.json
  • backend/test/api-contract.spec.ts
  • backend/test/jest-e2e-updated.json
  • backend/test/jest-integration.json
  • backend/test/jest-unit.json
  • backend/test/setup/e2e.setup.ts
  • backend/test/setup/integration.setup.ts
  • backend/test/setup/unit.setup.ts

Comment on lines +55 to +58
generateChallenge: jest.fn(async (publicKey: string) => {
if (!publicKey || !publicKey.startsWith('G') || publicKey.length < 56) {
throw new (require('@nestjs/common').BadRequestException)('Invalid public key format');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Public key validation is incorrect - allows invalid lengths.

The check publicKey.length < 56 allows keys longer than 56 characters. Stellar public keys are exactly 56 characters. Additionally, the inline require('@nestjs/common') is an anti-pattern.

Proposed fix

Add import at top of file:

import { BadRequestException, UnauthorizedException, NotFoundException } from '@nestjs/common';

Then update the validation:

 generateChallenge: jest.fn(async (publicKey: string) => {
-  if (!publicKey || !publicKey.startsWith('G') || publicKey.length < 56) {
-    throw new (require('@nestjs/common').BadRequestException)('Invalid public key format');
+  if (!publicKey || !publicKey.startsWith('G') || publicKey.length !== 56) {
+    throw new BadRequestException('Invalid public key format');
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/api-contract.spec.ts` around lines 55 - 58, The mock
generateChallenge implementation currently permits public keys with lengths
other than Stellar's required 56 characters and uses an inline require; update
the validation in the generateChallenge jest.fn to require publicKey &&
publicKey.startsWith('G') && publicKey.length === 56, and replace the inline
require('@nestjs/common').BadRequestException with an imported
BadRequestException (add import { BadRequestException, UnauthorizedException,
NotFoundException } from '@nestjs/common' at the top of the file) so the code
uses the BadRequestException symbol directly.

expect(res.body).toHaveProperty('expiresAt');
expect(typeof res.body.challengeId).toBe('string');
expect(typeof res.body.challenge).toBe('string');
expect(res.body.expiresAt).toBeInstanceOf(Date);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Assertion will fail - JSON responses contain date strings, not Date objects.

res.body.expiresAt will be a string (e.g., "2024-01-15T10:30:00.000Z") after JSON parsing, not a Date instance. This test will fail.

Proposed fix
-          expect(res.body.expiresAt).toBeInstanceOf(Date);
+          expect(typeof res.body.expiresAt).toBe('string');
+          expect(new Date(res.body.expiresAt).getTime()).toBeGreaterThan(Date.now());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(res.body.expiresAt).toBeInstanceOf(Date);
expect(typeof res.body.expiresAt).toBe('string');
expect(new Date(res.body.expiresAt).getTime()).toBeGreaterThan(Date.now());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/api-contract.spec.ts` at line 518, The test currently asserts
res.body.expiresAt is a Date instance but JSON responses contain ISO date
strings; change the assertion to check that res.body.expiresAt is a string and
is parseable as a valid date (e.g., assert typeof res.body.expiresAt ===
'string' and that Date.parse(res.body.expiresAt) is not NaN) so the assertion
uses res.body.expiresAt and verifies it is a valid ISO date string rather than
an instance of Date.

Comment on lines +594 to +606
it('GET /tracks/:id - should return track contract', () => {
return request(app.getHttpServer())
.get(`/tracks/${tracks.sample.id}`)
.expect(200)
.expect((res) => {
expect(res.body).toHaveProperty('id');
expect(res.body).toHaveProperty('title');
expect(res.body).toHaveProperty('genre');
expect(res.body).toHaveProperty('artist');
expect(res.body.artist).toHaveProperty('id');
expect(res.body.artist).toHaveProperty('artistName');
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Test will fail - mock findOne doesn't return artist relation.

The test expects res.body.artist.id and res.body.artist.artistName, but the buildMockTracksService().findOne (lines 118-124) returns tracks without a populated artist relation. The mock stores tracks as flat objects.

Proposed fix - update mock to include artist relation
       findOne: jest.fn(async (id: string) => {
         const found = trackStore.find((t) => t.id === id);
         if (!found) {
           throw new (require('@nestjs/common').NotFoundException)('Track not found');
         }
-        return found;
+        return {
+          ...found,
+          artist: {
+            id: found.artistId || artists.sample.id,
+            artistName: artists.sample.artistName,
+          },
+        };
       }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/api-contract.spec.ts` around lines 594 - 606, The test fails
because buildMockTracksService().findOne returns tracks without a populated
artist relation while the spec asserts res.body.artist.id and
res.body.artist.artistName; update the mock returned value in
buildMockTracksService().findOne (and/or the sample track data referenced as
tracks.sample) to include an artist object with the expected shape (id and
artistName) so the GET /tracks/:id contract test receives a track with a
populated artist relation.

Comment on lines +31 to +38
// Set up global pipes
app.useGlobalPipes(
new (require('@nestjs/common').ValidationPipe)({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

E2E setup missing SanitiseInputPipe that production uses.

The production main.ts (lines 31-37) registers both SanitiseInputPipe and ValidationPipe, but this E2E setup only configures ValidationPipe. This could cause behavioral differences where sanitization happens in production but not in E2E tests, potentially missing XSS or injection vulnerabilities that would be caught in production.

🔧 Proposed fix to match production pipe configuration
+import { ValidationPipe } from '@nestjs/common';
+// Import or mock SanitiseInputPipe to match production
+// import { SanitiseInputPipe } from '../../src/common/pipes/sanitise-input.pipe';

 global.httpTestUtils = {
   createTestApp: async (module: any) => {
     const app = module.createNestApplication();
     
     // Set up global pipes
     app.useGlobalPipes(
-      new (require('@nestjs/common').ValidationPipe)({
+      // Add SanitiseInputPipe to match production (main.ts:32-37)
+      // new SanitiseInputPipe(),
+      new ValidationPipe({
         whitelist: true,
         forbidNonWhitelisted: true,
         transform: true,
       }),
     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/setup/e2e.setup.ts` around lines 31 - 38, E2E setup registers
ValidationPipe but omits the production SanitiseInputPipe; update the
app.useGlobalPipes call to include the SanitiseInputPipe (the same class used in
production) alongside ValidationPipe so E2E behavior matches production: import
or require SanitiseInputPipe and pass new SanitiseInputPipe() as the first
argument to app.useGlobalPipes followed by the existing new
ValidationPipe({...}), ensuring you reference the SanitiseInputPipe symbol and
leave ValidationPipe configuration unchanged.

Comment on lines +39 to +60
global.createTestingModule = async (overrides: any = {}) => {
const moduleBuilder = Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '.env.test',
}),
TypeOrmModule.forRoot(testDbConfig),
...(overrides.imports || []),
],
...overrides,
});

// Override any providers if specified
if (overrides.providers) {
for (const provider of overrides.providers) {
moduleBuilder.overrideProvider(provider.provide).useValue(provider.useValue);
}
}

return moduleBuilder.compile();
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Confusing override logic may cause duplicate module metadata.

The current implementation spreads ...overrides which includes imports (already handled at line 47) and providers. This causes:

  1. imports to be added twice if present in overrides
  2. providers to be added to module metadata AND processed via overrideProvider, which are different mechanisms
Proposed fix to separate concerns cleanly
 global.createTestingModule = async (overrides: any = {}) => {
+  const { imports = [], providers = [], ...rest } = overrides;
+
   const moduleBuilder = Test.createTestingModule({
     imports: [
       ConfigModule.forRoot({
         isGlobal: true,
         envFilePath: '.env.test',
       }),
       TypeOrmModule.forRoot(testDbConfig),
-      ...(overrides.imports || []),
+      ...imports,
     ],
-    ...overrides,
+    ...rest,
   });
 
   // Override any providers if specified
-  if (overrides.providers) {
-    for (const provider of overrides.providers) {
+  if (providers.length > 0) {
+    for (const provider of providers) {
       moduleBuilder.overrideProvider(provider.provide).useValue(provider.useValue);
     }
   }
 
   return moduleBuilder.compile();
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/setup/integration.setup.ts` around lines 39 - 60, The
Test.createTestingModule call currently spreads ...overrides (which may include
imports and providers) causing duplicate imports and double-handling of
providers; change createTestingModule to exclude overrides.imports and
overrides.providers when spreading (e.g., const { imports, providers, ...rest }
= overrides) and pass rest into Test.createTestingModule while still explicitly
merging the default imports with (imports || []), and keep the existing
overrideProvider loop to apply providers (do not also include providers in the
module metadata).

Comment on lines +62 to +71
global.cleanupTestDatabase = async (dataSource: DataSource) => {
if (dataSource && dataSource.isInitialized) {
// Clean all tables
const entities = dataSource.entityMetadatas;
for (const entity of entities) {
const repository = dataSource.getRepository(entity.name);
await repository.query(`DELETE FROM "${entity.tableName}";`);
}
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Table cleanup may fail due to foreign key constraints.

Deleting from tables in arbitrary order (from entityMetadatas) can fail if foreign key constraints exist. Tables must be cleaned in dependency order, or constraints should be temporarily disabled.

Proposed fix using TRUNCATE with CASCADE
 global.cleanupTestDatabase = async (dataSource: DataSource) => {
   if (dataSource && dataSource.isInitialized) {
-    // Clean all tables
     const entities = dataSource.entityMetadatas;
     for (const entity of entities) {
-      const repository = dataSource.getRepository(entity.name);
-      await repository.query(`DELETE FROM "${entity.tableName}";`);
+      await dataSource.query(`TRUNCATE TABLE "${entity.tableName}" CASCADE;`);
     }
   }
 };

Note: TRUNCATE ... CASCADE handles FK dependencies but requires appropriate privileges.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
global.cleanupTestDatabase = async (dataSource: DataSource) => {
if (dataSource && dataSource.isInitialized) {
// Clean all tables
const entities = dataSource.entityMetadatas;
for (const entity of entities) {
const repository = dataSource.getRepository(entity.name);
await repository.query(`DELETE FROM "${entity.tableName}";`);
}
}
};
global.cleanupTestDatabase = async (dataSource: DataSource) => {
if (dataSource && dataSource.isInitialized) {
const entities = dataSource.entityMetadatas;
for (const entity of entities) {
await dataSource.query(`TRUNCATE TABLE "${entity.tableName}" CASCADE;`);
}
}
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/test/setup/integration.setup.ts` around lines 62 - 71,
global.cleanupTestDatabase currently deletes rows by iterating entityMetadatas
which can fail on FK constraints; change it to run TRUNCATE with CASCADE (or
temporarily disable referential integrity) instead of DELETE for each
entity.tableName using the DataSource.query or a QueryRunner so FK dependencies
are handled atomically; use entity.tableName from dataSource.entityMetadatas and
execute e.g. TRUNCATE TABLE "schema"."table" CASCADE (or appropriate DB-specific
disable/enable constraint commands) and ensure the DataSource/QueryRunner is
initialized and released after the operation in global.cleanupTestDatabase.

@OlufunbiIK OlufunbiIK merged commit 078fd77 into OlufunbiIK:main Mar 29, 2026
1 of 3 checks passed
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.

2 participants