diff --git a/package-lock.json b/package-lock.json index 5c392c63..a1ac481a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9695,6 +9695,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -17372,6 +17373,7 @@ "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, "license": "BSD-2-Clause", "optional": true, "bin": { diff --git a/src/transactions/dto/transaction.dto.ts b/src/transactions/dto/transaction.dto.ts index a8b965b5..b4814604 100644 --- a/src/transactions/dto/transaction.dto.ts +++ b/src/transactions/dto/transaction.dto.ts @@ -1,6 +1,20 @@ // @ts-nocheck +<<<<<<< Updated upstream import { IsString, IsNumber, IsOptional, IsEnum, IsUUID, IsDate, IsIn, Min } from 'class-validator'; +======= +import { + IsString, + IsNumber, + IsOptional, + IsEnum, + IsUUID, + IsDate, + IsIn, + Min, + Max, +} from 'class-validator'; +>>>>>>> Stashed changes import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; diff --git a/test/auth/password.utils.spec.ts b/test/auth/password.utils.spec.ts index 57ccb871..a2934cfd 100644 --- a/test/auth/password.utils.spec.ts +++ b/test/auth/password.utils.spec.ts @@ -1,30 +1,68 @@ -import { validatePassword } from '../../src/auth/password.utils'; +import { ConfigService } from '@nestjs/config'; +import { getPasswordPolicy, validatePassword } from '../../src/auth/password.utils'; -describe('validatePassword', () => { - const OLD_ENV = process.env; +function mockConfig(overrides: Record = {}): ConfigService { + return { + get: jest.fn((key: string) => { + const defaults: Record = { + PASSWORD_MIN_LENGTH: '8', + PASSWORD_REQUIRE_UPPERCASE: 'true', + PASSWORD_REQUIRE_LOWERCASE: 'true', + PASSWORD_REQUIRE_DIGIT: 'true', + PASSWORD_REQUIRE_SPECIAL: 'true', + }; + return overrides[key] ?? defaults[key] ?? undefined; + }), + } as unknown as ConfigService; +} + +describe('getPasswordPolicy', () => { + it('returns default values when config service has no overrides', () => { + const configService = mockConfig(); + const policy = getPasswordPolicy(configService); - afterEach(() => { - process.env = { ...OLD_ENV }; + expect(policy.minLength).toBe(8); + expect(policy.requireUppercase).toBe(true); + expect(policy.requireLowercase).toBe(true); + expect(policy.requireDigit).toBe(true); + expect(policy.requireSpecial).toBe(true); + expect(policy.specialChars).toBeDefined(); }); + it('reflects env-driven overrides', () => { + const configService = mockConfig({ + PASSWORD_MIN_LENGTH: '12', + PASSWORD_REQUIRE_UPPERCASE: 'false', + PASSWORD_SPECIAL_CHARS: '!@#$', + }); + + const policy = getPasswordPolicy(configService); + + expect(policy.minLength).toBe(12); + expect(policy.requireUppercase).toBe(false); + expect(policy.requireLowercase).toBe(true); + expect(policy.requireDigit).toBe(true); + expect(policy.requireSpecial).toBe(true); + expect(policy.specialChars).toBe('!@#$'); + }); +}); + +describe('validatePassword', () => { it('accepts a strong password by default policy', () => { - const errors = validatePassword('Str0ng!Pass'); + const configService = mockConfig(); + const errors = validatePassword('Str0ng!Pass', configService); expect(errors).toHaveLength(0); }); it('rejects short or simple passwords', () => { - process.env.PASSWORD_MIN_LENGTH = '12'; - const errors = validatePassword('weak'); + const configService = mockConfig({ PASSWORD_MIN_LENGTH: '12' }); + const errors = validatePassword('weak', configService); expect(errors.length).toBeGreaterThan(0); }); it('requires uppercase/lowercase/digit/special as configured', () => { - process.env.PASSWORD_REQUIRE_UPPERCASE = 'true'; - process.env.PASSWORD_REQUIRE_LOWERCASE = 'true'; - process.env.PASSWORD_REQUIRE_DIGIT = 'true'; - process.env.PASSWORD_REQUIRE_SPECIAL = 'true'; - - const errors = validatePassword('noupper1!'); + const configService = mockConfig(); + const errors = validatePassword('noupper1!', configService); expect(errors).toContain('Password must include at least one uppercase letter'); }); }); diff --git a/test/users/avatar-upload.spec.ts b/test/users/avatar-upload.spec.ts index 09c7b33a..1777321e 100644 --- a/test/users/avatar-upload.spec.ts +++ b/test/users/avatar-upload.spec.ts @@ -102,18 +102,28 @@ describe('AvatarUploadController', () => { const result = await controller.deleteAvatar(deleteDto, { user: mockUser }); +<<<<<<< Updated upstream expect(avatarUploadService.deleteAvatar).toHaveBeenCalledWith( mockUser.id, deleteDto.filename, ); +======= + expect(avatarUploadService.deleteAvatar).toHaveBeenCalledWith(mockUser.id, filename); +>>>>>>> Stashed changes expect(usersService.updateAvatar).toHaveBeenCalledWith(mockUser.id, null); expect(result).toEqual({ message: 'Avatar deleted successfully' }); }); it('should throw BadRequestException when user is not authenticated', async () => { +<<<<<<< Updated upstream await expect( controller.deleteAvatar({ filename: 'test.jpg' }, { user: null } as any), ).rejects.toThrow(BadRequestException); +======= + await expect(controller.deleteAvatar('test.jpg', { user: null } as any)).rejects.toThrow( + BadRequestException, + ); +>>>>>>> Stashed changes }); });