310 lines
9.7 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|