Files
invest-mind-store/apps/api/test/modules/user/user.service.integration.spec.ts

294 lines
11 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserService } from '../../../src/modules/user/user.service';
import { User } from '../../../src/modules/user/user.entity';
import { UserModule } from '../../../src/modules/user/user.module';
import { CreateUserDto } from '../../../src/modules/user/dto/create-user.dto';
import { getDatabaseConfig } from '../../../src/database/database.config';
import { ConflictException } from '@nestjs/common';
describe('UserService (集成测试)', () => {
let service: UserService;
let repository: Repository<User>;
let module: TestingModule;
beforeAll(async () => {
jest.setTimeout(30000); // 设置超时为 30 秒
// 创建测试模块,使用真实数据库连接
module = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env.development', '.env'],
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => {
const config = getDatabaseConfig(configService);
// 使用测试数据库(如果配置了),否则使用开发数据库
const testDatabase =
configService.get<string>('DB_DATABASE') ||
(config.database as string);
return {
...config,
database: testDatabase,
synchronize: true, // 测试环境允许同步
dropSchema: false, // 不删除现有数据
} as typeof config;
},
inject: [ConfigService],
}),
UserModule,
],
}).compile();
service = module.get<UserService>(UserService);
repository = module.get<Repository<User>>(getRepositoryToken(User));
// 清理测试数据(可选)
await repository.clear();
});
afterAll(async () => {
// 测试结束后清理
// await repository.clear();
await module.close();
});
describe('create - 集成测试', () => {
it('应该成功在数据库中创建用户', async () => {
const createUserDto: CreateUserDto = {
username: 'test_user_1',
password: 'password123',
email: 'test1@example.com',
nickname: '测试用户1',
};
const result = await service.create(createUserDto);
console.log('regitser result', result);
expect(result).toBeDefined();
expect(result.userId).toBeDefined();
expect(result.username).toBe(createUserDto.username);
expect(result.email).toBe(createUserDto.email);
expect(result.nickname).toBe(createUserDto.nickname);
expect(result.status).toBe('active');
expect(result.role).toBe('user'); // 默认角色
expect(result.passwordHash).toBeDefined();
expect(result.passwordHash).not.toBe(createUserDto.password); // 密码应该被加密
// 验证数据确实保存到数据库
const savedUser = await repository.findOne({
where: { userId: result.userId },
});
expect(savedUser).toBeDefined();
expect(savedUser?.username).toBe(createUserDto.username);
expect(savedUser?.email).toBe(createUserDto.email);
// 清理
await repository.remove(result);
});
it('应该使用默认角色和状态', async () => {
const createUserDto: CreateUserDto = {
username: 'test_user_2',
password: 'password123',
email: 'test2@example.com',
// 不提供 role
};
const result = await service.create(createUserDto);
expect(result.role).toBe('user'); // 默认角色
expect(result.status).toBe('active'); // 默认状态
// 清理
// await repository.remove(result);
});
it('应该支持可选字段', async () => {
const createUserDto: CreateUserDto = {
username: 'test_user_3',
password: 'password123',
email: 'test3@example.com',
nickname: '测试用户3',
avatarUrl: 'https://example.com/avatar.jpg',
phone: '13800138000',
};
const result = await service.create(createUserDto);
expect(result.nickname).toBe(createUserDto.nickname);
expect(result.avatarUrl).toBe(createUserDto.avatarUrl);
expect(result.phone).toBe(createUserDto.phone);
// 清理
// await repository.remove(result);
});
it('应该抛出 ConflictException 当用户名已存在时', async () => {
const createUserDto: CreateUserDto = {
username: 'duplicate_username',
password: 'password123',
email: 'unique@example.com',
};
// 先创建一个用户
await service.create(createUserDto);
// 尝试创建相同用户名的用户
const duplicateDto: CreateUserDto = {
username: 'duplicate_username',
password: 'password456',
email: 'another@example.com',
};
await expect(service.create(duplicateDto)).rejects.toThrow(
ConflictException,
);
await expect(service.create(duplicateDto)).rejects.toThrow(
'用户名',
);
// 清理
// const user = await repository.findOne({ where: { username: 'duplicate_username' } });
// if (user) await repository.remove(user);
});
it('应该抛出 ConflictException 当邮箱已存在时', async () => {
const createUserDto: CreateUserDto = {
username: 'unique_username',
password: 'password123',
email: 'duplicate@example.com',
};
// 先创建一个用户
await service.create(createUserDto);
// 尝试创建相同邮箱的用户
const duplicateDto: CreateUserDto = {
username: 'another_username',
password: 'password456',
email: 'duplicate@example.com',
};
await expect(service.create(duplicateDto)).rejects.toThrow(
ConflictException,
);
await expect(service.create(duplicateDto)).rejects.toThrow('邮箱');
// 清理
// const user = await repository.findOne({ where: { email: 'duplicate@example.com' } });
// if (user) await repository.remove(user);
});
});
describe('findAll - 集成测试', () => {
it('应该返回所有用户列表', async () => {
// 先创建几个测试用户
const users: CreateUserDto[] = [
{
username: 'findall_user_1',
password: 'password123',
email: 'findall1@example.com',
nickname: '查询测试用户1',
},
{
username: 'findall_user_2',
password: 'password123',
email: 'findall2@example.com',
nickname: '查询测试用户2',
},
{
username: 'findall_user_3',
password: 'password123',
email: 'findall3@example.com',
nickname: '查询测试用户3',
role: 'admin',
},
];
const createdUsers: User[] = [];
for (const userDto of users) {
const user = await service.create(userDto);
createdUsers.push(user);
}
// 查询所有用户
const result = await service.findAll();
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBeGreaterThanOrEqual(users.length);
// 验证创建的用户都在列表中
const usernames = result.map((u) => u.username);
users.forEach((userDto) => {
expect(usernames).toContain(userDto.username);
});
// 验证排序(按创建时间倒序)
if (result.length > 1) {
for (let i = 0; i < result.length - 1; i++) {
expect(
result[i].createdAt.getTime(),
).toBeGreaterThanOrEqual(result[i + 1].createdAt.getTime());
}
}
// 清理
// await repository.remove(createdUsers);
});
it('应该返回空数组当没有用户时', async () => {
// 清理所有用户
await repository.clear();
const result = await service.findAll();
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(0);
});
it('应该包含用户的所有字段', async () => {
// 创建一个完整信息的用户
const createUserDto: CreateUserDto = {
username: 'full_info_user',
password: 'password123',
email: 'fullinfo@example.com',
nickname: '完整信息用户',
avatarUrl: 'https://example.com/avatar.jpg',
phone: '13800138000',
role: 'admin',
};
await service.create(createUserDto);
// 查询所有用户
const result = await service.findAll();
const foundUser = result.find(
(u) => u.username === createUserDto.username,
);
expect(foundUser).toBeDefined();
expect(foundUser?.userId).toBeDefined();
expect(foundUser?.username).toBe(createUserDto.username);
expect(foundUser?.email).toBe(createUserDto.email);
expect(foundUser?.nickname).toBe(createUserDto.nickname);
expect(foundUser?.avatarUrl).toBe(createUserDto.avatarUrl);
expect(foundUser?.phone).toBe(createUserDto.phone);
expect(foundUser?.role).toBe(createUserDto.role);
expect(foundUser?.status).toBe('active');
expect(foundUser?.createdAt).toBeDefined();
expect(foundUser?.updatedAt).toBeDefined();
// 清理
// if (foundUser) await repository.remove(foundUser);
});
});
});