Files
3dp-manager/server/test/domains/domains.service.spec.ts
T
2026-03-29 03:52:43 +03:00

310 lines
9.7 KiB
TypeScript

/* eslint-disable @typescript-eslint/unbound-method */
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BadRequestException } from '@nestjs/common';
import { DomainsService } from 'src/domains/domains.service';
import { Domain } from 'src/domains/entities/domain.entity';
describe('DomainsService', () => {
let service: DomainsService;
let repo: Repository<Domain>;
const mockRepo = {
find: jest.fn(),
findOne: jest.fn(),
findOneBy: jest.fn(),
create: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
clear: jest.fn(),
findAndCount: jest.fn(),
count: jest.fn(),
createQueryBuilder: jest.fn(() => ({
where: jest.fn().mockReturnThis(),
getOne: jest.fn(),
})),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
DomainsService,
{
provide: getRepositoryToken(Domain),
useValue: mockRepo,
},
],
}).compile();
service = module.get<DomainsService>(DomainsService);
repo = module.get<Repository<Domain>>(getRepositoryToken(Domain));
});
afterEach(() => {
jest.clearAllMocks();
});
describe('onModuleInit', () => {
it('должен создать домены по умолчанию, если база пустая', async () => {
mockRepo.count.mockResolvedValue(0);
mockRepo.create.mockReturnValue({ name: 'ya.ru' });
mockRepo.save.mockResolvedValue({});
await service.onModuleInit();
expect(repo.count).toHaveBeenCalledTimes(1);
expect(repo.create).toHaveBeenCalledTimes(10);
expect(repo.save).toHaveBeenCalled();
});
it('НЕ должен создавать домены, если они уже есть', async () => {
mockRepo.count.mockResolvedValue(5);
await service.onModuleInit();
expect(repo.count).toHaveBeenCalledTimes(1);
expect(repo.create).not.toHaveBeenCalled();
expect(repo.save).not.toHaveBeenCalled();
});
});
describe('create', () => {
it('должен создать домен', async () => {
const dto = { name: 'example.com' };
const normalized = 'example.com';
const created = { id: 1, name: normalized };
mockRepo.createQueryBuilder.mockReturnValue({
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(null),
});
mockRepo.create.mockReturnValue(created);
mockRepo.save.mockResolvedValue(created);
const result = await service.create(dto);
expect(result).toEqual(created);
expect(repo.create).toHaveBeenCalledWith({ name: normalized });
});
it('должен вернуть существующий домен, если он уже есть', async () => {
const dto = { name: 'example.com' };
const existing = { id: 1, name: 'example.com' };
mockRepo.createQueryBuilder.mockReturnValue({
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(existing),
});
const result = await service.create(dto);
expect(result).toEqual(existing);
expect(repo.create).not.toHaveBeenCalled();
expect(repo.save).not.toHaveBeenCalled();
});
it('должен бросить BadRequestException для некорректного домена', async () => {
const dto = { name: 'not-a-domain' };
await expect(service.create(dto)).rejects.toThrow(BadRequestException);
await expect(service.create(dto)).rejects.toThrow(
'Некорректное доменное имя',
);
});
it('должен нормализовать домен с протоколом и путём', async () => {
const dto = { name: 'https://example.com/path/to/page' };
const normalized = 'example.com';
mockRepo.createQueryBuilder.mockReturnValue({
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(null),
});
mockRepo.create.mockReturnValue({ name: normalized });
mockRepo.save.mockResolvedValue({ name: normalized });
await service.create(dto);
expect(repo.create).toHaveBeenCalledWith({ name: normalized });
});
it('должен нормализовать wildcard домен *.example.com → example.com', async () => {
const dto = { name: '*.example.com' };
const normalized = 'example.com';
mockRepo.createQueryBuilder.mockReturnValue({
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(null),
});
mockRepo.create.mockReturnValue({ name: normalized });
mockRepo.save.mockResolvedValue({ name: normalized });
await service.create(dto);
expect(repo.create).toHaveBeenCalledWith({ name: normalized });
});
it('должен игнорировать комментарии в домене', async () => {
const dto = { name: 'example.com # это комментарий' };
const normalized = 'example.com';
mockRepo.createQueryBuilder.mockReturnValue({
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(null),
});
mockRepo.create.mockReturnValue({ name: normalized });
mockRepo.save.mockResolvedValue({ name: normalized });
await service.create(dto);
expect(repo.create).toHaveBeenCalledWith({ name: normalized });
});
});
describe('findAll', () => {
it('должен вернуть домены с пагинацией', async () => {
const mockDomains = [
{ id: 1, name: 'ya.ru' },
{ id: 2, name: 'vk.com' },
];
mockRepo.findAndCount.mockResolvedValue([mockDomains, 100]);
const result = await service.findAll(1, 10);
expect(result).toEqual({
data: mockDomains,
total: 100,
});
expect(repo.findAndCount).toHaveBeenCalledWith({
take: 10,
skip: 0,
order: { id: 'DESC' },
});
});
it('должен использовать значения по умолчанию для пагинации', async () => {
mockRepo.findAndCount.mockResolvedValue([[], 0]);
await service.findAll();
expect(repo.findAndCount).toHaveBeenCalledWith({
take: 10,
skip: 0,
order: { id: 'DESC' },
});
});
});
describe('findAllUnpaginated', () => {
it('должен вернуть все домены без пагинации', async () => {
const mockDomains = [
{ id: 1, name: 'ya.ru' },
{ id: 2, name: 'vk.com' },
];
mockRepo.find.mockResolvedValue(mockDomains);
const result = await service.findAllUnpaginated();
expect(result).toEqual(mockDomains);
expect(repo.find).toHaveBeenCalledWith({ order: { name: 'ASC' } });
});
});
describe('findOne', () => {
it('должен вернуть домен по ID', async () => {
const mockDomain = { id: 1, name: 'ya.ru' };
mockRepo.findOneBy.mockResolvedValue(mockDomain);
const result = await service.findOne(1);
expect(result).toEqual(mockDomain);
expect(repo.findOneBy).toHaveBeenCalledWith({ id: 1 });
});
});
describe('remove', () => {
it('должен удалить домен по ID', async () => {
mockRepo.delete.mockResolvedValue({ affected: 1 });
await service.remove(1);
expect(repo.delete).toHaveBeenCalledWith(1);
});
});
describe('removeAll', () => {
it('должен удалить все домены', async () => {
mockRepo.clear.mockResolvedValue(undefined);
const result = await service.removeAll();
expect(result).toEqual({ success: true });
expect(repo.clear).toHaveBeenCalledTimes(1);
});
});
describe('createMany', () => {
it('должен создать несколько доменов', async () => {
const domains = ['ya.ru', 'vk.com', 'ok.ru'];
mockRepo.find.mockResolvedValue([]);
mockRepo.create.mockReturnValue({ name: 'ya.ru' });
mockRepo.save.mockResolvedValue({});
const result = await service.createMany(domains);
expect(result.count).toBeGreaterThan(0);
expect(repo.save).toHaveBeenCalled();
});
it('должен пропустить существующие домены', async () => {
const domains = ['ya.ru', 'vk.com'];
const existing = [{ id: 1, name: 'ya.ru' }];
mockRepo.find.mockResolvedValue(existing);
const result = await service.createMany(domains);
expect(result.count).toBe(1); // Только vk.com будет создан
});
it('должен вернуть 0, если все домены уже существуют', async () => {
const domains = ['ya.ru', 'vk.com'];
const existing = [
{ id: 1, name: 'ya.ru' },
{ id: 2, name: 'vk.com' },
];
mockRepo.find.mockResolvedValue(existing);
const result = await service.createMany(domains);
expect(result.count).toBe(0);
});
it('должен вернуть 0 для пустого массива', async () => {
const result = await service.createMany([]);
expect(result.count).toBe(0);
expect(repo.find).not.toHaveBeenCalled();
});
it('должен нормализовать домены при массовом создании', async () => {
const domains = ['*.EXAMPLE.com', 'https://vk.com/path', ' ok.ru '];
mockRepo.find.mockResolvedValue([]);
mockRepo.create.mockReturnValue({ name: 'example.com' });
mockRepo.save.mockResolvedValue({});
await service.createMany(domains);
expect(repo.create).toHaveBeenCalled();
});
});
});