import { adminUserId, makeUser, load } from './setup.mjs'; import test from 'node:test'; import assert from 'node:assert/strict'; const { createHost, listHosts, updateHost, deleteHost, filterVisibleHostIds } = await load('../models/hostModel.js'); const { createCredential } = await load('../models/credentialModel.js'); const { createHostGroup, listHostGroups, syncHostGroup } = await load('../models/hostGroupModel.js'); const { createRunPlan, loadRunPlan } = await load('../models/runPlanModel.js'); const { createRdpLaunchSession } = await load('../services/rdpGatewayService.js'); const { db } = await load('../db.js'); const owner = adminUserId(); const other = makeUser({ email: 'other-host@test.local' }); test('personal hosts are visible only to their owner', () => { const host = createHost({ name: 'Private', address: 'priv.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'personal' }, owner); assert.ok(listHosts(owner).some((h) => h.id === host.id)); assert.ok(!listHosts(other).some((h) => h.id === host.id)); }); test('shared hosts are visible to everyone', () => { const host = createHost({ name: 'Shared', address: 'shared.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'shared' }, owner); assert.ok(listHosts(other).some((h) => h.id === host.id)); }); test('hosts preserve hidden source provenance', () => { const host = createHost({ name: 'VC Imported', address: 'vc-imported.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'shared', sourceType: 'vmware', sourceConnectionId: 'vc_test', sourceRef: 'vm-42' }, owner); assert.equal(host.sourceType, 'vmware'); assert.equal(host.sourceConnectionId, 'vc_test'); assert.equal(host.sourceRef, 'vm-42'); }); test('a non-owner cannot update or delete a personal host', () => { const host = createHost({ name: 'Locked', address: 'locked.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'personal' }, owner); assert.equal(updateHost(host.id, { name: 'Hacked' }, other), null); deleteHost(host.id, other); assert.ok(listHosts(owner).some((h) => h.id === host.id), 'host should still exist after foreign delete attempt'); }); test('filterVisibleHostIds drops hosts the user cannot see', () => { const priv = createHost({ name: 'OwnerOnly', address: 'owneronly.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'personal' }, owner); const shared = createHost({ name: 'Everyone', address: 'everyone.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'shared' }, owner); const visibleToOther = filterVisibleHostIds([priv.id, shared.id], other); assert.deepEqual(visibleToOther, [shared.id]); }); test('RDP launch creates a short-lived browser session for Windows hosts with visible credentials', () => { const credential = createCredential({ name: 'RDP Admin', kind: 'username_password', username: 'CONTOSO\\rdpadmin', secret: 'rdp-secret', visibility: 'shared' }, owner); const host = createHost({ name: 'RDP-WIN-01', address: 'rdp-win-01.contoso.local', fqdn: 'rdp-win-01.contoso.local', osFamily: 'windows', transport: 'winrm', credentialId: credential.id, tags: [], visibility: 'shared' }, owner); const session = createRdpLaunchSession(host.id, other); assert.match(session.url, /^\/rdp\/[^/]+$/); assert.equal(session.host.name, 'RDP-WIN-01'); assert.equal(session.host.address, 'rdp-win-01.contoso.local'); assert.ok(!session.url.includes('rdp-secret')); }); test('RDP launch rejects non-Windows hosts or hosts without credentials', () => { const credential = createCredential({ name: 'RDP Linux Credential', kind: 'username_password', username: 'linux-user', secret: 'linux-secret', visibility: 'shared' }, owner); const linux = createHost({ name: 'RDP-LINUX-01', address: 'rdp-linux.local', osFamily: 'linux', transport: 'ssh', credentialId: credential.id, tags: [], visibility: 'shared' }, owner); assert.throws(() => createRdpLaunchSession(linux.id, owner), /Windows hosts/i); const noCredential = createHost({ name: 'RDP-NOCRED-01', address: 'rdp-nocred.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'shared' }, owner); assert.throws(() => createRdpLaunchSession(noCredential.id, owner), /assigned visible credential/i); }); test('manual host groups keep explicit members', () => { const host = createHost({ name: 'ManualMember', address: 'manual-member.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'shared' }, owner); const group = createHostGroup({ name: 'Manual Group', mode: 'manual', hostIds: [host.id], visibility: 'shared' }, owner); assert.equal(group.mode, 'manual'); assert.deepEqual(group.hostIds, [host.id]); assert.equal(listHostGroups(owner).some((row) => row.id === group.id), true); }); test('dynamic host groups sync by rule', () => { const eng = createHost({ name: 'ENG-APP-01', address: 'eng-app-01.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'shared' }, owner); createHost({ name: 'FIN-APP-01', address: 'fin-app-01.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'shared' }, owner); const group = createHostGroup({ name: 'Engineering', mode: 'dynamic', matchMode: 'all', rules: [{ field: 'name', operator: 'contains', value: 'ENG' }], visibility: 'shared' }, owner); const synced = syncHostGroup(group.id); const refreshed = listHostGroups(owner).find((row) => row.id === group.id); assert.equal(synced.matched >= 1, true); assert.equal(refreshed.hostIds.includes(eng.id), true); assert.equal(refreshed.hostIds.some((hostId) => hostId !== eng.id && db.prepare('SELECT name FROM hosts WHERE id = ?').get(hostId)?.name === 'FIN-APP-01'), false); }); test('runplans can target host groups', () => { const script = db.prepare('SELECT id FROM scripts LIMIT 1').get(); const host = createHost({ name: 'RUN-GROUP-01', address: 'run-group-01.local', osFamily: 'windows', transport: 'winrm', tags: [], visibility: 'shared' }, owner); const group = createHostGroup({ name: 'Run Group', mode: 'manual', hostIds: [host.id], visibility: 'shared' }, owner); const runplan = createRunPlan({ name: 'Grouped Run', scriptId: script.id, visibility: 'shared', hostIds: [], hostGroupIds: [group.id] }, owner); const loaded = loadRunPlan(runplan.id); assert.deepEqual(loaded.hostGroupIds, [group.id]); assert.equal(loaded.hostCount, 1); assert.equal(loaded.hostGroupCount, 1); });