import { createHash } from 'node:crypto'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { newFixture, type Fixture } from '../helpers/fixture.js'; let fixture: Fixture; beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); }); afterEach(async () => { await fixture.cleanup(); }); describe('server detail data', () => { it('filters history before the global limit and includes the matching campaign members', async () => { const { db } = await import('../../src/lib/server/db/index.js'); const handle = db(); handle.exec("INSERT INTO servers (name) VALUES ('target'), ('other')"); handle.transaction(() => { const deployment = handle.prepare( "INSERT INTO deployments (id, action, started_at) VALUES (?, 'promote', ?)" ); const member = handle.prepare( "INSERT INTO deployment_servers (deployment_id, server, files_changed, restart_state) VALUES (?, ?, 1, 'done')" ); deployment.run('older-target', '2026-01-01'); member.run('older-target', 'target'); member.run('older-target', 'other'); for (let n = 0; n < 501; n++) { deployment.run('other-' + n, '2026-09-18'); member.run('other-' + n, 'other'); } })(); const { GET } = await import('../../src/routes/api/campaigns/+server.js'); const all = await ( await GET({ url: new URL('http://localhost/api/campaigns?limit=500') }) ).json(); expect(all.deployments).toHaveLength(500); const first = await ( await GET({ url: new URL('http://localhost/api/campaigns?limit=25') }) ).json(); const second = await ( await GET({ url: new URL('http://localhost/api/campaigns?limit=25&offset=25') }) ).json(); expect(first.total).toBe(502); expect(first.deployments).toHaveLength(25); expect( new Set([...first.deployments, ...second.deployments].map((row) => row.id)).size ).toBe(50); const filtered = await ( await GET({ url: new URL('http://localhost/api/campaigns?server=target') }) ).json(); expect(filtered.deployments.map((row: { id: string }) => row.id)).toEqual(['older-target']); expect(filtered.deployments[0].servers).toHaveLength(2); expect( ( await ( await GET({ url: new URL('http://localhost/api/campaigns?server=unknown') }) ).json() ).deployments ).toEqual([]); }); it('identifies plugins, scripts, composed configs and unknown inventory files', async () => { const { db } = await import('../../src/lib/server/db/index.js'); db().exec("INSERT INTO servers (name) VALUES ('target')"); const { createScript } = await import('../../src/lib/server/fleet/scripts.js'); const { recordScript } = await import('../../src/lib/server/fleet/inventory.js'); const { script, version } = createScript({ name: 'Plugin', kind: 'plugin', releaseVersion: '1.0.0', targetRelPath: 'native.bin', content: Buffer.from('plugin') }); recordScript('target', 'native.bin', version.id, version.sha256, 6, 'beta'); recordScript('target', 'dedicated_zm.cfg', null, 'hash', 10, 'compose'); recordScript('target', 'main.gsc', null, 'hash', 10, 'beta'); recordScript('target', 'unknown.dat', null, 'hash', 10, 'manual'); const { GET } = await import('../../src/routes/api/servers/[name]/scripts/+server.js'); const body = await (await GET({ params: { name: 'target' } })).json(); expect( Object.fromEntries( body.scripts.map((file: { relPath: string; kind: string }) => [ file.relPath, file.kind ]) ) ).toEqual({ 'dedicated_zm.cfg': 'config', 'main.gsc': 'script', 'native.bin': 'plugin', 'unknown.dat': 'file' }); expect( body.scripts.find((file: { relPath: string }) => file.relPath === 'native.bin') ).toMatchObject({ artifactId: script.id, version: version.version, releaseVersion: '1.0.0', sha256: version.sha256 }); }); it.each([ ['native.bin', 'file'], ['native.dll', 'plugin'] ])( 'omits saved artifact metadata for modified %s and falls back to its extension', async (path, kind) => { const { db } = await import('../../src/lib/server/db/index.js'); db().exec("INSERT INTO servers (name) VALUES ('target')"); const { createScript } = await import('../../src/lib/server/fleet/scripts.js'); const { recordScript } = await import('../../src/lib/server/fleet/inventory.js'); const { version } = createScript({ name: 'Plugin', kind: 'plugin', releaseVersion: '1.0.0', targetRelPath: path, content: Buffer.from('plugin') }); const content = Buffer.from('modified plugin'); const sha256 = createHash('sha256').update(content).digest('hex'); recordScript('target', path, version.id, sha256, content.length, 'manual'); const { GET } = await import('../../src/routes/api/servers/[name]/scripts/+server.js'); const body = await (await GET({ params: { name: 'target' } })).json(); expect(body.scripts).toEqual([ expect.objectContaining({ relPath: path, kind, artifactId: null, version: null, releaseVersion: null, scriptVersionId: version.id, sha256, size: content.length, source: 'manual' }) ]); } ); });