more tests
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { afterAll, describe, expect, test } from "bun:test";
|
||||
import { db } from "./db/connection";
|
||||
import { deleteClientRow, listClients, setClientBookmark, setClientTag, upsertClientRow } from "./db";
|
||||
|
||||
const createdClientIds: string[] = [];
|
||||
@@ -140,3 +141,75 @@ describe("client list ordering", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("client search index", () => {
|
||||
function getSearchIndexRow(id: string) {
|
||||
return db
|
||||
.query<{ id: string; host: string | null; customTag: string | null; customTagNote: string | null }>(
|
||||
`SELECT id, host, custom_tag as customTag, custom_tag_note as customTagNote
|
||||
FROM client_search_fts
|
||||
WHERE id = ?`,
|
||||
)
|
||||
.get(id);
|
||||
}
|
||||
|
||||
test("keeps the FTS search index in sync when client metadata changes", () => {
|
||||
try {
|
||||
const id = `fts-sync-${Date.now().toString(36)}`;
|
||||
|
||||
createTempClient(id, {
|
||||
online: true,
|
||||
lastSeen: Date.now(),
|
||||
host: "dispatch-terminal-old",
|
||||
});
|
||||
|
||||
expect(getSearchIndexRow(id)?.host).toBe("dispatch-terminal-old");
|
||||
|
||||
createTempClient(id, {
|
||||
online: true,
|
||||
lastSeen: Date.now(),
|
||||
host: "dispatch-terminal-new",
|
||||
});
|
||||
setClientTag(id, "Priority Ops", "Shift handoff workstation");
|
||||
|
||||
const updatedRow = getSearchIndexRow(id);
|
||||
expect(updatedRow?.host).toBe("dispatch-terminal-new");
|
||||
expect(updatedRow?.customTag).toBe("Priority Ops");
|
||||
expect(updatedRow?.customTagNote).toBe("Shift handoff workstation");
|
||||
|
||||
deleteClientRow(id);
|
||||
expect(getSearchIndexRow(id)).toBeNull();
|
||||
} finally {
|
||||
cleanupCreatedClients();
|
||||
}
|
||||
});
|
||||
|
||||
test("uses indexed candidates before fuzzy matching so exact token searches stay fast", () => {
|
||||
try {
|
||||
const prefix = `fts-candidate-${Date.now().toString(36)}`;
|
||||
const id = `${prefix}-ops-node`;
|
||||
|
||||
createTempClient(id, {
|
||||
online: true,
|
||||
lastSeen: Date.now(),
|
||||
host: "northbridge-ops-node",
|
||||
});
|
||||
setClientTag(id, "Incident Desk", "CPU spike triage host");
|
||||
|
||||
const result = listClients({
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
search: "northbridge",
|
||||
sort: "last_seen_desc",
|
||||
statusFilter: "all",
|
||||
osFilter: "all",
|
||||
countryFilter: "all",
|
||||
enrollmentFilter: "all",
|
||||
});
|
||||
|
||||
expect(result.items.some((item) => item.id === id)).toBe(true);
|
||||
} finally {
|
||||
cleanupCreatedClients();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test";
|
||||
import * as clientManager from "../clientManager";
|
||||
import { decodeMessage } from "../protocol";
|
||||
import * as sessionManager from "../sessions/sessionManager";
|
||||
import type { SocketData } from "../sessions/types";
|
||||
import type { ClientInfo } from "../types";
|
||||
import {
|
||||
handleRemoteDesktopViewerMessage,
|
||||
handleRemoteDesktopViewerOpen,
|
||||
rdStreamingState,
|
||||
} from "./ws-console-rd-hvnc";
|
||||
|
||||
type MockWs = {
|
||||
data: SocketData;
|
||||
sent: unknown[];
|
||||
closedCode?: number;
|
||||
closedReason?: string;
|
||||
send: (msg: unknown) => void;
|
||||
close: (code: number, reason: string) => void;
|
||||
getBufferedAmount: () => number;
|
||||
};
|
||||
|
||||
const clientIdsToCleanup = new Set<string>();
|
||||
|
||||
function createMockWs(data: Partial<SocketData>): MockWs {
|
||||
return {
|
||||
data: {
|
||||
role: "rd_viewer",
|
||||
clientId: "rd-test-client",
|
||||
...data,
|
||||
} as SocketData,
|
||||
sent: [],
|
||||
send(msg: unknown) {
|
||||
this.sent.push(msg);
|
||||
},
|
||||
close(code: number, reason: string) {
|
||||
this.closedCode = code;
|
||||
this.closedReason = reason;
|
||||
},
|
||||
getBufferedAmount() {
|
||||
return 0;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createClient(id: string) {
|
||||
const agentWs = createMockWs({ role: "client", clientId: id });
|
||||
const info: ClientInfo = {
|
||||
id,
|
||||
role: "client",
|
||||
ws: agentWs,
|
||||
lastSeen: Date.now(),
|
||||
online: true,
|
||||
host: "rd-test-host",
|
||||
os: "windows",
|
||||
user: "tester",
|
||||
monitors: 1,
|
||||
};
|
||||
clientManager.addClient(id, info);
|
||||
clientIdsToCleanup.add(id);
|
||||
return { info, agentWs };
|
||||
}
|
||||
|
||||
function agentCommands(ws: MockWs) {
|
||||
return ws.sent.map((msg) => decodeMessage(msg as Uint8Array) as any);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const clientId of clientIdsToCleanup) {
|
||||
for (const session of sessionManager.getRdSessionsForClient(clientId)) {
|
||||
sessionManager.deleteRdSession(session.id);
|
||||
}
|
||||
rdStreamingState.delete(clientId);
|
||||
clientManager.deleteClient(clientId);
|
||||
}
|
||||
clientIdsToCleanup.clear();
|
||||
});
|
||||
|
||||
describe("remote desktop viewer control", () => {
|
||||
test("starts once, ignores duplicate starts, and only stops after the last viewer leaves", () => {
|
||||
const clientId = `rd-control-${Date.now().toString(36)}`;
|
||||
const { agentWs } = createClient(clientId);
|
||||
const firstViewer = createMockWs({ clientId });
|
||||
const secondViewer = createMockWs({ clientId });
|
||||
|
||||
handleRemoteDesktopViewerOpen(firstViewer as any);
|
||||
handleRemoteDesktopViewerOpen(secondViewer as any);
|
||||
|
||||
handleRemoteDesktopViewerMessage(firstViewer as any, JSON.stringify({ type: "desktop_start" }));
|
||||
handleRemoteDesktopViewerMessage(secondViewer as any, JSON.stringify({ type: "desktop_start" }));
|
||||
|
||||
let commands = agentCommands(agentWs);
|
||||
expect(commands.filter((msg) => msg.commandType === "desktop_start")).toHaveLength(1);
|
||||
expect(rdStreamingState.get(clientId)?.isStreaming).toBe(true);
|
||||
|
||||
handleRemoteDesktopViewerMessage(firstViewer as any, JSON.stringify({ type: "desktop_stop" }));
|
||||
|
||||
commands = agentCommands(agentWs);
|
||||
expect(commands.filter((msg) => msg.commandType === "desktop_stop")).toHaveLength(0);
|
||||
expect(rdStreamingState.get(clientId)?.isStreaming).toBe(true);
|
||||
|
||||
sessionManager.deleteRdSession(firstViewer.data.sessionId!);
|
||||
handleRemoteDesktopViewerMessage(secondViewer as any, JSON.stringify({ type: "desktop_stop" }));
|
||||
|
||||
commands = agentCommands(agentWs);
|
||||
expect(commands.filter((msg) => msg.commandType === "desktop_stop")).toHaveLength(1);
|
||||
expect(commands.filter((msg) => msg.commandType === "webrtc_stop")).toHaveLength(1);
|
||||
expect(rdStreamingState.get(clientId)?.isStreaming).toBe(false);
|
||||
});
|
||||
|
||||
test("does not forward desktop_start when a macOS client is missing required permissions", () => {
|
||||
const clientId = `rd-mac-perms-${Date.now().toString(36)}`;
|
||||
const { info, agentWs } = createClient(clientId);
|
||||
info.os = "darwin";
|
||||
info.permissions = { screenRecording: false, accessibility: true };
|
||||
const viewer = createMockWs({ clientId });
|
||||
|
||||
handleRemoteDesktopViewerOpen(viewer as any);
|
||||
handleRemoteDesktopViewerMessage(viewer as any, JSON.stringify({ type: "desktop_start" }));
|
||||
|
||||
expect(agentCommands(agentWs).filter((msg) => msg.commandType === "desktop_start")).toHaveLength(0);
|
||||
expect(rdStreamingState.get(clientId)?.isStreaming).not.toBe(true);
|
||||
expect(viewer.sent.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user