Skip to content

Commit d581b15

Browse files
committed
eops-368-refactor(commands): drop explanatory comments and docstrings
1 parent 123f702 commit d581b15

4 files changed

Lines changed: 14 additions & 72 deletions

File tree

‎scripts/refresh_v2_routes.py‎

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,5 @@
11
#!/usr/bin/env python3
2-
"""
3-
Regenerate the committed snapshot of the public API v2 route table.
4-
5-
The CLI lives in its own repository, so it cannot import the backend to learn
6-
which routes exist. tests/test_route_conformance.py therefore checks the
7-
registered commands against a committed snapshot, and this script refreshes it
8-
from a backend checkout:
9-
10-
python scripts/refresh_v2_routes.py ~/src/dualentry/backend
11-
12-
Django is not a CLI dependency, so the schema is built by the backend's own
13-
interpreter: its .venv if present, otherwise `poetry run python`. Pass a third
14-
argument to point at a specific interpreter instead.
15-
16-
Re-run it whenever the public v2 API gains or loses routes.
17-
"""
2+
"""Regenerate the v2 route snapshot: refresh_v2_routes.py <backend> [python]."""
183

194
from __future__ import annotations
205

@@ -71,7 +56,6 @@ def main(argv: list[str]) -> int:
7156
sys.stderr.write(result.stderr)
7257
return result.returncode
7358

74-
# Django logs to stdout on startup, so take only the marked payload.
7559
_, _, payload = result.stdout.rpartition(_MARKER)
7660
try:
7761
routes = json.loads(payload)

‎src/dualentry_cli/commands/__init__.py‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,6 @@ def make_resource_app(
138138
app = typer.Typer(help=f"Manage {name}", no_args_is_help=True, cls=HelpfulGroup)
139139
enabled_filters = filters or set()
140140
base, detail = f"/{path}/", f"/{path}/{{}}/"
141-
# Every request the registered commands can issue. Append next to the command
142-
# that issues it; tests/test_route_conformance.py checks the result against the
143-
# real v2 route table so a resource can never gain a command the API lacks.
144141
operations: list[tuple[str, str]] = [("GET", base)]
145142

146143
@app.command("list")

‎src/dualentry_cli/commands/accounts.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,4 @@ def get_account(
3939
format_output(data, resource="account", fmt=output)
4040

4141

42-
# Declared by hand because these commands are not built by make_resource_app.
43-
# tests/test_route_conformance.py checks them against the real v2 route table.
4442
app.api_operations = [("GET", "/accounts/"), ("GET", "/accounts/{}/")]

‎tests/test_route_conformance.py‎

Lines changed: 13 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,4 @@
1-
"""
2-
Every command the CLI registers must map to a route the public API v2 has.
3-
4-
make_resource_app() registers create/update by default, which silently produced
5-
commands for resources that are read-only in v2 - they shipped in --help and
6-
returned 404 or 405 on every run. These tests close that off.
7-
8-
The check is one-directional: each request a registered command can issue must
9-
exist in v2. The reverse - v2 operations with no command - is expected and
10-
tracked separately.
11-
12-
The route table is a committed snapshot because the CLI is a separate
13-
repository from the backend; refresh it with scripts/refresh_v2_routes.py.
14-
"""
1+
"""Check registered commands against the routes public API v2 exposes."""
152

163
from __future__ import annotations
174

@@ -25,13 +12,10 @@
2512

2613
FIXTURE = Path(__file__).parent / "fixtures" / "public_api_v2_routes.json"
2714
PREFIX = "/public/v2"
28-
29-
# Command groups that manage the CLI itself rather than an API resource.
3015
NON_RESOURCE_GROUPS = {"auth", "config"}
3116

3217

3318
def _v2_routes() -> dict[str, set[str]]:
34-
"""Map each v2 path to its methods, with path params normalised to '{}'."""
3519
routes: dict[str, set[str]] = {}
3620
for path, methods in json.loads(FIXTURE.read_text()).items():
3721
assert path.startswith(PREFIX), f"unexpected path in fixture: {path}"
@@ -41,7 +25,6 @@ def _v2_routes() -> dict[str, set[str]]:
4125

4226

4327
def _walk(typer_app, prefix: tuple[str, ...] = ()) -> tuple[list, list]:
44-
"""Split sub-apps into those declaring api_operations and those skipped."""
4528
checked, skipped = [], []
4629
for group in typer_app.registered_groups:
4730
if group.name in NON_RESOURCE_GROUPS:
@@ -52,8 +35,6 @@ def _walk(typer_app, prefix: tuple[str, ...] = ()) -> tuple[list, list]:
5235
if operations is not None:
5336
checked.append((" ".join(name), operations))
5437
elif sub.registered_commands:
55-
# A sub-app with commands but no declared operations would pass the
56-
# conformance check below simply by never being looked at.
5738
skipped.append(" ".join(name))
5839
sub_checked, sub_skipped = _walk(sub, name)
5940
checked.extend(sub_checked)
@@ -64,14 +45,24 @@ def _walk(typer_app, prefix: tuple[str, ...] = ()) -> tuple[list, list]:
6445
RESOURCES, UNCHECKED = _walk(app)
6546
OPERATIONS = [(command, method, path) for command, ops in RESOURCES for method, path in ops]
6647

48+
STALE_COMMANDS = [
49+
("companies", "create"),
50+
("companies", "update"),
51+
("budgets", "create"),
52+
("budgets", "update"),
53+
("depreciation-books", "create"),
54+
("depreciation-books", "update"),
55+
("paper-checks", "create"),
56+
("paper-checks", "update"),
57+
("inbox", "get"),
58+
]
59+
6760

6861
def test_no_resource_app_escapes_the_check():
69-
"""Guard the introspection, so a sub-app cannot pass by being invisible."""
7062
assert UNCHECKED == [], f"these command groups declare no api_operations and are never checked: {UNCHECKED}"
7163

7264

7365
def test_resource_apps_are_discoverable():
74-
"""A walk that silently returned nothing would make every check below vacuous."""
7566
assert len(RESOURCES) > 30
7667

7768

@@ -83,21 +74,6 @@ def test_command_maps_to_a_real_v2_route(command: str, method: str, path: str):
8374
assert method in available, f"'dualentry {command}' calls {method} {path}, but v2 allows only {sorted(available)}"
8475

8576

86-
# Named explicitly so that re-adding any one of them fails here with a direct
87-
# message, not only as a route-table mismatch above.
88-
STALE_COMMANDS = [
89-
("companies", "create"),
90-
("companies", "update"),
91-
("budgets", "create"),
92-
("budgets", "update"),
93-
("depreciation-books", "create"),
94-
("depreciation-books", "update"),
95-
("paper-checks", "create"),
96-
("paper-checks", "update"),
97-
("inbox", "get"),
98-
]
99-
100-
10177
@pytest.mark.parametrize(("resource", "command"), STALE_COMMANDS)
10278
def test_stale_command_is_not_registered(resource: str, command: str):
10379
group = next(g for g in app.registered_groups if g.name == resource)
@@ -106,25 +82,12 @@ def test_stale_command_is_not_registered(resource: str, command: str):
10682

10783

10884
def test_accounts_declaration_covers_its_commands():
109-
"""
110-
Accounts declares its operations by hand, so additions must update them.
111-
112-
Pinning the command set is the point here: a new command has to be
113-
accompanied by a matching api_operations entry, or this fails.
114-
"""
11585
group = next(g for g in app.registered_groups if g.name == "accounts")
11686
registered = {c.name for c in group.typer_instance.registered_commands}
11787
assert registered == {"list", "get"}, "update api_operations in commands/accounts.py to match"
11888

11989

12090
def test_paper_checks_has_no_number_lookups():
121-
"""
122-
Paper checks are keyed by primary key and expose no record number.
123-
124-
_resolve_by_internal_id() matches on 'internal_id' then refetches by
125-
'number'; PublicPaperCheckSchemaOut has neither, so the number-based
126-
lookups could never resolve.
127-
"""
12891
group = next(g for g in app.registered_groups if g.name == "paper-checks")
12992
registered = {c.name for c in group.typer_instance.registered_commands}
13093
assert "get" in registered

0 commit comments

Comments
 (0)