Files
Sankofa/api/src/services/auth.test.ts
T
defiQUG fe0365757a Update documentation structure and enhance .gitignore
- Added generated index files and report directories to .gitignore to prevent unnecessary tracking of transient files.
- Updated README links to reflect new documentation paths for better navigation.
- Improved documentation organization by ensuring all links point to the correct locations, enhancing user experience and accessibility.
2025-12-12 21:18:55 -08:00

152 lines
4.0 KiB
TypeScript

/**
* Unit tests for authentication service
*
* This file demonstrates testing patterns for service functions in the API.
* See docs/TEST_EXAMPLES.md for more examples.
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import bcrypt from 'bcryptjs'
import jwt from 'jsonwebtoken'
import { login } from './auth'
import { getDb } from '../db'
import { AppErrors } from '../lib/errors'
// Mock dependencies
vi.mock('../db')
vi.mock('../lib/errors')
vi.mock('bcryptjs')
vi.mock('jsonwebtoken')
vi.mock('../lib/secret-validation', () => ({
requireJWTSecret: () => 'test-secret'
}))
describe('auth service', () => {
let mockDb: any
beforeEach(() => {
vi.clearAllMocks()
mockDb = {
query: vi.fn()
}
vi.mocked(getDb).mockReturnValue(mockDb as any)
})
describe('login', () => {
it('should authenticate valid user and return token', async () => {
// Arrange
const mockUser = {
id: '1',
email: '[email protected]',
name: 'Test User',
password_hash: '$2a$10$hashed',
role: 'USER',
created_at: new Date('2024-01-01'),
updated_at: new Date('2024-01-01'),
}
mockDb.query.mockResolvedValue({
rows: [mockUser]
})
vi.mocked(bcrypt.compare).mockResolvedValue(true as never)
vi.mocked(jwt.sign).mockReturnValue('mock-jwt-token' as any)
// Act
const result = await login('[email protected]', 'password123')
// Assert
expect(result).toHaveProperty('token')
expect(result.token).toBe('mock-jwt-token')
expect(result.user.email).toBe('[email protected]')
expect(result.user.name).toBe('Test User')
expect(result.user.role).toBe('USER')
expect(mockDb.query).toHaveBeenCalledWith(
expect.stringContaining('SELECT'),
['[email protected]']
)
expect(bcrypt.compare).toHaveBeenCalledWith('password123', mockUser.password_hash)
expect(jwt.sign).toHaveBeenCalled()
})
it('should throw error for invalid email', async () => {
// Arrange
mockDb.query.mockResolvedValue({
rows: []
})
// Act & Assert
await expect(login('[email protected]', 'password123')).rejects.toThrow()
expect(bcrypt.compare).not.toHaveBeenCalled()
expect(jwt.sign).not.toHaveBeenCalled()
})
it('should throw error for invalid password', async () => {
// Arrange
const mockUser = {
id: '1',
email: '[email protected]',
name: 'Test User',
password_hash: '$2a$10$hashed',
role: 'USER',
created_at: new Date('2024-01-01'),
updated_at: new Date('2024-01-01'),
}
mockDb.query.mockResolvedValue({
rows: [mockUser]
})
vi.mocked(bcrypt.compare).mockResolvedValue(false as never)
// Act & Assert
await expect(login('[email protected]', 'wrongpassword')).rejects.toThrow()
expect(bcrypt.compare).toHaveBeenCalledWith('wrongpassword', mockUser.password_hash)
expect(jwt.sign).not.toHaveBeenCalled()
})
it('should include user role in JWT token', async () => {
// Arrange
const mockUser = {
id: '1',
email: '[email protected]',
name: 'Admin User',
password_hash: '$2a$10$hashed',
role: 'ADMIN',
created_at: new Date('2024-01-01'),
updated_at: new Date('2024-01-01'),
}
mockDb.query.mockResolvedValue({
rows: [mockUser]
})
vi.mocked(bcrypt.compare).mockResolvedValue(true as never)
vi.mocked(jwt.sign).mockReturnValue('mock-jwt-token' as any)
// Act
await login('[email protected]', 'password123')
// Assert
expect(jwt.sign).toHaveBeenCalledWith(
expect.objectContaining({
id: '1',
email: '[email protected]',
name: 'Admin User',
role: 'ADMIN',
}),
'test-secret',
expect.objectContaining({
expiresIn: expect.any(String)
})
)
})
})
})