Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@ The Prisma schema is located under `apps/hotel-management-service-server/prisma/
```

* Seed data lives in `apps/hotel-management-service-server/scripts/seed.ts`.
* **Seeding the database**

Run the standalone seed script whenever you need fresh demo data:

```bash
cd apps/hotel-management-service-server
npm run seed # executes ts-node scripts/seed.ts
```

The script now performs all operations inside a `try … finally` block and **awaits `client.$disconnect()`**, ensuring the PostgreSQL connection is cleanly closed even if an error occurs.
Copy link
Author

Choose a reason for hiding this comment

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

Seeding instructions should mention that the BCRYPT_SALT environment variable must be set (or sourced from a copied .env file). Without it npm run seed throws at runtime, so readers following the guide are likely to hit an avoidable error.

If you prefer a single command, `npm run db:init` already applies migrations **and** invokes the seed script in the correct order.

---

Expand Down
1 change: 1 addition & 0 deletions apps/hotel-management-service-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"detectOpenHandles": true,
Copy link
Author

Choose a reason for hiding this comment

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

detectOpenHandles: true can slow Jest runs considerably and may mask problems when disabled locally. Consider enabling it only in CI (e.g. process.env.CI) or via the --detectOpenHandles CLI flag instead of persisting it in the shared config.

"moduleNameMapper": {
"@app/custom-validators": "<rootDir>/src/validators"
},
Expand Down
6 changes: 2 additions & 4 deletions apps/hotel-management-service-server/scripts/customSeed.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { PrismaClient } from "@prisma/client";

export async function customSeed() {
const client = new PrismaClient();

client.$disconnect();
export async function customSeed(client: PrismaClient) {
// TODO: add seeding logic here using the provided client instance
}
28 changes: 22 additions & 6 deletions apps/hotel-management-service-server/scripts/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import * as dotenv from "dotenv";
import { PrismaClient } from "@prisma/client";
import { customSeed } from "./customSeed";

// Single PrismaClient instance for the whole script
const client = new PrismaClient();
Copy link
Author

Choose a reason for hiding this comment

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

dotenv.config() is executed after instantiating PrismaClient, so env vars such as DATABASE_URL from .env will not be in effect at construction time. Move dotenv.config() to the very top of the file before new PrismaClient().


// Extra safety: ensure disconnect on process exit
process.on("beforeExit", async () => {
await client.$disconnect();
});

if (require.main === module) {
dotenv.config();

Expand All @@ -10,16 +18,24 @@ if (require.main === module) {
if (!BCRYPT_SALT) {
throw new Error("BCRYPT_SALT environment variable must be defined");
}

// Execute the seed when run directly
seed().catch((err) => {
console.error("Failed to seed database", err);
process.exit(1);
});
}

async function seed() {
console.info("Seeding database...");

const client = new PrismaClient();
void client.$disconnect();
try {
console.info("Seeding database with custom seed...");
await customSeed(client);

console.info("Seeding database with custom seed...");
customSeed();

console.info("Seeded database successfully");
console.info("Seeded database successfully");
} finally {
// Always disconnect, even if seeding throws
await client.$disconnect();
}
}
34 changes: 34 additions & 0 deletions apps/hotel-management-service-server/test/seed.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { resolve } from 'path';
Copy link
Author

Choose a reason for hiding this comment

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

The test assumes the exit event delivers a numeric code, but Node passes null when a process terminates via signal. Handle the code === null + signal case (or listen to the close event) to avoid false-negative failures in CI.

import { spawn } from 'child_process';

/**
* Integration test to ensure the seed script exits cleanly
* without leaving open handles (detectOpenHandles enabled in Jest config).
*/

describe('Database seed script', () => {
it('should run to completion with exit code 0', async () => {
// Path to the seed script (TypeScript).
const scriptPath = resolve(__dirname, '../scripts/seed.ts');

// Spawn the seed script via ts-node.
// Using stdio: 'inherit' helps observe any console output during CI failures.
const child = spawn(
process.execPath, // Node.js binary executing ts-node register and script
[
// Register ts-node transpiler
'-r',
'ts-node/register',
scriptPath,
],
{ stdio: 'inherit' },
);

const exitCode: number = await new Promise((resolvePromise, rejectPromise) => {
child.on('error', rejectPromise);
child.on('exit', resolvePromise);
});

expect(exitCode).toBe(0);
}, 60_000); // generous timeout for DB operations
});
40 changes: 40 additions & 0 deletions docs/seed-scripts-prisma-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Prisma Seed Scripts Audit – Database Connection Handling

Date: 2025-10-31

This audit searches the repository for potential mis-management of `PrismaClient` connections in seed / script files. We looked specifically for:

* `new PrismaClient(` – new client instantiation
* `$disconnect()` – connection teardown

---
Copy link
Author

Choose a reason for hiding this comment

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

The audit still flags issues that this PR already fixed (unawaited $disconnect, multiple client instances). Please update or annotate the Findings section so it reflects the current code state; otherwise the doc will confuse readers.


## Findings

| # | File Path | Line(s) | Code Snippet | Requires Change? | Recommended Action |
|---|-----------|---------|--------------|------------------|--------------------|
| 1 | `apps/hotel-management-service-server/scripts/seed.ts` | 18-19 | ```ts
const client = new PrismaClient();
void client.$disconnect();
``` | **Yes** | • Move `$disconnect()` into a `finally` block and `await` it.<br/>• Prefer passing a shared `PrismaClient` instance to `customSeed()` instead of creating separate instances.<br/>• Await `customSeed()` if it becomes async. |
| 2 | `apps/hotel-management-service-server/scripts/customSeed.ts` | 4-6 | ```ts
const client = new PrismaClient();

client.$disconnect();
``` | **Yes** | • Accept a `PrismaClient` parameter rather than creating a new one.<br/>• Ensure the caller handles cleanup in `finally` with `await client.$disconnect()`. |

No other occurrences of `PrismaClient` instantiation or `$disconnect()` were found in the repository at the time of this audit.

---

## Summary
Both seed scripts create their own `PrismaClient` instance and call `$disconnect()` without awaiting the returned promise. This risks the Node.js process exiting before the connection is cleanly closed, possibly causing connection leaks.

**Refactor Plan (to be implemented in subsequent phases):**

1. Instantiate a single `PrismaClient` in `seed.ts`.
2. Wrap all seeding logic in `try { … } finally { await client.$disconnect(); }`.
3. Pass the shared client into `customSeed()` (update its signature accordingly).
4. Use `process.on('beforeExit')` as a secondary safeguard if desired.

This document will serve as reference for Phase 2 refactoring work.