合并 `test` 的 v1.10.0 提交到 `main`。 **包含提交** - `28c9433` feat: 添加自定义命令片段库 - `9aa2e80` release: 发布 v1.10.0 自定义命令片段库版本 **关联** - Related to #90 > 本 PR 仅关联 #90,不自动关闭该 Issue。 **验证** - `pnpm run typecheck` 0 错 - `pnpm test` 47/47 603/603 通过 - `pnpm run build:frontend` 成功 --------- Co-authored-by: TanXin <tanxin@example.com>
104 lines
3.5 KiB
TypeScript
104 lines
3.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { SSHAuth } from '../../src/ssh/auth';
|
|
import { concat, encodeString, readUint32 } from '../../src/ssh/utils';
|
|
|
|
describe('SSHAuth', () => {
|
|
describe('buildPasswordAuthRequest', () => {
|
|
it('should build a valid password auth request', () => {
|
|
const username = 'testuser';
|
|
const password = 'testpass';
|
|
|
|
const request = SSHAuth.buildPasswordAuthRequest(username, password);
|
|
|
|
expect(request[0]).toBe(50); // SSH_MSG_USERAUTH_REQUEST
|
|
|
|
let offset = 1;
|
|
|
|
const usernameLen = readUint32(request, offset);
|
|
offset += 4;
|
|
const decodedUsername = new TextDecoder().decode(request.slice(offset, offset + usernameLen));
|
|
offset += usernameLen;
|
|
expect(decodedUsername).toBe(username);
|
|
|
|
const serviceLen = readUint32(request, offset);
|
|
offset += 4;
|
|
const service = new TextDecoder().decode(request.slice(offset, offset + serviceLen));
|
|
offset += serviceLen;
|
|
expect(service).toBe('ssh-connection');
|
|
|
|
const methodLen = readUint32(request, offset);
|
|
offset += 4;
|
|
const method = new TextDecoder().decode(request.slice(offset, offset + methodLen));
|
|
offset += methodLen;
|
|
expect(method).toBe('password');
|
|
|
|
expect(request[offset]).toBe(0);
|
|
offset += 1;
|
|
|
|
const passwordLen = readUint32(request, offset);
|
|
offset += 4;
|
|
const decodedPassword = new TextDecoder().decode(request.slice(offset, offset + passwordLen));
|
|
expect(decodedPassword).toBe(password);
|
|
});
|
|
|
|
it('should handle empty password', () => {
|
|
const username = 'testuser';
|
|
const password = '';
|
|
|
|
const request = SSHAuth.buildPasswordAuthRequest(username, password);
|
|
|
|
// Should still be valid - empty password is allowed in SSH
|
|
expect(request[0]).toBe(50);
|
|
expect(request.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should handle special characters in credentials', () => {
|
|
const username = 'user@host';
|
|
const password = 'p@ssw0rd!#$%';
|
|
|
|
const request = SSHAuth.buildPasswordAuthRequest(username, password);
|
|
|
|
expect(request[0]).toBe(50);
|
|
|
|
// Verify username is correctly encoded
|
|
let offset = 1;
|
|
const usernameLen = readUint32(request, offset);
|
|
offset += 4;
|
|
const decodedUsername = new TextDecoder().decode(request.slice(offset, offset + usernameLen));
|
|
expect(decodedUsername).toBe(username);
|
|
});
|
|
});
|
|
|
|
describe('handleResponse', () => {
|
|
it('should handle USERAUTH_SUCCESS', () => {
|
|
const payload = new Uint8Array([52]); // SSH_MSG_USERAUTH_SUCCESS
|
|
const result = SSHAuth.handleResponse(payload);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.allowedMethods).toBeUndefined();
|
|
});
|
|
|
|
it('should handle USERAUTH_FAILURE', () => {
|
|
const methods = 'publickey,password';
|
|
const methodsBytes = new TextEncoder().encode(methods);
|
|
const payload = new Uint8Array(6 + methodsBytes.length);
|
|
payload[0] = 51; // SSH_MSG_USERAUTH_FAILURE
|
|
|
|
new DataView(payload.buffer).setUint32(1, methodsBytes.length, false);
|
|
payload.set(methodsBytes, 5);
|
|
payload[payload.length - 1] = 0; // partial success = false
|
|
|
|
const result = SSHAuth.handleResponse(payload);
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.allowedMethods).toEqual(['publickey', 'password']);
|
|
});
|
|
|
|
it('should throw on unexpected message type', () => {
|
|
const payload = new Uint8Array([99]);
|
|
|
|
expect(() => SSHAuth.handleResponse(payload)).toThrow('Unexpected auth message type: 99');
|
|
});
|
|
});
|
|
});
|