Issue #3586324: Replace hardcoded admin path assertions in tests with...
Route-path test hardening
Problem
Production code in ai_context builds every admin link with Url::fromRoute(), but many tests asserted hardcoded URL strings (e.g. '/admin/config/ai/agents/debug?agent_id=' . $agent_id). When an upstream module moves a route — as ai_agents did in 1.3.2 by relocating agent URLs under /admin/config/ai/tools-automation/ — those tests fail even though ai_context behaves correctly, producing false negatives in CI on dependency updates unrelated to ai_context changes. #3586323 (closed) fixed one such assertion; this MR addresses the rest, plus the optional follow-ups from the issue (internal ai_context paths and outdated docs).
What was done
New shared test helper — tests/src/Traits/AiContextRoutePathTrait.php provides getRoutePath(string $route_name, array $route_parameters = [], array $options = []): string, a thin wrapper around Url::fromRoute(...)->toString(), now used by 22 test classes (Functional, FunctionalJavascript, and Kernel — all have a container).
High priority — external-route assertions (paths owned by other modules)
AiContextAgentSettingsTest: all debug/explore assertions derive fromai_agents_debugger.form/ai_agents_explorer.explorerwith the same['query' => ['agent_id' => ...]]option production uses (AiContextAgentsForm.php), including the occurrences embedded inelementsCount()CSS selectors (a[href*="..."]).AiContextAdminSettingsTest: the modules-filter link derives fromsystem.modules_listwith['query' => ['filter' => 'ai_context_document_loader']], mirroringAiContextExtensionsController.AiContextScopeLanguageTest/AiContextScopeTagTest(Kernel): manage-link URLs derive fromentity.configurable_language.collectionandentity.taxonomy_vocabulary.overview_form(with theAiContextScopeTag::VOCABULARY_IDparameter).AiContextDocumentLoaderIntegrationFunctionalTest(submodule): derives fromdocument_loader.settings_formanddocument_loader_mdx.dialog(route names verified in document_loader 2.0.x).AiContextLanguageDetectorTraitTest(Unit): no path changes — a class docblock now documents that the admin-style paths are intentional fixture data (see decisions below).
Medium priority — internal ai_context paths (~160 occurrences across 21 test files)
All drupalGet() / addressEquals() / linkByHrefExists() calls now derive from ai_context.routing.yml routes, ai_context_item entity link templates, the dynamic scope routes registered by AiContextScopeRouteSubscriber (ai_context.settings.scope.<plugin_id>), and the usage Views route (view.ai_context_usage.page_usage). Two kernel tests not listed in the issue (AiContextScopeSiteSectionTest, AiContextScopeTargetEntityTest) had the same manage-link pattern and were converted for consistency.
Low priority — docs
The four references to the pre-1.3.2 agents path /admin/config/ai/agents (README.md, docs/index.md, docs/developers/configuration.md, docs/features/agent_configuration.md) were updated to /admin/config/ai/tools-automation/agents, verified against the entity.ai_agent.collection route. Debug/explore doc paths were left untouched — those routes did not move. All other documented paths were audited against current routing and are correct.
Intentionally left hardcoded
AiContextScopeSettingsTest::testOldSiteSectionUrlNotFound()asserts a removed legacy path returns 404 — there is no route to derive it from. A comment now marks it as intentional.AiContextUsageTrackerTestfake paths andAiContextScopeSiteSectionTestpath-matching inputs — logic test data, out of scope per the issue.
Architectural decisions
- A trait rather than repeated inline
Url::fromRoute()— the issue proposed a shared helper; with 20+ call sites across many files it keeps call sites short, gives one place to change URL-generation behavior, and follows the existing convention of traits intests/src/Traits/. - Derive debug/explore paths before the mid-test module uninstalls —
testDebugAndExploreLinkVisibility()uninstallsai_agents_debuggerandai_agents_explorerpartway through;Url::fromRoute()throwsRouteNotFoundExceptiononce a route is gone. Paths are computed once at method start and stored in variables, with a code comment explaining why, so a future cleanup doesn't inline them back. $entity->toUrl('link-template')when an entity is in scope,getRoutePath()otherwise —toUrl()matches the existing exemplar (AiContextItemViewTest) and survives entity-type route changes automatically;getRoutePath()covers routing.yml-only routes (settings tabs, revision revert/delete confirm forms, dynamic scope routes). No entity is ever loaded solely to calltoUrl().- Local variables per test method by default;
setUp()properties only where repetition is heavy —AiContextItemListTestuses a$itemsListPathproperty for its 28 identical occurrences; everywhere else paths are derived where used, avoiding cross-test state. - Kernel manage-link assertions tightened from
assertStringContainsStringtoassertSame— both sides now come from the URL generator, so exact comparison is safe and stronger. - Unit test fixtures documented, not converted —
UnitTestCasehas no container, soUrl::fromRoute()is impossible there. The paths are Request/Referer parsing fixtures with no route dependency; keeping realistic paths preserves the docblock's documentation of the real AJAX scenario. The issue explicitly offered this option. - Redirect tests derive both sides — e.g.
drupalGet(getRoutePath('ai_context.context_redirect'))thenaddressEquals(getRoutePath('ai_context.overview')), so redirect source and destination both track route changes.
Testing instructions
Prerequisite for the submodule test only: the document_loader stack must be present or that test class self-skips (it is exercised in CI either way):
composer require drupal/document_loader:^2.0 drupal/ai_file_to_text
1. Run the changed test suites (from the Drupal project root; adjust -c to your phpunit config — SIMPLETEST_BASE_URL, SIMPLETEST_DB, and MINK_DRIVER_ARGS_WEBDRIVER must be set for Functional/FunctionalJavascript):
`# Unit + Kernel (fast):
vendor/bin/phpunit -c web/core web/modules/contrib/ai_context/tests/src/Unit
web/modules/contrib/ai_context/tests/src/Kernel
Functional:
vendor/bin/phpunit -c web/core web/modules/contrib/ai_context/tests/src/Functional
FunctionalJavascript (needs webdriver):
vendor/bin/phpunit -c web/core web/modules/contrib/ai_context/tests/src/FunctionalJavascript
Submodule (requires document_loader installed, otherwise skips):
vendor/bin/phpunit -c web/core web/modules/contrib/ai_context/modules/ai_context_document_loader/tests `
All suites should be green. Deprecation notices are pre-existing and unrelated.
2. Verify no brittle paths remain:
grep -rn "'/admin/config/ai" web/modules/contrib/ai_context/tests/src \ web/modules/contrib/ai_context/modules/*/tests/src | grep -v "getRoutePath\|toUrl"
Expected hits — exactly four, all intentional: the legacy-404 assertion in AiContextScopeSettingsTest and three unit-test fixture strings in AiContextLanguageDetectorTraitTest.
3. Confirm the resilience this MR buys (the actual point): temporarily change a route path, e.g. edit ai_context.routing.yml and change /admin/config/ai/context/settings/general to something else, clear caches, and re-run AiContextDebugLoggingTest — it still passes, where the old hardcoded assertion would have failed. Revert afterward.
4. Manual/CLI spot-check on a live site (optional): log in as an admin and confirm (a) /admin/config/ai/tools-automation/agents renders while /admin/config/ai/agents 404s — the docs fix; (b) the agents listing at /admin/config/ai/context/settings/agents shows Debug/Explore links matching /admin/config/ai/agents/debug|explore?agent_id=... (requires ai_agents_debugger/ai_agents_explorer enabled); (c) the extensions page links to /admin/modules?filter=ai_context_document_loader.
5. Linting (CI-blocking gates):
vendor/bin/phpcs --standard=Drupal,DrupalPractice web/modules/contrib/ai_context/tests \ web/modules/contrib/ai_context/modules/ai_context_document_loader/tests npx cspell --config web/modules/contrib/ai_context/.cspell.json "web/modules/contrib/ai_context/**"
Related
- #3586323 (closed) — agent edit form path assertion (fixed separately; pattern followed here)
- ai_agents #3586032 — upstream route move that exposed this failure class