Skip to content

Remote Test Automation Execution Modernized

opencode edited this page Sep 25, 2026 · 1 revision

Remote Test Automation Execution — Modernized Screen

Modernization of Remote Test Automation Execution (lib/testcases/tcExecute.php) — GitHub issue #1587.

tcExecute.php was the last standalone lib/testcases/* controller still rendering a full legacy page. It is replaced by a standalone Dashio page (gui/templates/testcases/tcAutoExec.html) backed by a plain-PHP REST BFF (api/tcautoexec/index.php). The ASIDE entry Test Case Design → Test Automation Execution sits right after Test Automation Specification (both gated on view_tc), and the legacy controller is kept as a session-guarded 302 shim.

URL: gui/templates/testcases/tcAutoExec.html?tproject_id=<id>&tplan_id=<id> BFF API: api/tcautoexec/index.php Rights: mgt_view_tc on the owning test project (fail closed — the legacy controller had no rights check at all) → 401 anonymous / 403 no right / 404 unknown project or node / 400 bad level, node or project / 405 wrong method / 403 missing same-origin proof on the POST.

Test Automation Execution - default state

Test Automation Execution - passed

Test Automation Execution - failed

Test Automation Execution - config problems

Test Automation Execution - connection failure

Test Automation Execution - access denied

Test Automation Execution - 200 cases truncated


Table of Contents

  1. What the screen does
  2. REST API Reference
  3. Legacy parity notes
  4. Bugs found and fixed
  5. i18n Keys
  6. Security
  7. Testing

1. What the screen does

Feature Legacy behavior Modern implementation
Context hidden form fields context card: test project (read-only, with id), test plan, build, platform
Target one flat "level" form LEVEL select: Single test case / Test suite / Whole test project + the matching node select
Server visibility only after the run every node in the select is prefixed with its resolved server and its source (test case vs. suite custom fields)
Result list one HTML table of raw XML rows summary counters + per-case table: test case (deep link), server, status badge, result, message, notes
Statuses rendered as text ANSWERED, CONFIG PROBLEMS, CONNECTION FAILURE as badges, localized
Volume unlimited (browser timeout) max 200 test cases per request + a truncation warning bar
No rights form rendered anyway, submit failed 403 rendered as an Access denied card

Plan changes reload the build list (builds are project scoped) and the platform list (platforms are test-plan scoped) and re-fetch the node list.

2. REST API Reference

GET ?action=init&tproject_id=N[&tplan_id=][&build_id=][&platform_id=]

{
  "status": "ok",
  "context": { "tproject_id": 107, "tproject_name": "AutoExec Demo",
               "tplan_id": 108, "build_id": 13, "platform_id": 10,
               "plans": [ ... ], "builds": [ ... ], "platforms": [ ... ] },
  "levels": {
    "testcase":    [ { "id": 113, "name": "AX1587-1", "server": "http://127.0.0.1:9999/xmlrpc.php",
                       "server_source": "testcase", "server_host": "127.0.0.1" } ],
    "testsuite":   [ ... ],
    "testproject": [ ... ]
  }
}

400 missing/unknown params · 401 anonymous · 403 no mgt_view_tc · 404 unknown project · 405 non-GET.

POST ?action=run

Form-encoded body: {level, node_id, tproject_id, tplan_id, build_id, platform_id}.

{ "status": "ok",
  "counters": { "total": 1, "ok": 1, "configProblems": 0,
                "connectionFailure": 0, "failed": 0 },
  "truncated": false,
  "results": [ { "tcase_id": 113, "tcversion_id": 114, "name": "AX1587-1",
                 "server": "http://127.0.0.1:9999/xmlrpc.php",
                 "server_source": "testcase",
                 "status": "ok", "result_status": "answered",
                 "result": "PASSED", "result_label": "Passed",
                 "notes": "executed by the mock server for #113",
                 "message": "Mock execution finished: AX1587-1",
                 "scheduled": "now", "timestampISO": "2026-09-25T10:00:00+00:00" } ] }

result_status / result_label come from taeResolveResultStatus(), which maps the XML-RPC code through the legacy code_status table (p → passed, f → failed, b → blocked). Unknown codes are reported verbatim, classified as failed, and — since #1588 — without any PHP warning.

The remote call itself is the legacy executeTestCase() (lib/functions/remote_exec.php), so the XML-RPC payload, the system status handling and the audit trail are identical to 1.9.20. tplan_api_key is resolved server-side only and never appears in any payload.

Automation server resolution (legacy cascade)

  1. tc_server_host / tc_server_port / tc_server_path on the test case (custom fields with the tc_ prefix), else
  2. tsuite_server_host / tsuite_server_port / tsuite_server_path on the test case's suite or any suite ancestor, else
  3. nothing → the test case is reported as CONFIG PROBLEMS without any remote call.

3. Legacy parity notes

  • level=testsuite collects the test cases of the suite and of every nested suite, ordered by spec order; level=testproject does the same from the project root.
  • The legacy subtree walk descends into the steps of every test case and asks tree::$node_tables for a testcase_step entry that does not exist → one E_WARNING per test case. The BFF passes the same exclude_children_of => testcase filter that testsuite::get_subtree() uses, so a 210-case run is warning free (core bug filed as #1589).
  • Statuses, messages and notes keep the legacy wording so existing automation servers (and existing documentation) need no change.

4. Bugs found and fixed

Issue Symptom Fix
#1588 lib/functions/remote_exec.php:125 looked the result code up in $code_status unguarded → PHP 8 E_WARNING "Undefined array key" for every non-conformant XML-RPC answer, plus a null label guarded lookup; the code is still displayed verbatim (e12b249d7)
(in #1587) only passed / failed were compared, so a b (blocked) answer was counted as passed taeResolveResultStatus() maps every code through code_status; counters and badges use it
#1589 tree::_get_subtree() walks into testcase_step children → one E_WARNING per test case in the Event Viewer contained in the BFF (init and run) with the exclude_children_of => testcase filter; core fix still open

Found by the code review of the change set:

Finding Fix
IXR_Client built without a timeout → a hung automation server blocks the request for the OS default 20s per remote call, surfaces as CONNECTION FAILURE
plan / build / platform ids of the XML-RPC context were not checked against the authorised project taeOwnsPlan() / taeOwnsBuild() / taeOwnsPlatform() drop foreign ids
a non-array (or result-less) XML-RPC answer made the legacy controller read an array offset on a non-array answer validated, reported as a config problem
the result badge printed a raw i18n key for the x / a result codes tae.status_not_available / tae.status_all added to the 10 bundles + fallback to the raw code
the "Open test case" fallback pointed at the non-existing testcases/searchTestCase.html now search/searchView.html
re-running the fixture reset the shared norights user and could delete the automation custom fields of a foreign project only creates the user when missing, skips fields linked to another project

5. i18n Keys

  • tae.* — 53 keys (titles, cards, context, target, counters, statuses, buttons, states, truncation bar) in all 10 client bundles (en, ro, de, es, fr, it, pt, ru, ja, zh).
  • tae.answered / tae.configProblems / tae.connectionFailure — the execution status column.
  • tae.status_passed / tae.status_failed / tae.status_blocked / tae.status_not_run / tae.status_not_available / tae.status_unknown / tae.status_all — the result column, one key per results.status_code domain of cfg/const.inc.php. A code the page does not know falls back to the raw code the automation server sent (never a raw i18n key).
  • footers.tcAutoExec — footer line.
  • href_tc_auto_exec — ASIDE label, added to all 19 locale/*/strings.txt.

No hardcoded strings on the page; the TLi18n locale switcher works like on every modern screen.

6. Security

  • Session-based auth (401 for anonymous), rights fail-closed on the owning project.
  • bffSameOriginGuard() CSRF proof required for the POST (403).
  • The testplan API key never leaves the server.
  • All ids are cast to int, all SQL uses the framework database class; node ownership is verified before the run.

7. Testing

Suite 1587 in tmp/TLU_Test_Cases.md — 24/24 PASS: the three levels, the custom-field cascade, pass / fail / config-problem / connection-failure rendering, the 200-case truncation bar, plan → build/platform re-init, the 400/401/403/404/405 + CSRF contract, unknown project, login redirect, the access-denied state, the ASIDE entries, the legacy shim, the ro locale, the i18n coverage, the #1588 and #1589 regressions, Event Viewer hygiene (0 new Error/Warning) and the lint/diff checks.

Fixtures: tmp/fixtures_1587.php (project 107, two plans, build 13, two platforms, three suites, three test cases, the six automation custom fields, the role-less norights user) and tmp/fixtures_1587_bulk.php (a 210-test-case suite for the truncation test). The XML-RPC double used for the runs is a throwaway script outside the repository.

Clone this wiki locally