Skip to content

Stop a client wildcard from covering literal endpoint routes - #210

Merged
Sev7eNup merged 2 commits into
mainfrom
test/endpoint-client-coverage-strict-matching
Aug 15, 2026
Merged

Stop a client wildcard from covering literal endpoint routes#210
Sev7eNup merged 2 commits into
mainfrom
test/endpoint-client-coverage-strict-matching

Conversation

@Sev7eNup

Copy link
Copy Markdown
Owner

EndpointClientCoverageTests exists to enforce "Jeder neue API-Endpoint braucht beide Clients" at the level of endpoint existence — and it reported the surface as covered while the effective-sizing endpoint shipped without a CLI client (#89).

Cause

Two independent leniencies, both in the test:

  • IsCovered accepted a wildcard on either side. The CLI's api/admin/settings/{section} normalizes to api/admin/settings/*, which then matched every literal sibling route under that path. This applies wherever a client has a by-id/by-name/by-section call next to literal siblings — i.e. everywhere it mattered.
  • NormalizeRoute collapsed any segment containing { to *. The query-string idiom $"api/alerting/deliveries{qs}" therefore became api/alerting/*: the deliveries call site both lost the route it actually calls and gained a wildcard covering catalog, preview-filter and preview-rule.

The matcher had no test of its own, which is why this regressed invisibly. It has one now (7 cases pinning both rules).

Surfaced gaps

Tightening surfaces 4 CLI + 5 MCP routes on current main. Two are closed here:

  • np alerting catalog + MCP get_alerting_catalog — rules are authored as JSON, so the vocabulary (event types, filter-referenceable fields, deliverable channels) has to be readable outside the web UI. Mirrors the existing np system-alert catalog.
  • GET /api/workflows/by-name/{name}WorkflowResolver used to list all workflows and filter client-side, with a comment claiming the route did not exist. np now resolves names exactly like the engine, API and trigger path do (exact case wins, then case-insensitive, ambiguity is an error).

Four stay as documented known gaps: preview-filter / preview-rule (stateless dry-runs for the rule builder's live preview, same category as the already-documented system/preview), and MCP-side workflows/export / workflows/{id}/move-folder (bulk export and folder RBAC already have documented MCP gaps).

Tests

  • EndpointClientCoverageTests — new Matcher_TreatsWildcardsAsParametersOnly theory + both guards for both clients.
  • WorkflowResolverTests — rewritten around the by-name endpoint, including 404/409 translation and "the list endpoint is not called".
  • CommandIntegrationAlertingTests.AlertingCatalog_RendersFieldsAndChannels, AlertingToolsTests.GetAlertingCatalog_ReturnsTheRuleVocabulary.

Run: dotnet test tests/NodePilot.Cli.Tests --filter "EndpointClientCoverage|ApiDtoParity|WorkflowResolver|CommandIntegrationAlerting" → 33 passed; tests/NodePilot.Mcp.Tests --filter "AlertingToolsTests|DocumentationCountsTests" → 27 passed.

Doc surfaces updated for the new tool (DocumentationCountsTests enforces the count): CLAUDE.md, README, docs/mcp-server.md, docs/alerting.md, docs-site cli.md + mcp-server.md.

Closes #179

EndpointClientCoverageTests enforces "every API endpoint needs both
clients", but its matcher accepted a wildcard on either side. A client
call like api/admin/settings/{section} normalizes to
api/admin/settings/* and then silently covered every literal sibling
route under that path — which is how the effective-sizing endpoint
shipped without a CLI client while the guard reported the surface as
covered (#89).

Two fixes:

- IsCovered now requires segment equality, so a client wildcard only
  satisfies an endpoint parameter.
- NormalizeRoute keeps a segment's literal prefix. The query-string
  idiom $"api/alerting/deliveries{qs}" collapsed to api/alerting/*,
  which both lost the route it actually calls and handed the matcher a
  wildcard covering catalog/preview-filter/preview-rule.

The matcher itself had no test — the reason this regressed invisibly —
so it gets one covering both rules.

That surfaces six real gaps. Two are closed:

- np alerting catalog + the get_alerting_catalog MCP tool. Rules are
  authored as JSON, so the vocabulary (event types, filter fields,
  channels) has to be readable outside the web UI.
- WorkflowResolver resolves names through GET /api/workflows/by-name,
  which it previously claimed did not exist. np now matches the engine,
  the API and the trigger path (exact case wins, then case-insensitive,
  ambiguity is an error) instead of dragging the whole workflow list
  over the wire to filter it client-side.

The remaining four are documented gaps: the two stateless rule-builder
previews, plus workflows/export and move-folder on the MCP side, which
follow the existing bulk-export and folder-RBAC gaps.

Closes #179
@Sev7eNup
Sev7eNup merged commit 1e64673 into main Aug 15, 2026
8 of 9 checks passed
@Sev7eNup
Sev7eNup deleted the test/endpoint-client-coverage-strict-matching branch August 15, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EndpointClientCoverageTests: a client wildcard silently covers literal endpoint routes

1 participant