Files
LinkPort/web/tests/host-identity.spec.ts

69 lines
4.8 KiB
TypeScript

import { test, expect } from '@playwright/test';
for (const protocol of ['ssh', 'sftp']) {
test(`${protocol} 网关维护目标并继承最新的主机指纹校验设置`, async ({ page }) => {
await page.goto('/');
const session = await (await page.request.get('/api/v1/session')).json();
const headers = { 'X-CSRF-Token': session.csrfToken };
let targetId = '', gatewayId = '';
try {
const response = await page.request.post('/api/v1/targets', { headers, data: {
name: `指纹继承目标-${protocol}`, type: protocol, targetHost: '127.0.0.1', targetPort: 22,
accounts: [{ id: 'user', remark: '测试账号', username: 'fixture', password: 'isolated-password', authMethod: 'password' }],
} });
expect(response.ok()).toBeTruthy();
targetId = (await response.json()).config.id;
const created = await page.request.post('/api/v1/proxies', { headers, data: {
name: `指纹继承网关-${protocol}`, type: protocol, listenHost: '127.0.0.1', listenPort: protocol === 'ssh' ? 24897 : 24898,
targetId, targetAccountId: 'user', gatewayAccounts: [{ id: 'gateway', username: 'gateway', password: 'isolated-gateway-password', targetAccountId: 'user' }],
} });
expect(created.ok()).toBeTruthy();
gatewayId = (await created.json()).config.id;
await page.reload();
const row = page.getByRole('row').filter({ hasText: `指纹继承网关-${protocol}` });
await row.getByRole('button', { name: '维护目标', exact: true }).click();
const editor = page.getByRole('dialog', { name: '编辑目标服务', exact: true });
await expect(editor.locator('[data-field="name"] input')).toHaveValue(`指纹继承目标-${protocol}`);
await editor.getByRole('tab', { name: '高级', exact: true }).click();
await editor.locator('.el-checkbox').filter({ hasText: '忽略目标 SSH 主机指纹校验' }).click();
await expect(editor.getByRole('checkbox', { name: '忽略目标 SSH 主机指纹校验(不推荐)', exact: true })).toBeChecked();
await editor.getByRole('button', { name: '保存配置', exact: true }).click();
await expect(editor).not.toBeVisible();
await expect(page.locator('.target-card').filter({ hasText: `指纹继承目标-${protocol}` }).getByText('主机指纹:已忽略校验', { exact: true })).toBeVisible();
const gateway = await (await page.request.get(`/api/v1/proxies/${gatewayId}`)).json();
expect(gateway.config.ssh.ignoreHostKey).toBe(true);
expect(gateway.config.ssh.targetHostKey || '').toBe('');
await page.getByRole('navigation').getByRole('button', { name: /网关服务/ }).click();
const checklist = page.getByRole('dialog', { name: '启动前检查', exact: true });
const openCheck = async () => {
await row.getByRole('button', { name: '更多操作', exact: true }).click();
await page.getByRole('menuitem', { name: '启动检查', exact: true }).click();
await expect(checklist).toBeVisible();
};
await openCheck();
await expect(checklist).toContainText('忽略主机指纹校验(继承目标服务)');
await expect(checklist.getByRole('button', { name: '启动网关', exact: true })).toBeEnabled();
await checklist.getByRole('button', { name: '关闭' }).click();
// Simulate a change from another client: reopening checks must refresh it.
expect((await page.request.put(`/api/v1/targets/${targetId}`, { headers, data: { ssh: { ignoreHostKey: false } } })).ok()).toBeTruthy();
await openCheck();
await expect(checklist).toContainText('尚未确认目标 SSH 主机指纹');
await expect(checklist.getByRole('button', { name: '启动网关', exact: true })).toBeDisabled();
await checklist.getByRole('button', { name: '关闭' }).click();
expect((await page.request.put(`/api/v1/targets/${targetId}`, { headers, data: { ssh: { ignoreHostKey: true } } })).ok()).toBeTruthy();
await openCheck();
await expect(checklist).toContainText('忽略主机指纹校验(继承目标服务)');
// Only an isolated local listener is started; no real SSH target is contacted.
await checklist.getByRole('button', { name: '启动网关', exact: true }).click();
await expect(checklist.getByRole('button', { name: '网关已运行', exact: true })).toBeDisabled();
expect((await (await page.request.get(`/api/v1/proxies/${gatewayId}`)).json()).runtime.running).toBe(true);
} finally {
if (gatewayId) {
await page.request.post(`/api/v1/proxies/${gatewayId}/stop`, { headers, data: {} }).catch(() => {});
await page.request.delete(`/api/v1/proxies/${gatewayId}`, { headers }).catch(() => {});
}
if (targetId) await page.request.delete(`/api/v1/targets/${targetId}`, { headers }).catch(() => {});
}
});
}