"""Tests for the Click CLI.""" from pathlib import Path from click.testing import CliRunner from spice2wireviz.cli import main FIXTURES = Path(__file__).parent / "fixtures" class TestCLIBasic: def test_version(self): runner = CliRunner() result = runner.invoke(main, ["--version"]) assert result.exit_code == 0 assert "2026.2.14" in result.output def test_list_subcircuits(self): runner = CliRunner() result = runner.invoke(main, [str(FIXTURES / "multi_board.net"), "--list-subcircuits"]) assert result.exit_code == 0 assert "power_supply" in result.output assert "amplifier" in result.output assert "io_board" in result.output def test_list_components(self): runner = CliRunner() result = runner.invoke(main, [str(FIXTURES / "multi_board.net"), "--list-components"]) assert result.exit_code == 0 assert "J_CHASSIS" in result.output assert "TP_VCC" in result.output def test_nonexistent_file(self): runner = CliRunner() result = runner.invoke(main, ["/nonexistent/file.net"]) assert result.exit_code != 0 class TestCLISingleModule: def test_single_mode_explicit(self): runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "simple_board.net"), "-m", "single", "-s", "amplifier_board"], ) assert result.exit_code == 0 assert "connectors:" in result.output assert "amplifier_board" in result.output def test_single_mode_auto_detect(self): runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "simple_board.net"), "-s", "amplifier_board"] ) assert result.exit_code == 0 assert "connectors:" in result.output def test_single_mode_only_subcircuit(self): """When netlist has one subcircuit and no instances, auto-detect single mode.""" runner = CliRunner() result = runner.invoke(main, [str(FIXTURES / "simple_board.net")]) assert result.exit_code == 0 assert "connectors:" in result.output def test_dry_run(self): runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "simple_board.net"), "-s", "amplifier_board", "--dry-run"], ) assert result.exit_code == 0 assert "Mode: single" in result.output assert "Connectors:" in result.output class TestCLIInterModule: def test_inter_mode_explicit(self): runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "multi_board.net"), "-m", "inter"] ) assert result.exit_code == 0 assert "connectors:" in result.output assert "cables:" in result.output def test_inter_mode_auto_detect(self): runner = CliRunner() result = runner.invoke(main, [str(FIXTURES / "multi_board.net")]) assert result.exit_code == 0 assert "connectors:" in result.output def test_output_to_file(self, tmp_path): out = tmp_path / "output.yml" runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "multi_board.net"), "-o", str(out)] ) assert result.exit_code == 0 assert out.exists() content = out.read_text() assert "connectors:" in content class TestCLIFiltering: def test_no_ground(self): runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "multi_board.net"), "--no-ground"] ) assert result.exit_code == 0 # GND should not appear in pinlabels assert "- GND" not in result.output def test_include_prefixes(self): runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "multi_board.net"), "--include-prefixes", "J"], ) assert result.exit_code == 0 def test_exclude_refs(self): runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "multi_board.net"), "--exclude-refs", "X1,TP_VCC"], ) assert result.exit_code == 0 assert "X1:" not in result.output class TestCLIAscInput: """Tests for .asc file input through the CLI.""" def test_asc_with_companion_works(self): """simple_board.asc has companion .net — full pipeline should work.""" runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "simple_board.asc"), "-s", "amplifier_board"], ) assert result.exit_code == 0 assert "connectors:" in result.output assert "amplifier_board" in result.output def test_asc_companion_resolution_in_stderr(self): """CLI should report which .net was resolved to stderr.""" runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "simple_board.asc"), "-s", "amplifier_board"], ) assert result.exit_code == 0 assert "simple_board.net" in result.stderr def test_asc_without_companion_errors(self): """standalone.asc has no companion .net — diagram generation should fail.""" runner = CliRunner() result = runner.invoke(main, [str(FIXTURES / "standalone.asc")]) assert result.exit_code != 0 assert "connectivity" in result.output.lower() or "connectivity" in ( getattr(result, "stderr", "") or "" ).lower() def test_asc_without_companion_shows_remediation(self): """Error message should tell the user how to fix it.""" runner = CliRunner() result = runner.invoke(main, [str(FIXTURES / "standalone.asc")]) assert result.exit_code != 0 stderr = result.stderr assert "--generate-netlist" in stderr or "--list-components" in stderr def test_asc_list_components_on_metadata_only(self): """--list-components should work even without connectivity.""" runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "standalone.asc"), "--list-components"], ) # Should succeed (exit 0) — inspection doesn't need connectivity assert result.exit_code == 0 def test_asc_list_subcircuits_with_companion(self): """--list-subcircuits through .asc with companion works.""" runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "simple_board.asc"), "--list-subcircuits"], ) assert result.exit_code == 0 assert "amplifier_board" in result.output def test_asc_dry_run_with_companion(self): """--dry-run through .asc with companion produces summary.""" runner = CliRunner() result = runner.invoke( main, [str(FIXTURES / "simple_board.asc"), "-s", "amplifier_board", "--dry-run"], ) assert result.exit_code == 0 assert "Mode: single" in result.output