Files
TREK/server/tests/integration/database-lifecycle.test.ts
T
Maurice 0a4c6fbace refactor(db): open the database through a DatabaseLifecycle provider
db/database.ts opened the connection as a side effect of being imported,
so the moment anything reached it the database was open. index.ts had to
defer loading the app until a first-boot restore was done, and nothing
in the container decided when the connection opened or closed.

The module now opens the connection on first use and opens nothing at
import. A connection closed on purpose (a restore, the shutdown) stays
closed until it is reopened, as before. The new DatabaseLifecycle
provider owns the decisions: buildApp() opens through it, which also
binds the ORM to later swaps (the hook attachOrm used to register) and
runs the schema bootstrap in the same order as before, and the shutdown
closes through it. Its file property is the one resolved database path.

The module keeps every export, so the test suites that replace it with
vi.mock keep working; buildDbMock gains the new openDb.
2026-10-08 18:38:28 +02:00

56 lines
2.0 KiB
TypeScript

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import request from 'supertest';
import type { INestApplication } from '@nestjs/common';
import { buildApp } from '../../src/bootstrap';
import { DatabaseLifecycle } from '../../src/nest/database/database-lifecycle.service';
/**
* The core database's lifecycle, owned by the DatabaseLifecycle provider, on a
* real boot.
*
* The connection used to open as a side effect of importing db/database.ts and
* was closed and reopened through module functions the restore and the demo
* reset called directly. buildApp() now opens it through the provider, which
* also binds the ORM to later swaps, and closes and reopens it through the
* same provider. Unmocked on purpose: under NODE_ENV=test the
* module opens a copy of the migrated schema snapshot, and every reopen opens a
* pristine copy again, which is all these cases need.
*/
describe('the database connection lifecycle under buildApp()', () => {
let app: INestApplication;
beforeAll(async () => {
app = await buildApp();
await app.init();
});
afterAll(async () => {
await app?.close();
});
it('LIFECYCLE-001: the boot opened the connection through the provider and the database answers', async () => {
expect(app.get(DatabaseLifecycle).file).toBe(':memory:');
const res = await request(app.getHttpServer()).get('/api/health/ready');
expect(res.status).toBe(200);
expect(res.body).toEqual({ status: 'ready' });
});
it('LIFECYCLE-002: closed, the readiness probe answers 503; reopened, the ORM is bound to the new handle and it answers again', async () => {
const lifecycle = app.get(DatabaseLifecycle);
lifecycle.close();
try {
const down = await request(app.getHttpServer()).get('/api/health/ready');
expect(down.status).toBe(503);
expect(down.body).toEqual({ status: 'unavailable' });
} finally {
await lifecycle.reopen();
}
const up = await request(app.getHttpServer()).get('/api/health/ready');
expect(up.status).toBe(200);
});
});