Skip to content
Draft
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
14 changes: 0 additions & 14 deletions jest.setup.js

This file was deleted.

42 changes: 8 additions & 34 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@
"cleanup:retroactive": "ts-node -r tsconfig-paths/register src/modules/jobs/commands/retroactive-deleted-items-cleanup.command.ts",
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
"start:prod": "NODE_ENV=production node -r newrelic dist/main",
"start:prod": "cross-env NODE_ENV=production node -r newrelic dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest",
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
"test:cov": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "NODE_OPTIONS=--experimental-vm-modules cross-env NODE_ENV=test jest --config ./test/jest-e2e.json --forceExit",
"test:e2e:watch": "cross-env NODE_ENV=test jest --config ./test/jest-e2e.json --watchAll",
"test": "cross-env NODE_ENV=test vitest run",
"test:watch": "cross-env NODE_ENV=test vitest --watch",
"test:cov": "cross-env NODE_ENV=test vitest run --coverage",
"migrate": "cross-env NODE_ENV=development sequelize db:migrate",
"migrate:undo": "cross-env NODE_ENV=development sequelize db:migrate:undo",
"migrate:prod": "cross-env NODE_ENV=production sequelize db:migrate",
Expand Down Expand Up @@ -97,14 +94,12 @@
"uuid": "^11.1.0"
},
"devDependencies": {
"@golevelup/ts-jest": "^1.2.0",
"@internxt/eslint-config-internxt": "^1.0.9",
"@nestjs/schematics": "^11.0.7",
"@nestjs/testing": "^11.1.6",
"@types/chance": "^1.1.6",
"@types/crypto-js": "^4.2.1",
"@types/express": "^5.0.1",
"@types/jest": "^30.0.0",
"@types/jsonwebtoken": "9.0.2",
"@types/multer": "^1.4.13",
"@types/multer-s3": "^3.0.3",
Expand All @@ -114,46 +109,25 @@
"@types/supertest": "^6.0.3",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"@vitest/coverage-istanbul": "^4.0.18",
"aws-sdk-client-mock": "^4.0.0",
"chance": "^1.1.13",
"cross-env": "^7.0.3",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"husky": "^8.0.3",
"jest": "^30.2.0",
"lint-staged": "^15.1.0",
"prettier": "^3.5.3",
"sequelize-cli": "^6.6.3",
"source-map-support": "^0.5.21",
"supertest": "^7.1.4",
"ts-jest": "^29.4.6",
"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "4.2.0",
"typescript": "^5.8.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"roots": [
"src",
"test"
],
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"testEnvironment": "node",
"setupFilesAfterEnv": [
"<rootDir>/jest.setup.js"
]
"typescript": "^5.8.3",
"vitest": "^4.0.18",
"vitest-mock-extended": "^3.1.0"
},
"lint-staged": {
"./src/**/*.{js,ts}": [
Expand Down
137 changes: 79 additions & 58 deletions src/common/audit-logs/audit-log.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,91 +1,112 @@
import { Test, type TestingModule } from '@nestjs/testing';
import { createMock, type DeepMocked } from '@golevelup/ts-jest';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { type Logger } from '@nestjs/common';
import { AuditLogService } from './audit-log.service';
import { AuditLogsRepository } from './audit-logs.repository';
import { AuditLogService, type CreateAuditLogDto } from './audit-log.service';
import { type AuditLogsRepository } from './audit-logs.repository';
import {
AuditAction,
AuditEntityType,
AuditAction,
AuditPerformerType,
} from './audit-logs.attributes';
import { v4 } from 'uuid';

const baseDto: CreateAuditLogDto = {
entityType: AuditEntityType.User,
entityId: 'user-123',
action: AuditAction.EmailChanged,
performerType: AuditPerformerType.User,
performerId: 'performer-456',
metadata: { ip: '127.0.0.1' },
};

describe('AuditLogService', () => {
let service: AuditLogService;
let repository: DeepMocked<AuditLogsRepository>;
let logger: DeepMocked<Logger>;
let mockRepository: { create: ReturnType<typeof vi.fn> };
let loggerWarnSpy: ReturnType<typeof vi.spyOn>;

beforeEach(async () => {
logger = createMock<Logger>();
mockRepository = { create: vi.fn() };

const module: TestingModule = await Test.createTestingModule({
providers: [AuditLogService],
})
.setLogger(logger)
.useMocker(() => createMock())
.compile();

service = module.get<AuditLogService>(AuditLogService);
repository = module.get(AuditLogsRepository);
});
service = new AuditLogService(mockRepository as AuditLogsRepository);

it('When the service is instantiated, then it should be defined', () => {
expect(service).toBeDefined();
loggerWarnSpy = vi
.spyOn(Logger.prototype, 'warn')

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / run-tests (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should work with a Workspace entity and Gateway performer

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / run-tests (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should work without optional fields (performerId, metadata)

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / run-tests (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should not log a warning when repository.create succeeds

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / run-tests (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should include the correct error message in the warning

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / run-tests (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should log a warning when repository.create fails

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / run-tests (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should not throw when repository.create fails

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / run-tests (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should call repository.create with the provided dto

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / SonarCloud (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should work with a Workspace entity and Gateway performer

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / SonarCloud (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should work without optional fields (performerId, metadata)

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / SonarCloud (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should not log a warning when repository.create succeeds

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / SonarCloud (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should include the correct error message in the warning

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / SonarCloud (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should log a warning when repository.create fails

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / SonarCloud (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should not throw when repository.create fails

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14

Check failure on line 31 in src/common/audit-logs/audit-log.service.spec.ts

View workflow job for this annotation

GitHub Actions / SonarCloud (20.x)

src/common/audit-logs/audit-log.service.spec.ts > AuditLogService > log > should call repository.create with the provided dto

ReferenceError: Logger is not defined ❯ src/common/audit-logs/audit-log.service.spec.ts:31:14
.mockImplementation(() => undefined);
});

describe('log', () => {
it('When valid audit log data is provided, then it should create an audit log', async () => {
const auditLogDto = {
entityType: AuditEntityType.User,
entityId: v4(),
action: AuditAction.PasswordChanged,
performerType: AuditPerformerType.User,
performerId: v4(),
metadata: { folderUuid: v4() },
};
it('should call repository.create with the provided dto', async () => {
await service.log(baseDto);

repository.create.mockResolvedValueOnce({} as any);
expect(mockRepository.create).toHaveBeenCalledOnce();
expect(mockRepository.create).toHaveBeenCalledWith(baseDto);
});

await service.log(auditLogDto);
it('should not throw when repository.create fails', async () => {
mockRepository.create.mockRejectedValue(new Error('Database error'));

expect(repository.create).toHaveBeenCalledWith(auditLogDto);
await expect(service.log(baseDto)).resolves.toBeUndefined();
});

it('When valid audit log data without metadata is provided, then it should create an audit log', async () => {
const auditLogDto = {
entityType: AuditEntityType.User,
entityId: v4(),
action: AuditAction.TfaEnabled,
performerType: AuditPerformerType.User,
performerId: v4(),
};
it('should log a warning when repository.create fails', async () => {
const dbError = new Error('Connection timeout');
mockRepository.create.mockRejectedValue(dbError);

await service.log(baseDto);

expect(loggerWarnSpy).toHaveBeenCalledOnce();
expect(loggerWarnSpy).toHaveBeenCalledWith(
{
action: baseDto.action,
entityType: baseDto.entityType,
entityId: baseDto.entityId,
error: dbError.message,
},
'Failed to create audit log',
);
});

repository.create.mockResolvedValueOnce({} as any);
it('should include the correct error message in the warning', async () => {
const specificError = new Error('Unique constraint violation');
mockRepository.create.mockRejectedValue(specificError);

await service.log(auditLogDto);
await service.log(baseDto);

expect(repository.create).toHaveBeenCalledWith(auditLogDto);
const [warnPayload] = loggerWarnSpy.mock.calls[0];
expect(warnPayload.error).toBe('Unique constraint violation');
});

it('When repository throws an error, then it should log a warning and not throw', async () => {
const auditLogDto = {
it('should not log a warning when repository.create succeeds', async () => {
mockRepository.create.mockResolvedValue(undefined);

await service.log(baseDto);

expect(loggerWarnSpy).not.toHaveBeenCalled();
});

it('should work without optional fields (performerId, metadata)', async () => {
const minimalDto: CreateAuditLogDto = {
entityType: AuditEntityType.User,
entityId: v4(),
action: AuditAction.EmailChanged,
performerType: AuditPerformerType.User,
performerId: v4(),
metadata: { newEmail: 'new@example.com' },
entityId: 'user-999',
action: AuditAction.AccountDeactivated,
performerType: AuditPerformerType.System,
};
mockRepository.create.mockResolvedValue(undefined);

const error = new Error('Database connection failed');
repository.create.mockRejectedValueOnce(error);

const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation();
await expect(service.log(minimalDto)).resolves.toBeUndefined();
expect(mockRepository.create).toHaveBeenCalledWith(minimalDto);
});

await service.log(auditLogDto);
it('should work with a Workspace entity and Gateway performer', async () => {
const workspaceDto: CreateAuditLogDto = {
entityType: AuditEntityType.Workspace,
entityId: 'workspace-123',
action: AuditAction.WorkspaceCreated,
performerType: AuditPerformerType.Gateway,
performerId: 'gateway-001',
};
mockRepository.create.mockResolvedValue(undefined);

expect(repository.create).toHaveBeenCalledWith(auditLogDto);
expect(loggerWarnSpy).toHaveBeenCalled();
await expect(service.log(workspaceDto)).resolves.toBeUndefined();
expect(mockRepository.create).toHaveBeenCalledWith(workspaceDto);
});
});
});
Loading
Loading