Skip to content

Conversation

@Dexploarer
Copy link
Owner

@Dexploarer Dexploarer commented Nov 22, 2025

This app will be decommissioned on Dec 1st. Please remove this app and install Qodo Git.

User description

Changes

  1. Fix migration path resolution

    • Changed from relative path ./server/db/migrations to absolute path
    • Uses fileURLToPath and dirname for reliable path resolution
    • Ensures migrations work in production environments (Railway)
  2. Remove duplicate migration files

    • Deleted 0030_fix_uuid_defaults.sql (duplicate of 0030_far_silver_fox)
    • Deleted 0031_add_row_level_security.sql (duplicate of 0031_stormy_black_panther)
    • Cleans up migration journal to prevent conflicts
  3. Improve migration logging

    • Added detailed startup logging with migrations folder path
    • Added database URL logging (with password redacted)
    • Enhanced error logging with migration folder info for debugging

Why This Fixes Railway Deployment

The original script used a relative path that failed in production because:

  • Working directory may differ in containerized environments
  • Relative paths are unreliable during Railway builds
  • Absolute paths ensure the script finds migrations regardless of cwd

Testing

Verified migration script works correctly with proper error handling:

  • ✅ Modules load successfully (drizzle-orm, postgres)
  • ✅ Migrations folder resolves to absolute path
  • ✅ Database connection attempts correctly
  • ✅ Error logging provides debugging information

PR Type

Bug fix


Description

  • Fix migration path resolution using absolute paths for Railway deployment

  • Remove duplicate migration files (0030 and 0031) to prevent conflicts

  • Improve migration logging with folder path and redacted database URL

  • Remove unnecessary dotenv import as Bun auto-loads .env files


Diagram Walkthrough

flowchart LR
  A["Relative Path<br/>./server/db/migrations"] -->|"Replace with"| B["Absolute Path<br/>fileURLToPath + dirname"]
  B -->|"Enables"| C["Reliable Migration<br/>in Production"]
  D["Duplicate Migrations<br/>0030, 0031"] -->|"Remove"| E["Clean Migration<br/>Journal"]
  F["Basic Logging"] -->|"Enhance with"| G["Detailed Debug Info<br/>+ Redacted URL"]
Loading

File Walkthrough

Relevant files
Bug fix
migrate.ts
Fix migration path resolution and improve logging               

apps/core/server/db/migrate.ts

  • Replace relative migration path with absolute path using fileURLToPath
    and dirname
  • Remove dotenv/config import as Bun auto-loads environment files
  • Add detailed startup logging with migrations folder path and redacted
    database URL
  • Enhance error logging with error code, message, and migrations folder
    for better debugging
+28/-4   
0030_fix_uuid_defaults.sql
Remove duplicate UUID defaults migration                                 

apps/core/server/db/migrations/0030_fix_uuid_defaults.sql

  • Delete duplicate migration file that sets UUID defaults for all tables
  • This migration duplicates functionality from 0030_far_silver_fox
  • Removing prevents migration journal conflicts and redundant operations
+0/-42   
0031_add_row_level_security.sql
Remove duplicate row-level security migration                       

apps/core/server/db/migrations/0031_add_row_level_security.sql

  • Delete duplicate migration file that implements row-level security
    policies
  • This migration duplicates functionality from 0031_stormy_black_panther
  • Removing prevents migration journal conflicts and redundant policy
    creation
+0/-194 

The managed version of the open source project PR-Agent is sunsetting on the 1st December 2025. The commercial version of this project will remain available and free to use as a hosted service. Install Qodo.

Summary by CodeRabbit

  • Revert

    • Removed a migration that enforced UUID defaults for id columns
    • Removed a migration that applied row-level security policies and related access logic
  • Chores

    • Improved database startup: added environment validation, clearer diagnostics and masked connection info, suppressed noisy DB notice output, and strengthened error handling and logging during migrations

✏️ Tip: You can customize this high-level summary in your review settings.

## Changes

1. **Fix migration path resolution**
   - Changed from relative path `./server/db/migrations` to absolute path
   - Uses `fileURLToPath` and `dirname` for reliable path resolution
   - Ensures migrations work in production environments (Railway)

2. **Remove duplicate migration files**
   - Deleted `0030_fix_uuid_defaults.sql` (duplicate of 0030_far_silver_fox)
   - Deleted `0031_add_row_level_security.sql` (duplicate of 0031_stormy_black_panther)
   - Cleans up migration journal to prevent conflicts

3. **Improve migration logging**
   - Added detailed startup logging with migrations folder path
   - Added database URL logging (with password redacted)
   - Enhanced error logging with migration folder info for debugging

## Why This Fixes Railway Deployment

The original script used a relative path that failed in production because:
- Working directory may differ in containerized environments
- Relative paths are unreliable during Railway builds
- Absolute paths ensure the script finds migrations regardless of cwd

## Testing

Verified migration script works correctly with proper error handling:
- ✅ Modules load successfully (drizzle-orm, postgres)
- ✅ Migrations folder resolves to absolute path
- ✅ Database connection attempts correctly
- ✅ Error logging provides debugging information
@coderabbitai
Copy link

coderabbitai bot commented Nov 22, 2025

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

The PR refactors the migration runner (path resolution, env validation, logging, suppressing PG notices, richer error handling) and deletes two SQL migration files that previously set UUID defaults and added row-level security policies.

Changes

Cohort / File(s) Summary
Migration runner update
apps/core/server/db/migrate.ts
Compute migrationsFolder from file path, remove dotenv usage, validate DATABASE_URL with structured error log and exit, log startup info (masked DB URL + migrations folder), suppress PostgreSQL NOTICE messages via onnotice, call migrate with computed folder, and improve error handling (derive errorCode/message, detect "already exists" cases, ensure client end and process exit).
Removed SQL migrations
apps/core/server/db/migrations/0030_fix_uuid_defaults.sql, apps/core/server/db/migrations/0031_add_row_level_security.sql
Deleted migration that set many tables' id defaults to gen_random_uuid() and deleted migration that created set_current_user() and per-table row-level security policies (RLS) and admin-bypass policies across several tables.

Sequence Diagram(s)

sequenceDiagram
    participant Runner as migrate.ts
    participant FS as Filesystem
    participant PG as Postgres (migrate client)
    Runner->>FS: resolve __filename/__dirname -> migrationsFolder
    Runner->>Runner: validate DATABASE_URL (exit on missing)
    Runner->>Runner: mask DATABASE_URL, logger.info(startup)
    Runner->>PG: create client with onnotice:() => {}
    Runner->>PG: run migrate(migrationsFolder)
    alt success
        PG-->>Runner: migration result
        Runner->>PG: client.end()
        Runner->>Runner: process.exit(0)
    else failure
        PG-->>Runner: error (with cause)
        Runner->>Runner: derive errorCode/message, detect "already exists"
        Runner->>PG: client.end()
        Runner->>Runner: logger.error(...), process.exit(1)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • apps/core/server/db/migrate.ts — correctness of path resolution, DATABASE_URL validation, masking logic, onnotice usage, and exit semantics.
    • Removal of 0030_fix_uuid_defaults.sql — verify existing DB schemas and migration history won't rely on those ALTER statements.
    • Removal of 0031_add_row_level_security.sql — confirm RLS, policies, and helper function are intentionally removed and that no runtime code expects those policies.
    • Ensure CI/migration ordering and rollback/archives account for deleted migration files.

Poem

🐇 I hopped through paths, found folders true,

Masked a URL, and silenced a clue.
Old SQL leaves fell from the trail,
New runner hums a steadier tale.
Tiny paws, a tidy deploy — hop, hop, hail!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly matches the main objective: fixing database migration with Drizzle Kit by resolving migration path issues.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a4252d and c33dc2c.

📒 Files selected for processing (1)
  • apps/core/server/db/migrate.ts (4 hunks)

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Dexploarer, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on improving the robustness and reliability of database migrations, particularly for production deployments. It addresses issues with migration path resolution, streamlines the migration history by removing duplicates, and enhances logging to provide clearer insights into the migration process and any potential failures.

Highlights

  • Migration Path Resolution: The migration script now uses absolute paths for the migrations folder, leveraging fileURLToPath and dirname. This ensures reliable path resolution in various environments, especially production deployments like Railway, where relative paths can be inconsistent.
  • Duplicate Migration File Removal: Two duplicate migration files, 0030_fix_uuid_defaults.sql and 0031_add_row_level_security.sql, have been removed. This cleans up the migration journal and prevents potential conflicts or redundant operations.
  • Enhanced Migration Logging: Detailed logging has been added to the migration process. This includes logging the resolved migrations folder path and a redacted database URL at startup, as well as more comprehensive error logging with specific error codes and messages, significantly improving debugging capabilities.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

);
process.exit(1);
}
}

Choose a reason for hiding this comment

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

Resource Management: Potential Unclosed Database Connection

If an error occurs before migrationClient.end() is called (for example, if an exception is thrown before or during the migration process), the database connection may remain open. To ensure proper resource cleanup, consider using a finally block to always close the connection, regardless of whether an error occurs:

try {
  await migrate(db, { migrationsFolder });
  logger.info({}, "[Migrations] ✓ Migrations completed successfully");
} catch (error: any) {
  // ... error handling ...
} finally {
  await migrationClient.end();
}

This guarantees that the connection is closed even if an error is thrown.

@codiumai-pr-agent-free
Copy link
Contributor

codiumai-pr-agent-free bot commented Nov 22, 2025

This app will be decommissioned on Dec 1st. Please remove this app and install Qodo Git.

PR Compliance Guide 🔍

(Compliance updated until commit c33dc2c)

Below is a summary of compliance checks for this PR:

Security Compliance
Credential Exposure Risk

Description: Database URL with credentials is being logged, and while there's an attempt to redact the
password, the implementation may not handle all URL formats correctly, potentially
exposing database credentials in logs.
migrate.ts [36-44]

Referred Code
databaseUrl: (() => {
  try {
    const url = new URL(process.env.DATABASE_URL!);
    url.password = "****";
    return url.toString();
  } catch {
    return process.env.DATABASE_URL!.replace(/:[^:@]+@/, ":****@");
  }
})(), // Hide password
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Previous compliance checks

Compliance check up to commit 5a4252d
Security Compliance
Credential Exposure Risk

Description: Database URL is logged with password partially redacted, but the redaction pattern may not
catch all password formats, potentially exposing database credentials in logs.
migrate.ts [36-36]

Referred Code
  databaseUrl: process.env.DATABASE_URL?.replace(/:[^:@]+@/, ":****@"), // Hide password
},
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

The managed version of the open source project PR-Agent is sunsetting on the 1st December 2025. The commercial version of this project will remain available and free to use as a hosted service. Install Qodo.

@greptile-apps
Copy link

greptile-apps bot commented Nov 22, 2025

Greptile Overview

Greptile Summary

This PR fixes critical database migration issues that were preventing deployments on Railway. The main change switches from relative path resolution (./server/db/migrations) to absolute paths using fileURLToPath and dirname in the migration script. This addresses a common containerized deployment issue where the working directory differs from development environments, causing relative paths to fail. The PR also removes two duplicate migration files (0030_fix_uuid_defaults.sql and 0031_add_row_level_security.sql) that were conflicting with existing migrations in the journal, and enhances logging with detailed debug information including redacted database URLs. These changes ensure the Drizzle ORM migration system works reliably in production environments while maintaining clean migration history.

Important Files Changed

Filename Score Overview
apps/core/server/db/migrate.ts 5/5 Fixed migration path resolution using absolute paths and enhanced logging for production deployment
apps/core/server/db/migrations/0030_fix_uuid_defaults.sql 5/5 Removed duplicate migration that was conflicting with existing 0030_far_silver_fox.sql
apps/core/server/db/migrations/0031_add_row_level_security.sql 5/5 Removed duplicate migration that was conflicting with existing 0031_stormy_black_panther.sql

Confidence score: 5/5

  • This PR is safe to merge with minimal risk as it addresses well-documented deployment failures with proven solutions
  • Score reflects straightforward bug fixes using standard Node.js path resolution patterns and cleanup of duplicate files that were causing migration conflicts
  • No files require special attention as the changes are focused, well-tested, and follow established patterns for containerized deployments

Sequence Diagram

sequenceDiagram
    participant "Railway Deploy" as Railway
    participant "Migration Script" as Script
    participant "File System" as FS
    participant "Database" as DB
    participant "Logger" as Log

    Railway->>Script: "Execute migrate.ts"
    Script->>Script: "Get absolute path using fileURLToPath + dirname"
    Script->>FS: "Check migrations folder exists"
    FS-->>Script: "Path resolved successfully"
    Script->>Log: "Log migration info with redacted DB URL"
    Script->>DB: "Connect with postgres client"
    DB-->>Script: "Connection established"
    Script->>DB: "Run migrate() with absolute migrationsFolder"
    alt Migration successful
        DB-->>Script: "Migrations completed"
        Script->>Log: "Log success message"
    else Tables already exist (42P07 error)
        DB-->>Script: "PostgreSQL error 42P07"
        Script->>Log: "Log warning about existing tables"
        Script->>Log: "Log schema up to date"
    else Other migration error
        DB-->>Script: "Other error"
        Script->>Log: "Log error with details"
        Script->>Railway: "Exit with code 1"
    end
    Script->>DB: "Close connection"
    Script->>Railway: "Exit with code 0"
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@codiumai-pr-agent-free
Copy link
Contributor

codiumai-pr-agent-free bot commented Nov 22, 2025

PR Code Suggestions ✨

This app will be decommissioned on Dec 1st. Please remove this app and install Qodo Git.

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Remove redundant optional chaining operator
Suggestion Impact:The suggestion pointed out that the optional chaining operator was redundant. The commit addressed this by removing the optional chaining and implementing a more robust URL parsing approach with error handling that uses the non-null assertion operator (!) instead, confirming the developer agreed the value is guaranteed to exist.

code diff:

-    databaseUrl: process.env.DATABASE_URL?.replace(/:[^:@]+@/, ":****@"), // Hide password
+    databaseUrl: (() => {
+      try {
+        const url = new URL(process.env.DATABASE_URL!);
+        url.password = "****";
+        return url.toString();
+      } catch {
+        return process.env.DATABASE_URL!.replace(/:[^:@]+@/, ":****@");
+      }
+    })(), // Hide password

Remove the redundant optional chaining operator from process.env.DATABASE_URL
since its existence is validated in a preceding check.

apps/core/server/db/migrate.ts [32-39]

 // Log migration info for debugging
 logger.info(
   {
     migrationsFolder,
-    databaseUrl: process.env.DATABASE_URL?.replace(/:[^:@]+@/, ":****@"), // Hide password
+    databaseUrl: process.env.DATABASE_URL.replace(/:[^:@]+@/, ":****@"), // Hide password
   },
   "[Migrations] Starting migration process",
 );

[Suggestion processed]

Suggestion importance[1-10]: 3

__

Why: The suggestion correctly identifies that process.env.DATABASE_URL is guaranteed to be defined due to a preceding check, making the optional chaining operator redundant. This is a minor code quality improvement.

Low
  • Update

The managed version of the open source project PR-Agent is sunsetting on the 1st December 2025. The commercial version of this project will remain available and free to use as a hosted service. Install Qodo.

@codiumai-pr-agent-free
Copy link
Contributor

codiumai-pr-agent-free bot commented Nov 22, 2025

CI Feedback 🧐

(Feedback updated until commit 5a4252d)

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: Performance & Load Tests

Failed stage: Run load tests [❌]

Failed test name: Load Testing

Failure summary:

The action failed because the load tests couldn't connect to the server at
http://localhost:3004/api/health/ready. All requests resulted in errors (0 successful requests):

- Every test showed 0 requests/sec with 100% error rate (all requests failed)
- Tests expected
minimum throughput (>50, >100, >200 req/sec) but received 0
- Error rates were Infinity% (expected
<0.01%, <0.05%, etc.)
- The server appears to be either not running, not responding, or not
accessible at the expected endpoint

The issue is in the load.test.ts file which runs integration tests against a server that isn't
properly responding.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

293:  NODE_VERSION: 22.12.0
294:  DATABASE_URL: ***localhost:5432/asset_forge_test
295:  CI: true
296:  ##[endgroup]
297:  bun test v1.3.2 (b131639c)
298:  ##[group]__tests__/integration/load.test.ts:
299:  Running 1000 requests test @ http://localhost:3004/api/health/ready
300:  10 connections
301:  [Load Test] Basic Load - Health Endpoint
302:  Requests: 0
303:  Duration: 1.01s
304:  Requests/sec: 0
305:  Latency p50: 0ms
306:  Latency p97.5: 0ms
307:  Latency p99: 0ms
308:  Errors: 1000
309:  Timeouts: 0
310:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
311:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
312:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
313:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
314:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
315:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
316:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
317:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
318:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
319:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
320:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
321:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
322:  Req/Bytes counts sampled once per second.
323:  # of samples: 1
324:  1k requests in 1.01s, 0 B read
325:  1k errors (0 timeouts)
326:  ##[error]Expected: > 50
327:  Received: 0
328:  
329:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:52:39)
330:  47 | Errors: ${result.errors}
331:  48 | Timeouts: ${result.timeouts}
332:  49 |       `);
333:  50 | 
334:  51 |       // Assertions
335:  52 |       expect(result.requests.average).toBeGreaterThan(50); // At least 50 req/s
336:  ^
337:  error: expect(received).toBeGreaterThan(expected)
338:  Expected: > 50
...

344:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
345:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
346:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
347:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
348:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
349:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
350:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
351:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
352:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
353:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
354:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
355:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
356:  Req/Bytes counts sampled once per second.
357:  # of samples: 1
358:  1k requests in 1s, 0 B read
359:  1k errors (0 timeouts)
360:  ##[error]Expected: > 50
361:  Received: 0
362:  
363:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:74:39)
364:  69 | Requests/sec: ${result.requests.average}
365:  70 | Latency p97.5: ${result.latency.p97_5}ms
366:  71 | Errors: ${result.errors}
367:  72 |       `);
368:  73 | 
369:  74 |       expect(result.requests.average).toBeGreaterThan(50);
370:  ^
371:  error: expect(received).toBeGreaterThan(expected)
372:  Expected: > 50
373:  Received: 0
374:  at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:74:39)
375:  (fail) Load Testing > Basic Load (100 req/sec for 30 seconds) > should handle 100 requests per second on prompts endpoint [1013.00ms]
376:  [Load Test] Basic Load - Prompts Endpoint
377:  Requests: 0
378:  Requests/sec: 0
379:  Latency p97.5: 0ms
380:  Errors: 1000
381:  Running 5000 requests test @ http://localhost:3004/api/health/ready
...

383:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
384:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
385:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
386:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
387:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
388:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
389:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
390:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
391:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
392:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
393:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
394:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
395:  Req/Bytes counts sampled once per second.
396:  # of samples: 1
397:  5k requests in 1.01s, 0 B read
398:  5k errors (0 timeouts)
399:  ##[error]Expected: > 200
400:  Received: 0
401:  
402:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:100:39)
403:  95 | Latency p97.5: ${result.latency.p97_5}ms
404:  96 | Latency p99: ${result.latency.p99}ms
405:  97 | Errors: ${result.errors}
406:  98 |       `);
407:  99 | 
408:  100 |       expect(result.requests.average).toBeGreaterThan(200);
409:  ^
410:  error: expect(received).toBeGreaterThan(expected)
411:  Expected: > 200
412:  Received: 0
413:  at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:100:39)
414:  (fail) Load Testing > Peak Load (500 req/sec for 10 seconds) > should handle peak load on health endpoint [1017.00ms]
415:  [Load Test] Peak Load - Health Endpoint
416:  Requests: 0
417:  Requests/sec: 0
418:  Latency p50: 0ms
419:  Latency p97.5: 0ms
420:  Latency p99: 0ms
421:  Errors: 5000
422:  Running 5000 requests test @ http://localhost:3004/api/health/ready
423:  50 connections
424:  [Load Test] Peak Load Error Rate
425:  Total Requests: 0
426:  Errors: 5000
427:  Error Rate: Infinity%
428:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
429:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
430:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
431:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
432:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
433:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
434:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
435:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
436:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
437:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
438:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
439:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
440:  Req/Bytes counts sampled once per second.
441:  # of samples: 1
442:  5k requests in 1.01s, 0 B read
443:  5k errors (0 timeouts)
444:  ##[error]Expected: < 0.01
445:  Received: Infinity
446:  
447:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:122:25)
448:  117 | Total Requests: ${result.requests.total}
449:  118 | Errors: ${result.errors}
450:  119 | Error Rate: ${(errorRate * 100).toFixed(2)}%
451:  120 |       `);
452:  121 | 
453:  122 |       expect(errorRate).toBeLessThan(0.01); // < 1% error rate
454:  ^
455:  error: expect(received).toBeLessThan(expected)
456:  Expected: < 0.01
457:  Received: Infinity
458:  at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:122:25)
459:  (fail) Load Testing > Peak Load (500 req/sec for 10 seconds) > should maintain low error rate under peak load [1015.00ms]
460:  Running 3000 requests test @ http://localhost:3004/api/health/ready
...

462:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
463:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
464:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
465:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
466:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
467:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
468:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
469:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
470:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
471:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
472:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
473:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
474:  Req/Bytes counts sampled once per second.
475:  # of samples: 1
476:  3k requests in 1s, 0 B read
477:  3k errors (0 timeouts)
478:  ##[error]Expected: > 100
479:  Received: 0
480:  
481:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:145:39)
482:  140 | Latency p97.5: ${result.latency.p97_5}ms
483:  141 | Latency p99: ${result.latency.p99}ms
484:  142 | Throughput: ${(result.throughput.average / 1024 / 1024).toFixed(2)} MB/s
485:  143 |       `);
486:  144 | 
487:  145 |       expect(result.requests.average).toBeGreaterThan(100);
488:  ^
489:  error: expect(received).toBeGreaterThan(expected)
490:  Expected: > 100
...

503:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
504:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
505:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
506:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
507:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
508:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
509:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
510:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
511:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
512:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
513:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
514:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
515:  Req/Bytes counts sampled once per second.
516:  # of samples: 1
517:  3k requests in 1s, 0 B read
518:  3k errors (0 timeouts)
519:  [Load Test] Latency Consistency
...

531:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
532:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
533:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
534:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
535:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
536:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
537:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
538:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
539:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
540:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
541:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
542:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
543:  Req/Bytes counts sampled once per second.
544:  # of samples: 1
545:  500 requests in 1s, 0 B read
546:  500 errors (0 timeouts)
547:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
548:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
549:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
550:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
551:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
552:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
553:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
554:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
555:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
556:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
557:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
558:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
559:  Req/Bytes counts sampled once per second.
560:  # of samples: 1
561:  500 requests in 1.01s, 0 B read
562:  500 errors (0 timeouts)
563:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
...

568:  Latency p97.5: 0ms
569:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
570:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
571:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
572:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
573:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
574:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
575:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
576:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
577:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
578:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
579:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
580:  Req/Bytes counts sampled once per second.
581:  # of samples: 1
582:  500 requests in 1.01s, 0 B read
583:  500 errors (0 timeouts)
584:  ##[error]Expected: > 20
585:  Received: 0
586:  
587:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:202:41)
588:        at forEach (1:11)
589:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:193:15)
590:  197 | Requests: ${result.requests.total}
591:  198 | Requests/sec: ${result.requests.average}
592:  199 | Latency p97.5: ${result.latency.p97_5}ms
593:  200 |         `);
594:  201 | 
595:  202 |         expect(result.requests.average).toBeGreaterThan(20);
596:  ^
597:  error: expect(received).toBeGreaterThan(expected)
598:  Expected: > 20
...

607:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
608:  [Performance] GET Request Latency
609:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
610:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
611:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
612:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
613:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
614:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
615:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
616:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
617:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
618:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
619:  Req/Bytes counts sampled once per second.
620:  # of samples: 1
621:  1k requests in 1s, 0 B read
622:  1k errors (0 timeouts)
623:  (pass) Load Testing > Performance Metrics > should achieve p50 latency < 50ms for GET requests [1010.00ms]
...

628:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
629:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
630:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
631:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
632:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
633:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
634:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
635:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
636:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
637:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
638:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
639:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
640:  Req/Bytes counts sampled once per second.
641:  # of samples: 1
642:  1k requests in 1s, 0 B read
643:  1k errors (0 timeouts)
644:  (pass) Load Testing > Performance Metrics > should achieve p95 latency < 100ms for health endpoint [1011.00ms]
645:  [Performance] Health Endpoint Latency
646:  p97.5: 0ms (target: <100ms)
647:  p99: 0ms
648:  Running 2000 requests test @ http://localhost:3004/api/health/ready
649:  20 connections
650:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
651:  [Performance] Error Rate
652:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
653:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
654:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
655:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
656:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
657:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
658:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
659:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
660:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
661:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
662:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
663:  Req/Bytes counts sampled once per second.
664:  # of samples: 1
665:  2k requests in 1s, 0 B read
666:  2k errors (0 timeouts)
667:  ##[error]Expected: < 0.001
668:  Received: Infinity
669:  
670:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:259:25)
671:  254 | Total: ${result.requests.total}
672:  255 | Errors: ${result.errors}
673:  256 | Rate: ${(errorRate * 100).toFixed(3)}% (target: <0.1%)
674:  257 |       `);
675:  258 | 
676:  259 |       expect(errorRate).toBeLessThan(0.001); // < 0.1%
677:  ^
678:  error: expect(received).toBeLessThan(expected)
679:  Expected: < 0.001
680:  Received: Infinity
681:  at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:259:25)
682:  (fail) Load Testing > Performance Metrics > should maintain error rate < 0.1% [1011.00ms]
683:  Total: 0
684:  Errors: 2000
685:  Rate: Infinity% (target: <0.1%)
...

688:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
689:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
690:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
691:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
692:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
693:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
694:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
695:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
696:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
697:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
698:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
699:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
700:  Req/Bytes counts sampled once per second.
701:  # of samples: 1
702:  2k requests in 1s, 0 B read
703:  2k errors (0 timeouts)
704:  ##[error]Expected: > 50
705:  Received: 0
706:  
707:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:276:39)
708:  271 | [Performance] Throughput
709:  272 | Requests/sec: ${result.requests.average} (target: >100)
710:  273 | Max req/sec: ${result.requests.max}
711:  274 |       `);
712:  275 | 
713:  276 |       expect(result.requests.average).toBeGreaterThan(50); // Adjusted for test environment
714:  ^
715:  error: expect(received).toBeGreaterThan(expected)
716:  Expected: > 50
...

726:  [Stress Test] High Concurrency
727:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
728:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
729:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
730:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
731:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
732:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
733:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
734:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
735:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
736:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
737:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
738:  Req/Bytes counts sampled once per second.
739:  # of samples: 1
740:  1k requests in 1.01s, 0 B read
741:  1k errors (0 timeouts)
742:  ##[error]Expected: < 0
743:  Received: 1000
744:  
745:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:298:29)
746:  293 | Requests/sec: ${result.requests.average}
747:  294 | Latency p99: ${result.latency.p99}ms
748:  295 | Errors: ${result.errors}
749:  296 |       `);
750:  297 | 
751:  298 |       expect(result.errors).toBeLessThan(result.requests.total * 0.05); // < 5% errors
752:  ^
753:  error: expect(received).toBeLessThan(expected)
754:  Expected: < 0
755:  Received: 1000
756:  at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:298:29)
757:  (fail) Load Testing > Stress Testing > should handle high concurrency (100 connections) [1014.00ms]
758:  Connections: 100
759:  Requests: 0
760:  Requests/sec: 0
761:  Latency p99: 0ms
762:  Errors: 1000
763:  Running 1000 requests test @ http://localhost:3004/api/health/ready
...

765:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
766:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
767:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
768:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
769:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
770:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
771:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
772:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
773:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
774:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
775:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
776:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
777:  Req/Bytes counts sampled once per second.
778:  # of samples: 1
779:  1k requests in 1.01s, 0 B read
780:  1k errors (0 timeouts)
781:  Running 500 requests test @ http://localhost:3004/api/health/ready
...

783:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
784:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
785:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
786:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
787:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
788:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
789:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
790:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
791:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
792:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
793:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
794:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
795:  Req/Bytes counts sampled once per second.
796:  # of samples: 1
797:  500 requests in 1s, 0 B read
798:  500 errors (0 timeouts)
799:  ##[error]Expected: 0
800:  Received: 500
801:  
802:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:328:29)
803:  323 | Latency p97.5: ${result.latency.p97_5}ms
804:  324 | Errors: ${result.errors}
805:  325 |       `);
806:  326 | 
807:  327 |       expect(result.latency.p97_5).toBeLessThan(500);
808:  328 |       expect(result.errors).toBe(0);
809:  ^
810:  error: expect(received).toBe(expected)
811:  Expected: 0
812:  Received: 500
813:  at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:328:29)
814:  (fail) Load Testing > Stress Testing > should recover from high load [4023.99ms]
815:  [Stress Test] Recovery After High Load
816:  Latency p97.5: 0ms
817:  Errors: 500
818:  Running 2000 requests test @ http://localhost:3004/api/health/ready
...

820:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
821:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
822:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
823:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
824:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
825:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
826:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
827:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
828:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
829:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
830:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
831:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
832:  Req/Bytes counts sampled once per second.
833:  # of samples: 1
834:  2k requests in 1s, 0 B read
835:  2k errors (0 timeouts)
836:  ##[error]Expected: > 0.1
837:  Received: 0
838:  
839:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:350:30)
840:  345 | Avg: ${throughputMBps.toFixed(2)} MB/s
841:  346 | Total: ${(result.throughput.total / 1024 / 1024).toFixed(2)} MB
842:  347 | Bytes/req: ${(result.throughput.average / result.requests.average).toFixed(0)} bytes
843:  348 |       `);
844:  349 | 
845:  350 |       expect(throughputMBps).toBeGreaterThan(0.1); // At least 0.1 MB/s
846:  ^
847:  error: expect(received).toBeGreaterThan(expected)
848:  Expected: > 0.1
...

862:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
863:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
864:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
865:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
866:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
867:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
868:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
869:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
870:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
871:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
872:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
873:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
874:  Req/Bytes counts sampled once per second.
875:  # of samples: 5
876:  452k requests in 5.01s, 0 B read
877:  45k errors (0 timeouts)
878:  # Unhandled error between tests
879:  -------------------------------
880:  ##[error]Expected: > 100
881:  Received: 0
882:  
883:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:371:39)
884:  366 | Pipelining: 10
885:  367 | Requests: ${result.requests.total}
886:  368 | Requests/sec: ${result.requests.average}
887:  369 |       `);
888:  370 | 
889:  371 |       expect(result.requests.average).toBeGreaterThan(100);
890:  ^
891:  error: expect(received).toBeGreaterThan(expected)
892:  Expected: > 100
...

901:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
902:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
903:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
904:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
905:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
906:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
907:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
908:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
909:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
910:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
911:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
912:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
913:  Req/Bytes counts sampled once per second.
914:  # of samples: 1
915:  2k requests in 1.01s, 0 B read
916:  2k errors (0 timeouts)
917:  ##[error]Expected: < 0
918:  Received: 2000
919:  
920:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:384:29)
921:  379 |         duration: 10,
922:  380 |         amount: 2000,
923:  381 |       });
924:  382 | 
925:  383 |       // All connections should complete
926:  384 |       expect(result.errors).toBeLessThan(result.requests.total * 0.01);
927:  ^
928:  error: expect(received).toBeLessThan(expected)
929:  Expected: < 0
...

935:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
936:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
937:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
938:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
939:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
940:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
941:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
942:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
943:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
944:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
945:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
946:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
947:  Req/Bytes counts sampled once per second.
948:  # of samples: 1
949:  5k requests in 1s, 0 B read
950:  5k errors (0 timeouts)
951:  ##[error]Expected: > 0
952:  Received: 0
953:  
954:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:407:37)
955:  402 |         duration: 15,
956:  403 |         amount: 5000,
957:  404 |       });
958:  405 | 
959:  406 |       // If test completes, server didn't crash
960:  407 |       expect(result.requests.total).toBeGreaterThan(0);
961:  ^
962:  error: expect(received).toBeGreaterThan(expected)
963:  Expected: > 0
...

969:  ┌─────────┬──────┬──────┬───────┬──────┬──────┬───────┬──────┐
970:  │ Stat    │ 2.5% │ 50%  │ 97.5% │ 99%  │ Avg  │ Stdev │ Max  │
971:  ├─────────┼──────┼──────┼───────┼──────┼──────┼───────┼──────┤
972:  │ Latency │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │ 0 ms │ 0 ms  │ 0 ms │
973:  └─────────┴──────┴──────┴───────┴──────┴──────┴───────┴──────┘
974:  ┌───────────┬─────┬──────┬─────┬───────┬─────┬───────┬─────┐
975:  │ Stat      │ 1%  │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
976:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
977:  │ Req/Sec   │ 0   │ 0    │ 0   │ 0     │ 0   │ 0     │ 0   │
978:  ├───────────┼─────┼──────┼─────┼───────┼─────┼───────┼─────┤
979:  │ Bytes/Sec │ 0 B │ 0 B  │ 0 B │ 0 B   │ 0 B │ 0 B   │ 0 B │
980:  └───────────┴─────┴──────┴─────┴───────┴─────┴───────┴─────┘
981:  Req/Bytes counts sampled once per second.
982:  # of samples: 1
983:  2k requests in 1.01s, 0 B read
984:  2k errors (0 timeouts)
985:  ##[error]Expected: < 0.05
986:  Received: Infinity
987:  
988:        at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:437:53)
989:  432 | p97.5 Latency: ${result.latency.p97_5}ms
990:  433 | Success Rate: ${((1 - result.errors / result.requests.total) * 100).toFixed(2)}%
991:  434 |       `);
992:  435 | 
993:  436 |       expect(result.latency.p97_5).toBeLessThan(1000);
994:  437 |       expect(result.errors / result.requests.total).toBeLessThan(0.05);
995:  ^
996:  error: expect(received).toBeLessThan(expected)
997:  Expected: < 0.05
998:  Received: Infinity
999:  at <anonymous> (/home/runner/work/hyper-forge/hyper-forge/apps/core/__tests__/integration/load.test.ts:437:53)
1000:  (fail) Load Testing > Real-World Scenarios > should handle typical user load (50 concurrent users) [1013.00ms]
1001:  ##[endgroup]
1002:  3 pass
1003:  15 fail
1004:  1 error
1005:  20 expect() calls
1006:  Ran 18 tests across 1 file. [25.39s]
1007:  [Scenario] Typical User Load
1008:  Concurrent Users: 50
1009:  Requests: 0
1010:  Avg Latency: 0ms
1011:  p97.5 Latency: 0ms
1012:  Success Rate: -Infinity%
1013:  ##[error]Process completed with exit code 1.
1014:  ##[group]Run # Generate test timing report

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly fixes the database migration script by using an absolute path for the migrations folder, which is crucial for containerized environments. It also cleans up duplicate migration files and improves logging. My main feedback is to improve the password redaction logic for the database URL in the logs. The current regex-based approach can fail if the password contains special characters like '@', potentially leaking parts of the password. Using the built-in URL constructor is a more robust and secure method for this.

Comment on lines 35 to 37
migrationsFolder,
databaseUrl: process.env.DATABASE_URL?.replace(/:[^:@]+@/, ":****@"), // Hide password
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Remove redundant optional chaining operator

Suggested change
migrationsFolder,
databaseUrl: process.env.DATABASE_URL?.replace(/:[^:@]+@/, ":****@"), // Hide password
},
migrationsFolder,
databaseUrl: process.env.DATABASE_URL.replace(/:[^:@]+@/, ":****@"), // Hide password
},

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@Dexploarer Dexploarer merged commit 2b0fac3 into main Nov 22, 2025
0 of 5 checks passed
Copy link

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

🧹 Nitpick comments (2)
apps/core/server/db/migrate.ts (2)

32-39: Startup logging is helpful; URL masking is good but could be made more robust

Logging migrationsFolder plus a password-redacted DATABASE_URL is very useful operationally, and the current regex works for standard postgres://user:pass@host/... URLs. If you anticipate supporting less conventional DSN formats (e.g., credentials in query params), consider switching to new URL() and explicitly blanking url.password / removing any password query param before logging as a future hardening step.


68-76: Structured error logging is strong; consider narrowing the “already exists” heuristic

Including err, code, message, and migrationsFolder in the error log gives you the right context to debug failed migrations. One minor concern is that the earlier isAlreadyExistsError check also relies on errorMessage.includes("already exists"), which might, in rare cases, classify non-42P07 errors as ignorable. If you later run into masked failures, consider tightening that logic (e.g., preferring errorCode when available and being more specific with the message match) while keeping this structured logging as-is.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a10fa76 and 5a4252d.

📒 Files selected for processing (3)
  • apps/core/server/db/migrate.ts (4 hunks)
  • apps/core/server/db/migrations/0030_fix_uuid_defaults.sql (0 hunks)
  • apps/core/server/db/migrations/0031_add_row_level_security.sql (0 hunks)
💤 Files with no reviewable changes (2)
  • apps/core/server/db/migrations/0031_add_row_level_security.sql
  • apps/core/server/db/migrations/0030_fix_uuid_defaults.sql
🧰 Additional context used
🧬 Code graph analysis (1)
apps/core/server/db/migrate.ts (1)
apps/core/server/db/db.ts (1)
  • db (95-98)
🔇 Additional comments (3)
apps/core/server/db/migrate.ts (3)

4-4: Header comment accurately documents Bun behavior

The clarification that Bun auto-loads .env files matches the current setup and avoids unnecessary dotenv usage. No changes needed here.


10-18: Absolute migrationsFolder resolution is a solid improvement; be mindful of build layout

Using import.meta.url + fileURLToPath + dirname to compute an absolute migrationsFolder removes CWD sensitivity and should behave reliably in environments like Railway. This does assume that the migrations directory is deployed as a sibling of this file (e.g., if you ever move to a compiled/bundled dist layout, the SQL files will also need to live alongside the compiled script).


53-53: Using the computed migrationsFolder in migrate() is correct

Passing the migrationsFolder constant into migrate(db, { migrationsFolder }) ties the runtime configuration to the resolved absolute path and eliminates the previous hard-coded relative path issue.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants