"""The exit-code contract, and the banner that should appear exactly once. Exit codes are three-valued on purpose: 0 worked, 1 the command ran and reported findings, 2 it was called wrong. Before that split, `nexa ask` with no question printed help and returned 0 -- so `nexa ask odoo "$Q" || exit 1` reported success for an empty $Q. Every row of the table below is a contract, and the comments mark the ones that used to return the wrong number. Most of these call main() in-process: it returns the code rather than exiting, so the assertion is direct, it runs in milliseconds, and a coverage run can actually see the branches being taken. A handful go through a real subprocess at the bottom, because in-process calls would never catch a broken __main__ block. """ import os import subprocess import sys import pytest import nexa ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) OK, FINDINGS, MISUSE = 0, 1, 2 @pytest.fixture(autouse=True) def fresh_banner(monkeypatch): """banner(once=True) is process-global, and every test here is a new run.""" monkeypatch.setattr(nexa, "_BANNER_SHOWN", False) @pytest.fixture(autouse=True) def in_repo(monkeypatch): """nexa resolves the corpus relative to its own file, but check and index take paths from the caller's working directory.""" monkeypatch.chdir(ROOT) def main(*args): return nexa.main(list(args)) def shell(*args): proc = subprocess.run([sys.executable, os.path.join(ROOT, "nexa.py")] + list(args), capture_output=True, text=True, encoding="utf-8", errors="replace", cwd=ROOT) return proc.returncode, (proc.stdout or "") CASES = [ ([], OK, "bare invocation prints the command list"), (["version"], OK, "machine-readable, and the one command with no banner"), (["--version"], OK, "the flag spelling"), (["help"], OK, "help with no topic is the command list"), (["help", "ask"], OK, "help for a real command"), (["help", "bogus"], MISUSE, "was 1: an unknown topic is misuse, not a finding"), (["bogus"], MISUSE, "was 1"), (["ask"], MISUSE, "was 0 -- the bug that motivated the split"), (["ask", "odoo"], MISUSE, "was 0: a stack with no question is still misuse"), (["run"], MISUSE, "was 0"), (["run", "odoo"], MISUSE, "was 0"), (["index"], MISUSE, "was 0"), (["check"], MISUSE, "was 0"), (["check", "no_such_file_here.py"], MISUSE, "was 1: a bad path is a bad argument"), (["rules"], OK, "the whole corpus"), (["rules", "onchange"], OK, "a term that matches"), (["rules", "zzzznomatch"], FINDINGS, "ran fine, found nothing -- NOT misuse"), (["rules", "--stack", "odoo"], OK, "the one stack, named"), (["rules", "--full", "odoo.onchange-is-not-validation"], OK, "a real id"), (["rules", "--full", "no.such.rule"], MISUSE, "was 1: an id that does not exist"), (["validate"], OK, "corpus is schema-valid"), (["crosscheck"], OK, "informational, never fails the build"), (["coverage"], OK, "reports its own blind spots"), (["adversarial"], OK, "all three probes"), (["adversarial", "evasion"], OK, "one probe by name"), ] @pytest.mark.parametrize("args,expected,why", CASES, ids=[" ".join(c[0]) or "(none)" for c in CASES]) def test_exit_code(args, expected, why, capsys): assert main(*args) == expected, "{0}: {1}".format(" ".join(args) or "(none)", why) def test_empty_search_and_bad_id_are_different_codes(capsys): """The distinction the three-valued scheme exists for: 'I looked and found nothing' is a result; 'that id does not exist' is a mistake.""" assert main("rules", "zzzznomatch") == FINDINGS assert main("rules", "--full", "no.such.rule") == MISUSE def test_usage_error_still_shows_the_help_text(capsys): """Exiting non-zero is no reason to withhold the thing the user needs. Asserted against the usage string the command actually declares, rather than a copy of it: this used to spell out the stack list and went red when a fourth stack was added, which is not what it is testing. """ assert main("ask") == MISUSE assert nexa.COMMANDS["ask"][1] in capsys.readouterr().out def test_unknown_command_points_back_at_the_command_list(capsys): for args in (["bogus"], ["help", "bogus"]): nexa._BANNER_SHOWN = False main(*args) out = capsys.readouterr().out assert "unknown command" in out assert "for the command list" in out, "both spellings give the same hint" def test_commands_table_and_dispatch_agree(capsys): """A command listed in help with no dispatch branch would print its help and then fall through to 'unknown command'.""" for name in nexa.COMMANDS: nexa._BANNER_SHOWN = False assert main("help", name) == OK assert "unknown command" not in capsys.readouterr().out # ---------------------------------------------------------------- the banner def test_banner_prints_once_per_process(capsys): nexa.banner(subtitle=False, once=True) nexa.banner(subtitle=False, once=True) assert capsys.readouterr().out.count("NEXA") == 1 def test_banner_without_once_always_prints(capsys): nexa.banner(subtitle=False) nexa.banner(subtitle=False) assert capsys.readouterr().out.count("NEXA") == 2 def test_every_command_wears_the_banner(capsys): for args in (["rules"], ["validate"], ["coverage"], ["adversarial"], ["bogus"]): nexa._BANNER_SHOWN = False main(*args) assert "NEXA" in capsys.readouterr().out, "{0} printed no banner".format(args) def test_version_is_the_one_command_without_a_banner(capsys): main("version") out = capsys.readouterr().out assert out.strip() == "nexa {0}".format(nexa.VERSION) # ------------------------------------------------------ the real entry point def test_subprocess_entry_point_exits_with_the_returned_code(): """In-process tests never execute the __main__ block, so sys.exit(main()) and the KeyboardInterrupt handler around it would go unchecked.""" assert shell("version")[0] == OK assert shell("bogus")[0] == MISUSE assert shell("ask")[0] == MISUSE def test_subprocess_version_is_parseable(): code, out = shell("version") assert code == OK assert out.strip() == "nexa {0}".format(nexa.VERSION) def test_subprocess_output_is_clean_when_not_a_terminal(): """Piped output must carry no escape codes: COLOR is gated on isatty, and a redirect that captured raw ANSI would make `nexa rules > file` unreadable.""" code, out = shell("rules", "onchange") assert code == OK assert "\033" not in out # --------------------------------------------- the wordmark and its underbar def _art_rows(art): """The art's rows, indentation intact, without the blank lines the triple- quoted literal starts and ends with.""" return [row for row in art.splitlines() if row.strip()] def test_both_spellings_are_the_same_height(): """banner() bands by row index, so a fallback of a different height would paint the bar as a letterform and vice versa.""" assert len(_art_rows(nexa.BANNER_UNICODE)) == len(_art_rows(nexa.BANNER_ASCII)) # The wordmark, and the bar under it. assert len(_art_rows(nexa.BANNER_UNICODE)) == nexa.BANNER_ROWS + 1 def test_the_bar_is_as_wide_as_the_wordmark(): """The bar is light thrown by the sign. One that overhangs the letters, or falls short of them, reads as a rule somebody drew underneath instead.""" for art in (nexa.BANNER_UNICODE, nexa.BANNER_ASCII): widths = set(len(row) for row in _art_rows(art)) assert len(widths) == 1, art def test_the_ascii_fallback_is_actually_ascii(): """It exists for a console on a legacy code page. A block-drawing character surviving into it defeats the entire point.""" assert nexa.BANNER_ASCII.isascii() def test_glyphs_lists_every_non_ascii_character_the_module_can_print(): """GLYPHS is what _enable_utf8 probes a declared encoding with. A glyph added to the UI and not to GLYPHS makes the probe pass on an encoding that cannot hold it, and the character reaches the terminal anyway -- the exact crash the fallback exists to prevent, reintroduced one character at a time. Asserted against the source rather than against the constants, because those are already degraded to ASCII when UNICODE is False and would have nothing to say in the run that matters. Comments are excluded. They are never printed, and the ones describing the bar name the very characters this is looking for. """ import io import tokenize with open(os.path.join(ROOT, "nexa.py"), encoding="utf-8") as handle: source = handle.read() printable = set() for token in tokenize.generate_tokens(io.StringIO(source).readline): if token.type == tokenize.COMMENT: continue printable.update(ch for ch in token.string if ord(ch) > 127) assert printable, "the scan found nothing, so it is not reading the art" missing = sorted(printable - set(nexa.GLYPHS)) assert not missing, "used but not in nexa.GLYPHS: " + " ".join(missing) def _cube(n): """An xterm-256 cube index as its (r, g, b) coordinates, each 0-5.""" n -= 16 return (n // 36, (n % 36) // 6, n % 6) def test_the_ramp_sweeps_cyan_to_magenta(): """Endpoints, asserted against the NUMBERS rather than the escape codes. Under NO_COLOR every code is the empty string, so a test written against codes would pass for any ramp at all, which is worse than not testing it. """ assert nexa.BANNER_RAMP[0] == 51, "starts on cyan" assert nexa.BANNER_RAMP[-1] == 201, "ends on magenta" assert nexa.BANNER_BAR == 214, "amber, what the rest of the CLI warns in" assert nexa.BANNER_BAR not in nexa.BANNER_RAMP, ( "the bar contrasts with the wordmark rather than continuing it") def test_the_steps_between_are_an_interpolation_not_a_shortlist(): """The whole claim of a ramp. Cyan to magenta the short way round holds blue at maximum while red rises and green falls; a colour picked because somebody liked it breaks one of those three, and the gradient develops a kink no amount of squinting at a screenshot reliably catches.""" steps = [_cube(n) for n in nexa.BANNER_RAMP] assert all(b == 5 for _r, _g, b in steps), steps reds = [r for r, _g, _b in steps] greens = [g for _r, g, _b in steps] assert reds == sorted(reds), "red must rise monotonically" assert greens == sorted(greens, reverse=True), "green must fall monotonically" assert len(set(nexa.BANNER_RAMP)) == len(nexa.BANNER_RAMP), ( "two rows sharing a colour is a ramp with a step missing") def test_the_readme_shows_the_banner_that_ships(): """The README drifted silently once already -- it was still showing an outlined NX after the art had changed twice, and that stale block reached two public remotes before anybody noticed. A wordmark in the README is a claim about what the command prints, so it is the same art or it is wrong.""" path = os.path.join(ROOT, "README.md") with open(path, encoding="utf-8") as handle: readme = handle.read() for row in _art_rows(nexa.BANNER_UNICODE): assert row in readme, "README is missing this row of the wordmark: " + row def test_the_rendered_palette_is_built_from_the_ramp(): """And the codes that reach the terminal come from those numbers rather than from a second, hand-kept list that can drift from them.""" assert nexa.WORDMARK_GRADIENT == [nexa.c(n) for n in nexa.BANNER_RAMP] assert nexa.BAR_COLOUR == nexa.c(nexa.BANNER_BAR) def test_the_wordmark_row_count_matches_the_hue_count(): """One hue per letterform row. If they drift, the banding wraps and two rows share a colour.""" assert len(nexa.WORDMARK_GRADIENT) == nexa.BANNER_ROWS def test_the_banner_prints_once_in_a_real_run(): """`python nexa.py` leaves the name `nexa` unclaimed, so nexa_cmds doing `import nexa` loaded a SECOND copy of the module -- with its own COLOR, its own UNICODE and its own _BANNER_SHOWN. banner(once=True) then printed twice on `doctor` and `build`, each copy believing it was the first. Only a subprocess can see this. Under pytest the module is imported once under its own name, the two never diverge, and the test above passes while the CLI prints two banners. """ _, out = shell("doctor") assert out.count("validation intelligence") == 1, out def test_the_banner_says_whose_work_it_is(capsys): """The byline rides under the wordmark on every command that prints one, including the short form. Read from the same two constants the README footer and LICENSE spell out, so the three cannot drift apart.""" nexa.banner(subtitle=False) out = capsys.readouterr().out assert "created by " + nexa.AUTHOR in out assert nexa.YEAR in out def test_version_stays_bare(capsys): """The byline must not reach the one line a script parses.""" assert nexa.main(["version"]) == 0 assert capsys.readouterr().out.strip() == "nexa " + nexa.VERSION