Files
TanXinandTanXin 4eeb050576 release: 发布 v1.10.0 自定义命令片段库版本 (#93)
合并 `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>
2026-08-20 11:42:28 +08:00

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');
});
});
});