Files
LinkPort/web/tests/connection-diagnostics.spec.ts

70 lines
5.8 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { connectionFailure, configuredConnectionPath, diagnosticCode } from '../src/domain/connectionFailure';
test('Failure stages distinguish connection interruption from rejected credentials and unknown causes', () => {
expect(connectionFailure('target_connect').problemType).toContain('网络');
expect(connectionFailure('mongodb_authentication').problemType).toContain('认证');
expect(connectionFailure('mongodb_authentication_connection').problemType).not.toContain('认证/权限');
expect(connectionFailure('mongodb_authentication_connection').hint).toContain('不能判断密码错误');
expect(connectionFailure('target_connect_refused')).toMatchObject({stage:'目标 TCP 连接被拒绝',problemType:'网络/转发'});
expect(connectionFailure('target_connect_refused', 'en').stage).toBe('Target TCP connection refused');
expect(connectionFailure('target_connect_refused', 'ja').problemType).toBe('ネットワーク / 転送');
expect(connectionFailure('target_protocol').stage).toBe('目标协议响应异常');
expect(connectionFailure('target_permission').problemType).toBe('认证/权限');
expect(connectionFailure('target_connection_or_protocol')).toMatchObject({stage:'未能定位具体阶段',problemType:'无法确定'});
expect(connectionFailure('future_stage').stage).toBe('未能定位具体阶段');
expect(diagnosticCode('server diagnostic: private password')).toBe('unknown');
});
test('Configured path uses asset names and resources without credentials or a made-up successful Pod hop', () => {
const config = {type:'mongodb',targetHost:'unused-pod-ip.invalid',targetPort:27017,authSource:'admin',targetPassword:'private-password',outbound:{mode:'k8s',kubeConfigAssetId:'opaque-asset-id',kubeContext:'fixture-context',namespace:'wall-e-test',resource:'statefulset/mongo-shopee',kubeConfigContent:'private-kube-token'}};
const path = configuredConnectionPath(config,[{config:{id:'opaque-asset-id',name:'测试集群凭据',content:'private-file-content'}}]);
expect(path).toContain('测试集群凭据');
expect(path).toContain('fixture-context');
expect(path).toContain('wall-e-test/statefulset/mongo-shopee');
expect(path).toContain('27017');
expect(path).toContain('admin');
for (const forbidden of ['private-password','private-kube-token','private-file-content','opaque-asset-id','unused-pod-ip.invalid']) expect(path).not.toContain(forbidden);
expect(configuredConnectionPath({targetHost:'db.example',targetPort:3306,outbound:{mode:'socks5',host:'jump.example',port:1080,password:'secret'}})).toContain('SOCKS5 jump.example:1080');
});
test('Kubernetes target failure dialog shows configured path, Chinese stage, problem type and code', async ({page}) => {
await page.goto('/');
const session = await (await page.request.get('/api/v1/session')).json();
const headers = {'X-CSRF-Token':session.csrfToken};
let assetId='',targetId='';
try {
const assetResponse = await page.request.post('/api/v1/assets',{headers,data:{name:'诊断集群配置',type:'kubeconfig',content:JSON.stringify({apiVersion:'v1',kind:'Config','current-context':'fixture',clusters:[{name:'fixture',cluster:{server:'https://127.0.0.1:1','insecure-skip-tls-verify':true}}],contexts:[{name:'fixture',context:{cluster:'fixture',user:'fixture',namespace:'wall-e-test'}}],users:[{name:'fixture',user:{token:'private-diagnostic-kube-token'}}]})}});
expect(assetResponse.ok()).toBeTruthy();
assetId=(await assetResponse.json()).config.id;
const targetResponse=await page.request.post('/api/v1/targets',{headers,data:{name:'连接路径诊断目标',type:'mongodb',targetHost:'',targetPort:27017,targetTLS:false,cluster:{mode:'single'},outbound:{mode:'k8s',kubeConfigAssetId:assetId,kubeContext:'fixture',namespace:'wall-e-test',resource:'statefulset/mongo-shopee'},accounts:[{id:'fixture',remark:'fixture',username:'fixture',password:'private-diagnostic-password'}]}});
expect(targetResponse.ok()).toBeTruthy();
targetId=(await targetResponse.json()).config.id;
await page.route('**/api/v1/assets/*/kubernetes-namespaces?**',route=>route.fulfill({json:{namespaces:['wall-e-test']}}));
let stage='mongodb_authentication_connection';
await page.route('**/api/v1/targets/test',route=>route.fulfill({status:422,json:{stage,error:'连接测试未完成'}}));
await page.reload();
await page.getByRole('navigation').getByRole('button',{name:/目标服务/}).click();
await page.locator('.target-card').filter({hasText:'连接路径诊断目标'}).getByRole('button',{name:'配置目标',exact:true}).click();
const editor=page.getByRole('dialog',{name:'编辑目标服务',exact:true});
const result=page.getByRole('dialog',{name:'连接测试',exact:true});
for (stage of ['mongodb_authentication_connection','mongodb_authentication','target_connection_or_protocol']) {
await editor.getByRole('button',{name:'测试连接',exact:true}).click();
await expect(result).toContainText('配置连接路径');
await expect(result).toContainText('诊断集群配置');
await expect(result).toContainText('wall-e-test/statefulset/mongo-shopee');
await expect(result).toContainText('诊断代码');
await expect(result).toContainText(stage);
await expect(result).toContainText(connectionFailure(stage).stage);
await expect(result).toContainText(connectionFailure(stage).problemType);
await expect(result).not.toContainText('private-diagnostic-password');
await expect(result).not.toContainText('private-diagnostic-kube-token');
await result.getByRole('button',{name:'关闭',exact:true}).click();
}
} finally {
await page.unrouteAll({behavior:'wait'});
if(targetId) await page.request.delete(`/api/v1/targets/${targetId}`,{headers});
if(assetId) await page.request.delete(`/api/v1/assets/${assetId}`,{headers});
}
});