Modernize Requirement Coverage Tree reqCoverageTree - sebiboga/testlink-upgraded GitHub Wiki

Modernize: Requirement Coverage Tree (reqCoverageTree) — Refs #1765

Modern screen: gui/templates/requirements/reqCoverageTree.html BFF: api/reqcoveragetree/index.php Entry point: $actions->reqCoverageTree in lib/functions/common.php Legacy predecessor: lib/ajax/getreqcoveragenodes.php (now a non-mutating 302/405 shim) Tests: suite T1765 = 50/50 PASS in tmp/TLU_Test_Cases.md

Why

In 1.9.20 the coverage navigator was the ExtJS tree built by tlTestCaseFilterByRequirementControl::build_tree_menu() and filled by lib/ajax/getreqcoveragenodes.php. It answered which requirements of a project are covered by a test case, and it was also the drag-and-drop source tree of the create-test-suite / create-test-case, assign-keywords and assign-requirements popups. Three problems:

  1. No rights check at all — only testlinkInitPage(), i.e. a session check. Any authenticated user, including one with no requirement right, could read the specification and requirement doc_id / title of any project by sending an arbitrary root_node / node id. Same class of bug as #1696 on the sibling getrequirementnodes.php.
  2. No project scope and no node-type gate — display_children() joined on parent_id only, so the walk escaped the requirement tree entirely.
  3. Unreachable in practice — tlTestCaseFilterByRequirementControl has zero users in the tree, so no shipped UI ever displayed it. Meanwhile the legacy drag-drop write it advertised (dragdroprequirementnodes.php, #1681) mutated nodes_hierarchy on a plain GET.

API

Route Purpose
GET ?action=projects every project the caller may read requirements in (drives the switcher)
GET ?action=init&tproject_id=N context (project, prefix, requirements enabled/integration on, rights view/modify, user), totals, has_containers, the top-level specification nodes
GET ?action=children&tproject_id=N&node_id=M the children of one node — specifications, #1699 containers or requirements
GET ?action=coverage&tproject_id=N&req_id=R the test cases that cover one requirement (name, external id, TC version, requirement version, active / closed-by-execution)

Contract: 401 anonymous or expired session, 403 no right / CSRF, 404 unknown or foreign project / node / requirement, 405 write verb, 400 bad parameter, with stable machine codes (no_right_req_view, unknown_project, unknown_node, unknown_requirement, …).

Security decisions worth remembering:

  • mgt_view_req or mgt_modify_req is checked on the addressed project before the project is resolved, so a foreign and a non-existent id cannot be told apart.
  • Every node id is proven to live inside that same project (owningProjectId() walks requirement → version → specification → project and resolves a #1699 container through its parent specification).
  • coverage additionally requires mgt_view_tc / mgt_modify_tc: it returns test case names, external ids and versions, and the requirement and test-case right groups are independent.
  • Only is_active = 1 links count as coverage; a link closed by execution stays visible in the panel but does not make the requirement "covered".
  • bffEnforceSession() on every route — a tab idle past sessionInactivityTimeout stops reading.

The screen

  • Project strip that stays usable in 403/404 (those are exactly the states in which the caller must reach another project).
  • Toolbar: Refresh, Expand all, Collapse all, Assign test cases, Specification management, Move / reorder, Close — the three links follow the selected project.
  • Context card with Requirements enabled|disabled and Integration on|off chips, a coverage summary with an Only uncovered requirements filter, and the lazy test project → specification → #1699 container → requirement tree with live x / y covered chips.
  • Coverage panel per requirement, localized loading / empty / no-project / requirements-disabled / denied / not-found / bad-request states and a real read-only banner that disables the writes with an explanation.

Bugs found while testing

#1766 — nested containers (#1699) were unusable (fixed here). The ownership walk treated every node_type_id = 1 node as a project root and returned its own id, so a container never matched the addressed project: expanding it answered 404 unknown_node, and its requirements were missing from the specification count, the per-node chips, the coverage percentage and the totals. A container now resolves through its parent specification, and specStats() counts the containers a specification parents.

Screen defects (all fixed):

Symptom Cause
one click → two GETs and duplicated rows the row and its button carried the handler attribute; the delegated queue fires once per matching ancestor
Collapse all never collapsed; an expanded spec kept the collapsed icon cached children rendered unconditionally; the twisty read a hoisted-but-unassigned open
doc id rendered CVT-CVT-1 a requirement doc_id is already qualified, only the TC external_id needs the prefix
a 403 showed "the server could not answer this request" jQuery routes 4xx to error, so the machine code had to be recovered from the body
icons invisible fa-plussquare / fa-file-text-o render nothing in Font Awesome 6
toolbar links acted in the previous project buildLinks() ran once at load
a 403/404 was a dead end the switcher lived inside the hidden card
one Event Viewer warning per count fetch_array() answers false on an exhausted rowset, and the coverage rows were read without an isset() guard

Mandatory code review (subagent)

4 BLOCKER (test-case right missing on coverage, bffEnforceSession() never called, the stale tproject_id in the toolbar links, the dead-end 403/404 state) and 12 MINOR (stale children answers after a switch, a node-existence oracle in the type gate, inactive projects in the switcher, duplicated clicks, a broken "open specification" link on a container row, BUSY not driving the toolbar, machine codes shown as UI text, dead code) — all fixed.

Screenshots

docs/screenshots/issue-1765-reqCoverageTree-{tree,403,readonly}.png.

Tree with the coverage panel open

Specification A expanded, the #1699 container with its 0 / 1 covered chip, a covered requirement with its coverage panel (test case, version, requirement version, active coverage) and two uncovered ones.

Access denied, switcher still usable

A user without mgt_view_req on the project: an honest not authorized card with the project switcher still available.

Read-only banner

A view-only user: the banner explains that assigning and reordering are disabled, and both toolbar actions are disabled while the tree itself stays fully browsable.

See also

  • #1696 — the sibling loader getrequirementnodes.php still has no rights check (open).
  • #1699 — the #1699 containers hardened by reqTreeReorder.
  • #1695 / reqSpecListTree — the specification-tree navigator this screen is a sibling of.
  • Modernize-Move-Reorder-Test-Suites-suiteMove — the other end of the retired drag-drop pair.