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
@@ -0,0 +1,82 @@
import { UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { DateTime } from '../../../common/value-objects/date-time/date-time';
import type { User } from '../../users/user';
import { UsersService } from '../../users/users.service';
import { RevokedAccessTokensRepository } from '../revoked-access-tokens.repository';
import { JwtStrategy } from './jwt.strategy';
describe('JwtStrategy', () => {
let strategy: JwtStrategy;
let usersService: jest.Mocked<Pick<UsersService, 'findById'>>;
let revoked: jest.Mocked<Pick<RevokedAccessTokensRepository, 'exists'>>;
const now = DateTime.fromUnixMs(1_700_000_000_000);
const user: User = {
id: 'user-1',
username: 'alice',
passwordHash: 'hash',
createdAt: now,
updatedAt: now,
};
beforeEach(async () => {
usersService = { findById: jest.fn() };
revoked = { exists: jest.fn() };
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
JwtStrategy,
{
provide: ConfigService,
useValue: {
getOrThrow: () => 'test-secret-at-least-32-characters-long!!',
},
},
{ provide: UsersService, useValue: usersService },
{ provide: RevokedAccessTokensRepository, useValue: revoked },
],
}).compile();
strategy = moduleRef.get(JwtStrategy);
});
it('returns AuthUser for a valid access payload', async () => {
revoked.exists.mockResolvedValue(false);
usersService.findById.mockResolvedValue(user);
await expect(
strategy.validate({
sub: 'user-1',
username: 'alice',
jti: 'jti-1',
typ: 'access',
}),
).resolves.toEqual({ id: 'user-1', username: 'alice', jti: 'jti-1' });
});
it('rejects revoked access tokens', async () => {
revoked.exists.mockResolvedValue(true);
await expect(
strategy.validate({
sub: 'user-1',
username: 'alice',
jti: 'jti-1',
typ: 'access',
}),
).rejects.toBeInstanceOf(UnauthorizedException);
});
it('rejects non-access token types', async () => {
await expect(
strategy.validate({
sub: 'user-1',
username: 'alice',
jti: 'jti-1',
typ: 'refresh' as 'access',
}),
).rejects.toBeInstanceOf(UnauthorizedException);
});
});