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:
- 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 requirementdoc_id/ title of any project by sending an arbitraryroot_node/ node id. Same class of bug as #1696 on the siblinggetrequirementnodes.php. - No project scope and no node-type gate —
display_children()joined onparent_idonly, so the walk escaped the requirement tree entirely. - Unreachable in practice —
tlTestCaseFilterByRequirementControlhas zero users in the tree, so no shipped UI ever displayed it. Meanwhile the legacy drag-drop write it advertised (dragdroprequirementnodes.php, #1681) mutatednodes_hierarchyon 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_reqormgt_modify_reqis 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). coverageadditionally requiresmgt_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 = 1links 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 pastsessionInactivityTimeoutstops 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|disabledandIntegration on|offchips, a coverage summary with an Only uncovered requirements filter, and the lazytest project → specification → #1699 container → requirementtree with livex / y coveredchips. - 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.

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.

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

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.phpstill 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.