import { describe, it, expect, vi } from 'vitest';
import { checkSSRFStrict, isPrivateOrForbidden } from './ssrf-strict.js';

describe('isPrivateOrForbidden', () => {
  it('blocks IPv4 loopback', () => {
    expect(isPrivateOrForbidden('127.0.0.1', 4)).toBe(true);
  });
  it('blocks IPv4 RFC1918 ranges', () => {
    expect(isPrivateOrForbidden('10.0.0.1', 4)).toBe(true);
    expect(isPrivateOrForbidden('172.16.0.1', 4)).toBe(true);
    expect(isPrivateOrForbidden('10.0.0.10', 4)).toBe(true);
  });
  it('blocks IPv4 link-local and IMDS', () => {
    expect(isPrivateOrForbidden('169.254.169.254', 4)).toBe(true);
  });
  it('blocks IPv6 loopback and mapped', () => {
    expect(isPrivateOrForbidden('::1', 6)).toBe(true);
    expect(isPrivateOrForbidden('::ffff:127.0.0.1', 6)).toBe(true);
  });
  it('blocks IPv6 unique-local', () => {
    expect(isPrivateOrForbidden('fc00::1', 6)).toBe(true);
    expect(isPrivateOrForbidden('fd00::1', 6)).toBe(true);
  });
  it('blocks IPv6 link-local', () => {
    expect(isPrivateOrForbidden('fe80::1', 6)).toBe(true);
  });
  it('blocks AWS IMDSv6 prefix', () => {
    expect(isPrivateOrForbidden('fd00:ec2::254', 6)).toBe(true);
  });
  it('blocks NAT64 prefix', () => {
    expect(isPrivateOrForbidden('64:ff9b::1', 6)).toBe(true);
  });
  it('allows a public IPv4', () => {
    expect(isPrivateOrForbidden('8.8.8.8', 4)).toBe(false);
  });
  it('allows a public IPv6', () => {
    expect(isPrivateOrForbidden('2001:4860:4860::8888', 6)).toBe(false);
  });
});

describe('checkSSRFStrict', () => {
  it('rejects non-https schemes', async () => {
    const res = await checkSSRFStrict('http://example.com');
    expect(res.ok).toBe(false);
  });

  it('rejects if any resolved IP is private', async () => {
    const fakeLookup = vi.fn().mockResolvedValue([
      { address: '8.8.8.8', family: 4 },
      { address: '::1', family: 6 }, // AAAA points to loopback — should reject
    ]);
    const res = await checkSSRFStrict('https://example.com', { lookup: fakeLookup });
    expect(res.ok).toBe(false);
  });

  it('accepts when all resolved IPs are public', async () => {
    const fakeLookup = vi.fn().mockResolvedValue([
      { address: '8.8.8.8', family: 4 },
    ]);
    const res = await checkSSRFStrict('https://example.com', { lookup: fakeLookup });
    expect(res.ok).toBe(true);
    if (res.ok) {
      expect(res.pinnedIp).toBe('8.8.8.8');
      expect(res.family).toBe(4);
    }
  });
});
