Skip to content

flightcheck: add SN-CFG-002 — ServiceNow portal base URL populated in template config#169

Open
GrahamMcMynn wants to merge 2 commits into
mainfrom
auto/flightcheck-issue-168-servicenow-portal-base-url-populated-in-te
Open

flightcheck: add SN-CFG-002 — ServiceNow portal base URL populated in template config#169
GrahamMcMynn wants to merge 2 commits into
mainfrom
auto/flightcheck-issue-168-servicenow-portal-base-url-populated-in-te

Conversation

@GrahamMcMynn

Copy link
Copy Markdown
Collaborator

Implements the FlightCheck check requested in #168.

Checkpoint: SN-CFG-002
Category: ServiceNow
Priority: MEDIUM
Conditional? Yes — only runs when ServiceNow flows are detected (same gate as run_servicenow_checks, which early-returns when runner._servicenow_flows is empty).

API tier: documented
Mock source:

  • Dataverse Web API v9.2 (documented per tests/fixtures/cassettes/INDEX.md API tier registry). msdyn_value field name confirmed against the production selector in solutions/ess-maker-skills/scripts/backup_template_configs.py; new documented mock builder tests/mocks/dataverse.py::template_config cites that plus the MS Learn Dataverse query docs.

What it validates: Extends SN-CFG-001 from config presence to value populated. Reads msdyn_value for each ServiceNow template config and verifies it contains a populated, well-formed absolute http(s) portal base URL. WARNs (naming the offending configs) when the value is blank, carries an unsubstituted placeholder (e.g. {{ServiceNowBaseUrl}}), or is only a relative path — the silent failure where "read all tickets" / "read all cases" responses omit hyperlinks with no deploy-time error.

Tests: tests/flightcheck/checks/test_servicenow_deep.py — covers GOOD (all populated → Passed), BAD (blank / placeholder / relative-path → Warning with remediation), plus NotConfigured, Skipped, and query-error branches. 27 passing locally across the ServiceNow suites.

Fixes #168

… template config

Extends the ServiceNow deep validation from config *presence* (SN-CFG-001)
to config *value populated*. SN-CFG-002 reads the `msdyn_value` column of
the `msdyn_employeeselfservicetemplateconfigs` Dataverse entity for each
ServiceNow template config and verifies it carries a populated, well-formed
absolute http(s) portal base URL. When the base URL is blank, an
unsubstituted placeholder (e.g. `{{ServiceNowBaseUrl}}`), or only a relative
path, the check WARNs and names the offending configs — catching the silent
failure where "read all tickets" / "read all cases" responses omit
hyperlinks at runtime without surfacing any deploy-time error.

API tier: Dataverse Web API v9.2 is the `documented` tier per
tests/fixtures/cassettes/INDEX.md. No cassette required. The `msdyn_value`
field name is confirmed against the production selector in
backup_template_configs.py; a documented mock builder
(`dataverse.template_config`) is added for the tests. Gating mirrors the rest
of run_servicenow_checks (early-return when no ServiceNow flows are detected)
and the no-token / no-config / query-error paths mirror SN-CFG-001.

Tests in tests/flightcheck/checks/test_servicenow_deep.py cover GOOD (all
populated -> Passed), BAD (blank value, unsubstituted placeholder, relative
path -> Warning with remediation), plus NotConfigured, Skipped, and
query-error branches.

Fixes #168

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@GrahamMcMynn GrahamMcMynn left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review by ESS Code Review Agent (Auto mode)


candidates = _ABSOLUTE_URL_RE.findall(value)
has_real_url = any(_is_absolute_http_url(c) for c in candidates)
if has_real_url:

@GrahamMcMynn GrahamMcMynn Jun 25, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SN-CFG-002 can still pass an unsubstituted placeholder when the placeholder appears in the host. For example, https://{{ServiceNowBaseUrl}}/api/now/table/incident is matched as https://{{ServiceNowBaseUrl, and _is_absolute_http_url() accepts it because it has an http(s) scheme and non-empty netloc, so has_real_url becomes true. That is one of the bad states this check is meant to catch. Please reject placeholder tokens before accepting URL candidates (or validate the parsed host excludes template tokens) and add a test for this scheme-prefixed placeholder case.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d240a6c. _is_absolute_http_url now rejects any netloc containing placeholder characters ({}<>%$ ), so https://{{ServiceNowBaseUrl}}/... no longer passes. Added a regression test pinning the scheme-prefixed form (plus a bare-token case).

runner = SimpleNamespace(env_url="https://org.crm.dynamics.com", dv_token="t")
cfg = _by_id(_check_template_config_base_urls(runner), "SN-CFG-002")

assert cfg.status == "Warning"

@GrahamMcMynn GrahamMcMynn Jun 25, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests/AGENTS.md requires every WARNING test to assert specific phrases from both result and remediation, including the impact and concrete fix path. This warning test only pins result; test_base_url_relative_path_only_warns and test_base_url_query_error_warns below have the same gap. As written, the remediation text for these actionable warnings could regress or disappear without failing tests. Please add remediation assertions for each SN-CFG-002 Warning branch.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d240a6c. Both warning tests now assert remediation phrases (https://.service-now.com and the omit/hyperlinks impact), and a new bare-placeholder warning test was added.

Comment thread tests/mocks/dataverse.py
verify the ServiceNow portal base URL inside the value is a
populated, well-formed absolute http(s) URL.

Source (documented):

@GrahamMcMynn GrahamMcMynn Jun 25, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documented-tier mock requirements in tests/AGENTS.md require the builder docstring to cite an anchored vendor doc and include the documented example response verbatim. This builder cites generic Dataverse query docs plus backup_template_configs.py, but it does not include a documented example for the msdyn_employeeselfservicetemplateconfigs shape/fields it returns. That leaves tests depending on a shape that is not verified by one of the permitted tiers. Please add the required documented example evidence, or promote this surface with cassette/schema evidence before using the builder in FlightCheck tests.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d240a6c. The emplate_config builder docstring now carries the verbatim field contract (cited to backup_template_configs.py \ + record mapping) wrapped in a documented OData v4 collection-envelope example.

…holder hosts

Code review of #169 found SN-CFG-002 would falsely PASS a value like
`https://{{ServiceNowBaseUrl}}/api/...`: the placeholder host parses to a
non-empty netloc, so the old `_is_absolute_http_url` accepted it. Tighten the
host check to reject any netloc containing placeholder characters (`{}<>%$ `)
so an unsubstituted base-URL token never counts as a populated base URL.

Also strengthen the tests and mock per the review:
- The placeholder warning test now pins the scheme-prefixed form
  (`https://{{ServiceNowBaseUrl}}/...`) that triggered the bug, plus a new
  bare-token case; both warning tests now assert remediation phrases as
  required by tests/AGENTS.md.
- The documented-tier `dataverse.template_config` builder docstring now
  includes the verbatim field contract (cited to the backup_template_configs.py
  $select + mapping) inside a documented OData envelope example.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nehaoss

nehaoss commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

👋 Friendly reminder: This PR has been inactive for a while. Could the author or reviewers take a look and either push it forward, request changes, or close it if it's no longer needed? Let's keep our PR queue healthy! 🚀

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.

FlightCheck: Add check for ServiceNow portal base URL populated in template config

2 participants