ktg-plugin-marketplace/plugins/llm-security-copilot/tests/hooks/update-check.test.mjs
Kjell Tore Guttormsen f418a8fe08 feat(llm-security-copilot): port llm-security v5.1.0 to GitHub Copilot CLI
Full port of llm-security plugin for internal use on Windows with GitHub
Copilot CLI. Protocol translation layer (copilot-hook-runner.mjs)
normalizes Copilot camelCase I/O to Claude Code snake_case format — all
original hook scripts run unmodified.

- 8 hooks with protocol translation (stdin/stdout/exit code)
- 18 SKILL.md skills (Agent Skills Open Standard)
- 6 .agent.md agent definitions
- 20 scanners + 14 scanner lib modules (unchanged)
- 14 knowledge files (unchanged)
- 39 test files including copilot-port-verify.mjs (17 tests)
- Windows-ready: node:path, os.tmpdir(), process.execPath, no bash

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 21:56:10 +02:00

79 lines
2.7 KiB
JavaScript

// update-check.test.mjs — Tests for hooks/scripts/update-check.mjs
// Zero external dependencies: node:test + node:assert only.
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { resolve } from 'node:path';
import { runHook, runHookWithEnv } from './hook-helper.mjs';
const SCRIPT = resolve(import.meta.dirname, '../../hooks/scripts/update-check.mjs');
// ---------------------------------------------------------------------------
// Unit tests for isNewer (imported directly)
// ---------------------------------------------------------------------------
import { isNewer, CHECK_INTERVAL_MS } from '../../hooks/scripts/update-check.mjs';
describe('isNewer — semver comparison', () => {
it('returns true when remote patch is higher', () => {
assert.equal(isNewer('2.8.1', '2.8.0'), true);
});
it('returns false when versions are equal', () => {
assert.equal(isNewer('2.8.0', '2.8.0'), false);
});
it('returns false when remote is older', () => {
assert.equal(isNewer('2.7.9', '2.8.0'), false);
});
it('returns true when remote major is higher', () => {
assert.equal(isNewer('3.0.0', '2.99.99'), true);
});
it('returns true when remote minor is higher', () => {
assert.equal(isNewer('2.9.0', '2.8.99'), true);
});
it('handles different length versions', () => {
assert.equal(isNewer('2.8.1', '2.8'), true);
assert.equal(isNewer('2.8', '2.8.0'), false);
});
});
describe('CHECK_INTERVAL_MS', () => {
it('is 24 hours in milliseconds', () => {
assert.equal(CHECK_INTERVAL_MS, 86_400_000);
});
});
// ---------------------------------------------------------------------------
// Integration tests (subprocess via hook-helper)
// ---------------------------------------------------------------------------
describe('update-check hook — opt-out', () => {
it('exits silently when LLM_SECURITY_UPDATE_CHECK=off', async () => {
const result = await runHookWithEnv(SCRIPT, '{}', {
LLM_SECURITY_UPDATE_CHECK: 'off',
});
assert.equal(result.code, 0);
assert.equal(result.stdout.trim(), '');
});
});
describe('update-check hook — graceful failures', () => {
it('exits 0 with empty stdin', async () => {
const result = await runHook(SCRIPT, '');
assert.equal(result.code, 0);
});
it('exits 0 with valid JSON stdin (no CLAUDE_PLUGIN_ROOT → fails to read plugin.json)', async () => {
// Without CLAUDE_PLUGIN_ROOT set to a valid plugin, it will fail to
// read plugin.json from the default path and exit 0 silently.
const result = await runHookWithEnv(SCRIPT, '{}', {
CLAUDE_PLUGIN_ROOT: '/nonexistent/path',
});
assert.equal(result.code, 0);
assert.equal(result.stdout.trim(), '');
});
});