107 lines
3.4 KiB
TypeScript
107 lines
3.4 KiB
TypeScript
import { describe, expect, test, afterEach } from "bun:test";
|
|||
|
|
import { handleCommand, handleClientsRequest } from "./httpHandlers";
|
||
|
|
import * as clientManager from "./clientManager";
|
||
|
|
import type { ClientInfo } from "./types";
|
||
|
|
|
||
|
|
function makeClient(id: string, overrides: Partial<ClientInfo> = {}): ClientInfo {
|
||
|
|
const sent: any[] = [];
|
||
|
|
return {
|
||
|
|
id,
|
||
|
|
lastSeen: Date.now(),
|
||
|
|
role: "client",
|
||
|
|
ws: {
|
||
|
|
send(data: any) { sent.push(data); },
|
||
|
|
close() {},
|
||
|
|
_sent: sent,
|
||
|
|
},
|
||
|
|
...overrides,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function uniqueId(): string {
|
||
|
|
return `hh-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
describe("handleCommand", () => {
|
||
|
|
const ids: string[] = [];
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
for (const id of ids) clientManager.deleteClient(id);
|
||
|
|
ids.length = 0;
|
||
|
|
});
|
||
|
|
|
||
|
|
function tracked(id: string) {
|
||
|
|
ids.push(id);
|
||
|
|
return id;
|
||
|
|
}
|
||
|
|
|
||
|
|
test("ping sets lastPingSent and lastPingNonce on the target", () => {
|
||
|
|
const id = tracked(uniqueId());
|
||
|
|
const client = makeClient(id);
|
||
|
|
clientManager.addClient(id, client);
|
||
|
|
|
||
|
|
const req = new Request("https://localhost/api/command?action=ping");
|
||
|
|
const res = handleCommand(client, "ping", req);
|
||
|
|
expect(res.status).toBe(200);
|
||
|
|
expect(client.lastPingSent).toBeGreaterThan(0);
|
||
|
|
expect(typeof client.lastPingNonce).toBe("number");
|
||
|
|
expect((client.ws as any)._sent.length).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("simple commands return ok and send message", () => {
|
||
|
|
for (const action of ["desktop_start", "desktop_stop", "disconnect", "reconnect"]) {
|
||
|
|
const id = tracked(uniqueId());
|
||
|
|
const client = makeClient(id);
|
||
|
|
|
||
|
|
const req = new Request(`https://localhost/api/command?action=${action}`);
|
||
|
|
const res = handleCommand(client, action, req);
|
||
|
|
expect(res.status).toBe(200);
|
||
|
|
expect((client.ws as any)._sent.length).toBe(1);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test("payload commands return ok", () => {
|
||
|
|
for (const action of ["desktop_select_display", "desktop_enable_mouse", "desktop_enable_keyboard"]) {
|
||
|
|
const id = tracked(uniqueId());
|
||
|
|
const client = makeClient(id);
|
||
|
|
|
||
|
|
const req = new Request(`https://localhost/api/command?action=${action}`);
|
||
|
|
const res = handleCommand(client, action, req);
|
||
|
|
expect(res.status).toBe(200);
|
||
|
|
expect((client.ws as any)._sent.length).toBe(1);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test("file commands pass path from query string", () => {
|
||
|
|
for (const action of ["file_list", "file_download", "file_delete", "file_mkdir", "file_zip"]) {
|
||
|
|
const id = tracked(uniqueId());
|
||
|
|
const client = makeClient(id);
|
||
|
|
|
||
|
|
const req = new Request(`https://localhost/api/command?action=${action}&path=C%3A%5CUsers`);
|
||
|
|
const res = handleCommand(client, action, req);
|
||
|
|
expect(res.status).toBe(200);
|
||
|
|
expect((client.ws as any)._sent.length).toBe(1);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test("unknown action returns 400", () => {
|
||
|
|
const id = tracked(uniqueId());
|
||
|
|
const client = makeClient(id);
|
||
|
|
|
||
|
|
const req = new Request("https://localhost/api/command?action=bogus_action");
|
||
|
|
const res = handleCommand(client, "bogus_action", req);
|
||
|
|
expect(res.status).toBe(400);
|
||
|
|
expect((client.ws as any)._sent.length).toBe(0);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("handleClientsRequest", () => {
|
||
|
|
test("returns valid JSON with expected shape", () => {
|
||
|
|
const req = new Request("https://localhost/api/clients?page=1&pageSize=5");
|
||
|
|
const res = handleClientsRequest(req);
|
||
|
|
expect(res.status).toBe(200);
|
||
|
|
const ct = res.headers.get("content-type") || "";
|
||
|
|
expect(ct).toContain("json");
|
||
|
|
});
|
||
|
|
});
|