first commit 🎉
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
import { describe, expect, it, beforeEach, mock } from 'bun:test'
|
||||
import { UnauthenticatedError, InternalServerError } from '~/helpers/errors'
|
||||
|
||||
// Mock dependencies
|
||||
mock.module('@/user/queries/query', () => ({
|
||||
default: {
|
||||
getUserByEmail: mock(async (email: string) => {
|
||||
if (email === 'test@example.com') {
|
||||
return {
|
||||
id: 'user-123',
|
||||
name: 'Test User',
|
||||
email: 'test@example.com',
|
||||
role: 'admin',
|
||||
image: null,
|
||||
googleId: null,
|
||||
password: '$2a$10$mockhashedpassword',
|
||||
}
|
||||
}
|
||||
if (email === 'volunteer@example.com') {
|
||||
return {
|
||||
id: 'volunteer-123',
|
||||
name: 'Test Volunteer',
|
||||
email: 'volunteer@example.com',
|
||||
role: 'volunteer',
|
||||
image: null,
|
||||
googleId: null,
|
||||
password: '$2a$10$mockhashedpassword',
|
||||
}
|
||||
}
|
||||
return null
|
||||
}),
|
||||
getVolunteerById: mock(async ({ id }: { id: string }) => {
|
||||
if (id === 'volunteer-123') {
|
||||
return {
|
||||
id: 'vol-456',
|
||||
partnerId: 'partner-789',
|
||||
name: 'Test Volunteer',
|
||||
phone: '+1234567890',
|
||||
gender: 'male',
|
||||
}
|
||||
}
|
||||
return null
|
||||
}),
|
||||
},
|
||||
}))
|
||||
|
||||
mock.module('~/helpers/utils', () => ({
|
||||
passwordVerify: mock((password: string, _hash: string) => {
|
||||
return password === 'correctpassword'
|
||||
}),
|
||||
}))
|
||||
|
||||
mock.module('~/helpers/jwt', () => ({
|
||||
sign: mock(async (payload: unknown) => {
|
||||
return `jwt.token.${Buffer.from(JSON.stringify(payload)).toString('base64')}`
|
||||
}),
|
||||
}))
|
||||
|
||||
mock.module('~/plugins', () => ({
|
||||
logger: {
|
||||
debug: mock(() => undefined),
|
||||
info: mock(() => undefined),
|
||||
},
|
||||
}))
|
||||
|
||||
import CommandService from '~/modules/auth/commands/service'
|
||||
|
||||
describe('Auth CommandService', () => {
|
||||
beforeEach(() => {
|
||||
// Reset all mocks before each test
|
||||
mock.restore()
|
||||
|
||||
// Reapply main mocks
|
||||
mock.module('@/user/queries/query', () => ({
|
||||
default: {
|
||||
getUserByEmail: mock(async (email: string) => {
|
||||
if (email === 'test@example.com') {
|
||||
return {
|
||||
id: 'user-123',
|
||||
name: 'Test User',
|
||||
email: 'test@example.com',
|
||||
role: 'admin',
|
||||
image: null,
|
||||
googleId: null,
|
||||
password: '$2a$10$mockhashedpassword',
|
||||
}
|
||||
}
|
||||
if (email === 'volunteer@example.com') {
|
||||
return {
|
||||
id: 'volunteer-123',
|
||||
name: 'Test Volunteer',
|
||||
email: 'volunteer@example.com',
|
||||
role: 'volunteer',
|
||||
image: null,
|
||||
googleId: null,
|
||||
password: '$2a$10$mockhashedpassword',
|
||||
}
|
||||
}
|
||||
return null
|
||||
}),
|
||||
getVolunteerById: mock(async ({ id }: { id: string }) => {
|
||||
if (id === 'volunteer-123') {
|
||||
return {
|
||||
id: 'vol-456',
|
||||
partnerId: 'partner-789',
|
||||
name: 'Test Volunteer',
|
||||
phone: '+1234567890',
|
||||
gender: 'male',
|
||||
}
|
||||
}
|
||||
return null
|
||||
}),
|
||||
},
|
||||
}))
|
||||
|
||||
mock.module('~/helpers/utils', () => ({
|
||||
passwordVerify: mock((password: string, hash: string) => {
|
||||
return password === 'correctpassword'
|
||||
}),
|
||||
}))
|
||||
|
||||
mock.module('~/helpers/jwt', () => ({
|
||||
sign: mock(async (payload: any) => {
|
||||
return `jwt.token.${Buffer.from(JSON.stringify(payload)).toString('base64')}`
|
||||
}),
|
||||
}))
|
||||
|
||||
mock.module('~/plugins', () => ({
|
||||
logger: {
|
||||
debug: mock(() => {}),
|
||||
info: mock(() => {}),
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
describe('login', () => {
|
||||
it('should login successfully with valid credentials', async () => {
|
||||
const result = await CommandService.login({
|
||||
email: 'test@example.com',
|
||||
password: 'correctpassword',
|
||||
})
|
||||
|
||||
expect(result).toHaveProperty('accessToken')
|
||||
expect(typeof result.accessToken).toBe('string')
|
||||
expect(result.accessToken).toMatch(/^jwt\.token\./)
|
||||
})
|
||||
|
||||
it('should throw UnauthenticatedError for non-existent user', async () => {
|
||||
expect(async () => {
|
||||
await CommandService.login({
|
||||
email: 'nonexistent@example.com',
|
||||
password: 'anypassword',
|
||||
})
|
||||
}).toThrow(UnauthenticatedError)
|
||||
})
|
||||
|
||||
it('should throw UnauthenticatedError for incorrect password', async () => {
|
||||
expect(async () => {
|
||||
await CommandService.login({
|
||||
email: 'test@example.com',
|
||||
password: 'wrongpassword',
|
||||
})
|
||||
}).toThrow(UnauthenticatedError)
|
||||
})
|
||||
|
||||
it('should login volunteer user successfully', async () => {
|
||||
const result = await CommandService.login({
|
||||
email: 'volunteer@example.com',
|
||||
password: 'correctpassword',
|
||||
})
|
||||
|
||||
expect(result).toHaveProperty('accessToken')
|
||||
expect(typeof result.accessToken).toBe('string')
|
||||
|
||||
// Decode the JWT payload to verify volunteer data is included
|
||||
const payloadBase64 = result.accessToken.split('.')[2]
|
||||
const payload = JSON.parse(Buffer.from(payloadBase64, 'base64').toString())
|
||||
expect(payload.user).toHaveProperty('volunteer')
|
||||
expect(payload.user.volunteer.id).toBe('vol-456')
|
||||
})
|
||||
|
||||
it('should throw InternalServerError when volunteer detail not found', async () => {
|
||||
// Mock getUserByEmail to return a volunteer user but getVolunteerById to return null
|
||||
const mockUserQuery = await import('~/modules/user/queries/query')
|
||||
mockUserQuery.default.getUserByEmail = mock(async () => ({
|
||||
id: 'volunteer-without-detail',
|
||||
name: 'Volunteer Without Detail',
|
||||
email: 'volunteer-no-detail@example.com',
|
||||
role: 'volunteer',
|
||||
image: null,
|
||||
googleId: null,
|
||||
password: '$2a$10$mockhashedpassword',
|
||||
})) as any
|
||||
mockUserQuery.default.getVolunteerById = mock(async () => null) as any
|
||||
|
||||
expect(async () => {
|
||||
await CommandService.login({
|
||||
email: 'volunteer-no-detail@example.com',
|
||||
password: 'correctpassword',
|
||||
})
|
||||
}).toThrow(InternalServerError)
|
||||
})
|
||||
|
||||
it('should generate unique sub for each login', async () => {
|
||||
const result1 = await CommandService.login({
|
||||
email: 'test@example.com',
|
||||
password: 'correctpassword',
|
||||
})
|
||||
|
||||
// Wait a millisecond to ensure different timestamp
|
||||
await new Promise((resolve) => setTimeout(resolve, 1))
|
||||
|
||||
const result2 = await CommandService.login({
|
||||
email: 'test@example.com',
|
||||
password: 'correctpassword',
|
||||
})
|
||||
|
||||
// Decode both tokens to compare sub values
|
||||
const payload1Base64 = result1.accessToken.split('.')[2]
|
||||
const payload1 = JSON.parse(Buffer.from(payload1Base64, 'base64').toString())
|
||||
|
||||
const payload2Base64 = result2.accessToken.split('.')[2]
|
||||
const payload2 = JSON.parse(Buffer.from(payload2Base64, 'base64').toString())
|
||||
|
||||
expect(payload1.sub).not.toBe(payload2.sub)
|
||||
})
|
||||
|
||||
it('should include correct user data in JWT payload', async () => {
|
||||
const result = await CommandService.login({
|
||||
email: 'test@example.com',
|
||||
password: 'correctpassword',
|
||||
})
|
||||
|
||||
const payloadBase64 = result.accessToken.split('.')[2]
|
||||
const payload = JSON.parse(Buffer.from(payloadBase64, 'base64').toString())
|
||||
|
||||
expect(payload.user.id).toBe('user-123')
|
||||
expect(payload.user.name).toBe('Test User')
|
||||
expect(payload.user.email).toBe('test@example.com')
|
||||
expect(payload.user.role).toBe('admin')
|
||||
expect(payload.user).not.toHaveProperty('password')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user