Files
3dp-manager/server/test/tunnels/tunnels.service.spec.ts
T
Den Piligrim 26f3bb2934 v2.2.0
2026-05-21 22:58:29 +03:00

232 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* eslint-disable @typescript-eslint/unbound-method */
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { HttpException } from '@nestjs/common';
import { TunnelsService } from 'src/tunnels/tunnels.service';
import { Tunnel } from 'src/tunnels/entities/tunnel.entity';
import { SshService } from 'src/tunnels/ssh.service';
import { Setting } from 'src/settings/entities/setting.entity';
import { Node } from 'src/nodes/entities/node.entity';
import { Subscription } from 'src/subscriptions/entities/subscription.entity';
describe('TunnelsService', () => {
let service: TunnelsService;
let tunnelRepo: Repository<Tunnel>;
let sshService: SshService;
const mockTunnelRepo = {
find: jest.fn(),
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
createQueryBuilder: jest.fn(() => ({
addSelect: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
getOne: jest.fn(),
})),
};
const mockSettingRepo = {
find: jest.fn(),
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn(),
};
const mockNodeRepo = {
findOne: jest.fn(),
};
const mockSubscriptionRepo = {
find: jest.fn(),
save: jest.fn(),
};
const mockSshService = {
executeCommand: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
TunnelsService,
{
provide: getRepositoryToken(Tunnel),
useValue: mockTunnelRepo,
},
{
provide: getRepositoryToken(Setting),
useValue: mockSettingRepo,
},
{
provide: getRepositoryToken(Node),
useValue: mockNodeRepo,
},
{
provide: getRepositoryToken(Subscription),
useValue: mockSubscriptionRepo,
},
{
provide: SshService,
useValue: mockSshService,
},
],
}).compile();
service = module.get<TunnelsService>(TunnelsService);
tunnelRepo = module.get<Repository<Tunnel>>(getRepositoryToken(Tunnel));
settingRepo = module.get<Repository<Setting>>(getRepositoryToken(Setting));
sshService = module.get<SshService>(SshService);
});
afterEach(() => {
jest.resetAllMocks();
});
describe('create', () => {
it('должен создать туннель', async () => {
const dto = { ip: '192.168.1.1', sshPort: 22, username: 'root' };
const node = { id: 'node-1', isMain: true };
const mockTunnel = { id: 1, ...dto, node, nodeId: node.id };
mockNodeRepo.findOne.mockResolvedValue(node);
mockTunnelRepo.create.mockReturnValue(mockTunnel);
mockTunnelRepo.save.mockResolvedValue(mockTunnel);
const result = await service.create(dto);
expect(result).toEqual(mockTunnel);
expect(tunnelRepo.create).toHaveBeenCalledWith({
...dto,
node,
nodeId: node.id,
});
});
});
describe('findAll', () => {
it('должен вернуть все туннели', async () => {
const mockTunnels = [
{ id: 1, ip: '192.168.1.1' },
{ id: 2, ip: '192.168.1.2' },
];
mockTunnelRepo.find.mockResolvedValue(mockTunnels);
const result = await service.findAll();
expect(result).toEqual(mockTunnels);
expect(tunnelRepo.find).toHaveBeenCalledWith({ relations: ['node'] });
});
});
describe('remove', () => {
it('должен удалить туннель по ID', async () => {
mockSubscriptionRepo.find.mockResolvedValue([]);
mockTunnelRepo.delete.mockResolvedValue({ affected: 1 });
await service.remove(1);
expect(tunnelRepo.delete).toHaveBeenCalledWith(1);
});
});
describe('installScript', () => {
const mockTunnel = {
id: 1,
ip: '192.168.1.100',
sshPort: 22,
username: 'root',
password: 'password123',
privateKey: null,
isInstalled: false,
};
it('должен установить скрипт перенаправления', async () => {
mockTunnelRepo.createQueryBuilder.mockReturnValue({
addSelect: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(mockTunnel),
});
mockSettingRepo.findOne.mockResolvedValue({
key: 'xui_ip',
value: '10.0.0.1',
});
mockSshService.executeCommand.mockResolvedValue(
'Script executed successfully',
);
mockTunnelRepo.save.mockResolvedValue({
...mockTunnel,
isInstalled: true,
});
const result = await service.installScript(1);
expect(result).toEqual({
success: true,
output: 'Script executed successfully',
});
expect(sshService.executeCommand).toHaveBeenCalled();
expect(tunnelRepo.save).toHaveBeenCalledWith(
expect.objectContaining({ isInstalled: true }),
);
});
it('должен бросить HttpException, если туннель не найден', async () => {
mockTunnelRepo.createQueryBuilder.mockReturnValue({
addSelect: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(null),
});
await expect(service.installScript(999)).rejects.toThrow(HttpException);
await expect(service.installScript(999)).rejects.toThrow(
'Tunnel not found',
);
});
it('должен бросить HttpException, если xui_host не настроен', async () => {
mockTunnelRepo.createQueryBuilder.mockReturnValue({
addSelect: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(mockTunnel),
});
mockSettingRepo.findOne.mockResolvedValue(null);
await expect(service.installScript(1)).rejects.toThrow(HttpException);
await expect(service.installScript(1)).rejects.toThrow(
'В настройках (Settings) не сохранен Host/IP основного сервера',
);
});
it('должен бросить HttpException при ошибке SSH', async () => {
mockTunnelRepo.createQueryBuilder.mockReturnValue({
addSelect: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
getOne: jest.fn().mockResolvedValue(mockTunnel),
});
mockSettingRepo.findOne.mockResolvedValue({
key: 'xui_ip',
value: '10.0.0.1',
});
mockSshService.executeCommand.mockRejectedValue(
new Error('SSH connection failed'),
);
await expect(service.installScript(1)).rejects.toThrow(HttpException);
await expect(service.installScript(1)).rejects.toThrow(
'Ошибка установки: SSH connection failed',
);
});
});
});