Skip to content

Testing plan: an inline url for the Slack integration

Derived from the approved requirements.md and design.md. Authored at test-planning, completed at verification.

This file is executable content — it names commands an agent runs. Credentials appear by reference only; the URLs below are https://hooks.slack.example/… fakes that reach nothing.

Test matrix

#TypeApplies?Scope / what it provesWhere it runs
T1Unityesprecedence in _SlackBase._url() through resolve(): inline only, inline over env, env only, neither; and both transports resolving through the same methoduv run --project cli python -m pytest -q cli/tests/test_graph_integrations.py
T2Integration (scenario)yesthe URL an operator wrote in the config is the URL notify posts to, end to end through resolve() and the hookuv run --project cli python -m pytest -q cli/tests/test_graph_slack_url_integration.py
T3Contract (OpenAPI / GraphQL SDL)n/a — the control-plane API is untouched; this work item adds no endpoint and changes no response shape
T4End-to-endn/a — an end-to-end row would mean posting to a real Slack workspace. the-loop does not test Slack; the HTTP boundary is the contract, and T2 verifies our side of it
T5UI / visualn/a — no user-facing surface (CLI/daemon only)
T6Snapshotn/a — no rendered output or serialized artifact changes
T7Performance / loadn/a — one dictionary lookup replaces one environment lookup on a path that already makes an HTTP call
T8Security / abuse caseyesthe three abuse cases of design.md § Security design: a non-string url refused by the schema, an empty url falling back rather than disabling, and the failure message naming sources but never the URLuv run --project cli python -m pytest -q cli/tests/test_graph_integrations.py -k "abuse or refused or empty or remedies"
T9Accessibilityn/a — no user interface
T10Migration / upgradeyes (as a non-migration)a CLI config written before this change validates and behaves identically, and the schema version is unchanged — the claim that no migrate-config step is neededuv run python scripts/validate_config.py + uv run --project cli python -m pytest -q cli/tests/test_docs_parity.py cli/tests/test_migrations.py
T11Manual exploratoryn/a — every behaviour is reachable from a unit or integration test; a manual pass would only re-run them by hand

Scenarios & requirement trace

RowRequirement(s)Scenario / case
T1R1.1, R1.2, R1.3inline-only resolves to the inline URL; inline + env resolves to the inline URL; env-only resolves to the env URL
T1R3.3sdk and webhook resolve through the same _url() — asserted over both transports
T1R3.1the pre-existing positional constructor (SlackWebhook("X")) still builds a working provider
T2R1.1, R1.2Scenario: a notification is delivered to the URL configured inline
T2R1.3Scenario: a configuration with no inline url still reads the environment
T8R1.4 / abuse 1a non-string url fails schema validation
T8abuse 2an empty url falls back to the environment
T8R2.1 / abuse 3the failure names the config key and the env var, and contains neither URL
T10R3.2the schema version is unchanged; a pre-change config validates

Verification environment

  • Repositories: this repository only.
  • Services / containers: none. Every row runs offline — the Slack HTTP boundary is faked in-process, and no row makes a network call.
  • Fixtures & data: in-test config mappings and fake webhook URLs on the reserved hooks.slack.example host. No fixture files are added.
  • Credentials: none. THE_LOOP_SLACK_WEBHOOK_URL is set and deleted by name through monkeypatch within tests; no real webhook exists in this repository.
  • Bring-up: make install-dev · Tear-down: none.
  • If bring-up fails: record it under Verification results, leave the dependent rows unticked, and escalate.

Evidence plan

RowEvidencePath under evidence/
T1, T8unit + abuse-case run output, with countsunit.md
T2the Gherkin scenario table and the run outputintegration.md
T10validate_config.py output and the unchanged-version checkunit.md
allthe whole-suite and repository gates (make test, make lint format-check typecheck validate)gates.md

Redaction: nothing captured contains a credential — the only URLs in the output are the hooks.slack.example fakes — so the evidence is committed as captured.

Verification activities

  • [x] T1 — uv run --project cli python -m pytest -q cli/tests/test_graph_integrations.py
  • [x] T2 — uv run --project cli python -m pytest -q cli/tests/test_graph_slack_url_integration.py
  • [x] T8 — uv run --project cli python -m pytest -q cli/tests/test_graph_integrations.py -k "refused or empty or remedies"
  • [x] T10 — uv run python scripts/validate_config.py and uv run --project cli python -m pytest -q cli/tests/test_docs_parity.py
  • [x] Whole suite — make test
  • [x] Repository gates — make lint format-check typecheck validate

Verification results

ActivityCommand / procedureOutcomeEvidence
T1pytest -q cli/tests/test_graph_integrations.pypass — 23 passed; 6 of them red before the source changeevidence/unit.md
T2pytest -q cli/tests/test_graph_slack_url_integration.pypass — 3 passed; 2 of the 3 verified red against the pre-change resolver (the third is the R3 regression guard, green on both sides)evidence/integration.md
T8pytest -q cli/tests/test_graph_integrations.py -k "refused or empty or remedies"pass — 9 passed, 14 deselected (the selector also picks up the pre-existing transport-refusal tests)evidence/unit.md
T10uv run python scripts/validate_config.py; pytest -q cli/tests/test_docs_parity.pypass — 7 configs VALID; 5 parity assertions pass; CURRENT_CONFIG_VERSION unchanged at 0.4.0 and no version line in the schema diffevidence/unit.md
Whole suitemake testpass — 1796 passed, 1 skipped (1782 before this work item)evidence/gates.md
Gatesmake lint format-check typecheck validatepass — ruff clean, 0 markdown errors, pyright 0 errors, 7 configs VALIDevidence/gates.md

Not executed: none. Every row marked yes ran; the n/a rows carry their reason in the matrix.

Review comments

Appended by the-loop's record-feedback hook when a human gate approves with comments (issue-109).

Released under the MIT License.