Set up PostgreSQL database configuration and enhance application structure

- Added .env.example with database connection details and JWT configuration.
- Introduced docker-compose.yml for PostgreSQL service setup with health checks.
- Created drizzle.config.ts for database schema and migration management.
- Updated nest-cli.json to include Swagger plugin configuration for API documentation.
- Enhanced package.json with new database-related scripts and dependencies.
- Implemented initial database migrations for user and token management.
- Configured application bootstrap process to load environment variables and set up Swagger.
- Added shared application configuration in configure-app.ts for consistent setup.
- Included unit tests for application configuration and Swagger setup.
This commit is contained in:
shancheas
2026-08-21 15:00:19 +07:00
parent 7f37985d22
commit d01fd6e2ef
54 changed files with 4378 additions and 87 deletions
+141
View File
@@ -0,0 +1,141 @@
import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { SwaggerModule } from '@nestjs/swagger';
import { AppController } from '../../app.controller';
import { AppService } from '../../app.service';
import { AuthController } from '../../modules/auth/auth.controller';
import { AuthService } from '../../modules/auth/auth.service';
import {
createOpenApiDocument,
isSwaggerEnabled,
setupSwagger,
} from './setup-swagger';
describe('isSwaggerEnabled', () => {
it('is disabled in production by default', () => {
expect(isSwaggerEnabled({ NODE_ENV: 'production' })).toBe(false);
});
it('is enabled in production when SWAGGER_ENABLED=true', () => {
expect(
isSwaggerEnabled({
NODE_ENV: 'production',
SWAGGER_ENABLED: 'true',
}),
).toBe(true);
});
it('is enabled when not production', () => {
expect(isSwaggerEnabled({ NODE_ENV: 'development' })).toBe(true);
});
it('is disabled when SWAGGER_ENABLED=false', () => {
expect(
isSwaggerEnabled({
NODE_ENV: 'development',
SWAGGER_ENABLED: 'false',
}),
).toBe(false);
});
});
describe('createOpenApiDocument', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
controllers: [AppController, AuthController],
providers: [
AppService,
{
provide: AuthService,
useValue: {
register: jest.fn(),
login: jest.fn(),
refresh: jest.fn(),
revoke: jest.fn(),
},
},
],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
it('includes auth and app paths with bearer scheme', () => {
const document = createOpenApiDocument(app);
expect(document.info.title).toBe('Tracking API');
expect(document.paths['/auth/login']).toBeDefined();
expect(document.paths['/auth/me']).toBeDefined();
expect(document.paths['/']).toBeDefined();
expect(document.components?.securitySchemes?.['access-token']).toEqual(
expect.objectContaining({ type: 'http', scheme: 'bearer' }),
);
});
it('requires bearer on /auth/me but not on public auth posts', () => {
const document = createOpenApiDocument(app);
expect(document.paths['/auth/me']?.get?.security).toEqual([
{ 'access-token': [] },
]);
expect(document.paths['/auth/login']?.post?.security).toBeUndefined();
expect(document.paths['/auth/register']?.post?.security).toBeUndefined();
});
});
describe('setupSwagger', () => {
let app: INestApplication;
let setupSpy: jest.SpyInstance;
beforeAll(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
beforeEach(() => {
setupSpy = jest
.spyOn(SwaggerModule, 'setup')
.mockImplementation(() => undefined);
});
afterEach(() => {
setupSpy.mockRestore();
});
it('skips mounting when swagger is disabled', () => {
setupSwagger(app, {
NODE_ENV: 'production',
});
expect(setupSpy).not.toHaveBeenCalled();
});
it('mounts at /docs when swagger is enabled', () => {
setupSwagger(app, {
NODE_ENV: 'test',
});
expect(setupSpy).toHaveBeenCalledWith(
'docs',
app,
expect.any(Object),
expect.objectContaining({ jsonDocumentUrl: 'docs-json' }),
);
});
});