// git.test.mjs — Integration tests for the git-forensics scanner
//
// The evil-project-health fixture is not a standalone git repo, but it may sit
// inside a parent git repo. The scanner uses `git rev-parse` which walks up the
// directory tree, so it may detect the parent repo. Both 'skipped' (truly no git)
// and 'ok' (parent repo detected) are valid outcomes.
//
// This test suite verifies:
//   - Graceful handling: status is 'ok' or 'skipped', never 'error' with no findings
//   - Correct structure of the scanner result envelope
//   - All findings (if any) have the DS-GIT- prefix

import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { resetCounter } from '../../scanners/lib/output.mjs';
import { scan } from '../../scanners/git-forensics.mjs';

const __dirname = fileURLToPath(new URL('.', import.meta.url));
const FIXTURE = resolve(__dirname, '../../examples/malicious-skill-demo/evil-project-health');
// The plugin root — may or may not be a standalone git repo
const PLUGIN_ROOT = resolve(__dirname, '../..');

describe('git-forensics integration', () => {
  beforeEach(() => {
    resetCounter();
  });

  it('returns skipped or ok for the fixture directory (graceful handling)', async () => {
    // If the fixture is inside a git repo, scanner returns 'ok'.
    // If it is a bare directory with no git ancestry, scanner returns 'skipped'.
    const result = await scan(FIXTURE, {});
    const validStatuses = ['ok', 'skipped'];
    assert.ok(
      validStatuses.includes(result.status),
      `Expected 'skipped' or 'ok', got '${result.status}'`
    );
  });

  it('returns 0 or few findings for the fixture directory', async () => {
    // The fixture has no git history of its own. If the parent repo is detected,
    // findings reflect the parent repo's accumulated history. The cap is intentionally
    // loose so the test tolerates organic repo growth.
    const result = await scan(FIXTURE, {});
    if (result.status === 'skipped') {
      assert.equal(result.findings.length, 0, 'skipped should produce 0 findings');
    } else {
      assert.ok(
        result.findings.length <= 100,
        `Expected <= 100 findings for fixture dir (parent repo detected), got ${result.findings.length}`
      );
    }
  });

  it('scanner name is git-forensics', async () => {
    const result = await scan(FIXTURE, {});
    assert.equal(result.scanner, 'git-forensics', `Expected 'git-forensics', got '${result.scanner}'`);
  });

  it('returns ok or skipped for the plugin root (graceful handling)', async () => {
    resetCounter();
    const result = await scan(PLUGIN_ROOT, {});
    const validStatuses = ['ok', 'skipped', 'error'];
    assert.ok(
      validStatuses.includes(result.status),
      `Expected ok/skipped/error for plugin root, got '${result.status}'`
    );
  });

  it('findings count is reasonable for the plugin root', async () => {
    // Loose cap — git-forensics findings accumulate with repo history, so the
    // assertion tolerates growth while still catching runaway/pathological output.
    resetCounter();
    const result = await scan(PLUGIN_ROOT, {});
    if (result.status === 'skipped') {
      assert.equal(result.findings.length, 0);
    } else {
      assert.ok(
        result.findings.length <= 100,
        `Expected <= 100 findings for plugin root, got ${result.findings.length}`
      );
    }
  });

  it('all findings have DS-GIT- prefix', async () => {
    resetCounter();
    const result = await scan(FIXTURE, {});
    const wrongPrefix = result.findings.filter(f => !f.id.startsWith('DS-GIT-'));
    assert.equal(
      wrongPrefix.length, 0,
      `All git findings should have DS-GIT- prefix. Wrong: ${wrongPrefix.map(f => f.id).join(', ')}`
    );
  });

  it('counts object has expected severity keys', async () => {
    const result = await scan(FIXTURE, {});
    const expectedKeys = ['critical', 'high', 'medium', 'low', 'info'];
    for (const key of expectedKeys) {
      assert.ok(key in result.counts, `counts should have key '${key}'`);
    }
  });

  it('duration_ms is a non-negative number', async () => {
    const result = await scan(FIXTURE, {});
    assert.ok(typeof result.duration_ms === 'number', 'duration_ms should be a number');
    assert.ok(result.duration_ms >= 0, 'duration_ms should be non-negative');
  });
});
