Code Tracker Management - sebiboga/testlink-upgraded GitHub Wiki
Code Tracker Management permite integrarea TestLink cu sisteme de control al versiunilor (Git, GitHub, GitLab), legand testele de codul sursa si schimbarile aferente.
Path: System > Code Tracker Management
URL: gui/templates/codetracker/codetrackerView.html
Prerequisite: codetracker_management right
- Ce Este Code Tracker Management?
- De Ce Este Important?
- Sisteme Suportate
- Cum Se Configureaza
- Exemplu Practic — GitHub
- Diferenta Fata de Issue Tracker
- BFF API
- Git-nativ: GitHub Code Tracker
- Format Configuratie (XML) si Depanare
- Validarea Tipurilor de Code Tracker
- Link-count si delete gating (Refs #971)
In timp ce Issue Tracker urmareste bug-uri (probleme), Code Tracker urmareste codul sursa (commit-uri, branch-uri, pull requeste).
Fluxul complet:
Testare → Gasit bug → Create GitHub Issue (#234)
→ Developer face fix → Commit abc123 → "Fixes #234"
→ Pull Request #56 → Review → Merge in main
→ TestLink vede: commit abc123 inchide issue #234
→ TestLink: "TC-047 are linked commit abc123 si issue #234 CLOSED"
Code Tracker completeaza cercul de trasabilitate:
Requirements ←→ Test Cases ←→ Test Execution ←→ Bug Reports ←→ Source Code
↑ ↑
└──────────── Requirements Traceability ───────────────────────┘
Fara Code Tracker:
TC-012 esueaza → Bug #45 creat → Developer face fix → ...stoppasecum s-a intamplat?
Cu Code Tracker:
TC-012 esueaza → Bug #45 creat
→ Commit "fix: validate card expiry" (abc123)
→ PR #67 linked la Bug #45
→ Merge in main
→ TC-012: "Bug #45 CLOSED by commit abc123 via PR #67"
→ Testerul stie EXACT ce s-a schimbat
GitHub suporta atat Issue Tracking cat si Code Tracking:
| Ce urmaresti | Unde |
|---|---|
| Bug-ul | GitHub Issues → Issue Tracker |
| Codul | GitHub Commits/PR → Code Tracker |
Exemplu real — TestLink 2.0.1:
Proiect: sebiboga/testlink-upgraded
Bug: "Header shows login name instead of full name" (#523)
Fix: commit 54f9dd09a → "feat: i18n 10 locales + fix tabs async + fix #523"
PR: sebiboga/testlink-upgraded#1
Cu Code Tracker configurat:
- TestLink stie ca commit-ul
54f9dd09aa rezolvat issue #523 - Raportul arata: "TC-15.1 linked to commit 54f9dd09a, issue #523 CLOSED"
- Testerul poate vedea exact ce cod s-a schimbat
GitHub Actions workflow:
1. Push commit → ruleaza teste automat
2. Daca esueaza → creaza issue in GitHub
3. Daca trece → marcheaza commit-ul ca "tested"
4. TestLink vede statusul automat
| Sistem | API | Status | Descriere |
|---|---|---|---|
| GitHub | REST | ✅ Activ | Open source + private, cel mai popular |
| GitLab | REST | ✅ Activ | Open source, self-hosted option |
| Gitea | REST | ✅ Activ | Lightweight Git, self-hosted |
| Gogs | REST | ✅ Activ | Ultra-lightweight Git |
| Bitbucket | REST | ✅ Activ | Atlassian ecosystem |
Ordine in interfata: GitHub → GitLab → Gitea → Gogs → Bitbucket
- Navigheaza la System > Code Tracker Management
- Click "+ Create Code Tracker"
- Completeaza:
| Camp | Descriere | Exemplu |
|---|---|---|
| Name | Nume identificativ | "GitHub - TLU Repository" |
| Type | Tipul de sistem | GitHub REST API |
| Server URL | URL-ul de baza | https://github.com/ |
| API Key | Token de autentificare | ghp_xxxxxxxxxxxx |
-
Creeaza Personal Access Token:
- GitHub → Settings → Developer settings → Personal access tokens
- Scope:
repo - Copiaza token-ul
-
Creeaza Code Tracker in TestLink:
Name: GitHub - TestLink Repo Type: GitHub REST API Server URL: https://github.com/ API Key: ghp_TU_TOKEN_AICI -
Acum TestLink poate:
- Cauta commit-uri legate de issue-uri
- Vede daca un PR a fost merged
- Afiseaza statusul build-ului
- Link direct la commit/PR din TestLink
Context: Testam proiectul sebiboga/testlink-upgraded
1. Ruleaza TC-15.1: "Header Shows Full Name Not Login"
→ PASSED pe PR #1
→ TestLink: "TC-15.1 tested on commit 54f9dd09a (PR #1)"
2. Ruleaza TC-13.5: "All 6 Modernized Pages Have Locale Switcher"
→ FAILED
→ Creeaza issue: GitHub #12 "Custom Fields page missing locale switcher"
→ Link: TC-13.5 ↔ GitHub #12
3. Developer face fix:
→ Commit: "fix: add locale switcher to cfieldsView.html"
→ PR #2: "Fixes #12"
→ Merge in main
4. TestLink detecteaza:
→ Issue #12 CLOSED
→ Commit linked la TC-13.5
→ Status: "Retest needed — fix available in commit abc123"
5. Testerul retesteaza TC-13.5:
→ PASSED
→ Ciclul se inchide
TC-13.5: All Modernized Pages Have Locale Switcher
Status: PASSED (retested)
Code Links:
- Commit 54f9dd09a: "feat: i18n 10 locales" [MERGED]
- PR #1: "i18n support" [MERGED]
Issue Links:
- GitHub #12: "Missing locale switcher on cfields" [CLOSED]
Closed by: commit abc123
| Aspect | Issue Tracker | Code Tracker |
|---|---|---|
| Ce urmareste | Bug-uri, tichete | Commit-uri, PR-uri, branch-uri |
| Cand se foloseste | La raportarea unui bug | La legarea codului de test |
| Exemplu | "TC-012 esueaza → GitHub #45" | "Fix-ul e in commit abc123" |
| Status | OPEN / CLOSED / IN PROGRESS | MERGED / OPEN / PENDING |
| Integrare | Creare issue din TestLink | Vizualizare commit-uri existente |
Ambele impreuna ofera trasabilitate completa:
Test Case (TC-012)
├── Execution: FAILED
├── Issue: GitHub #45 "Payment bug"
├── Code: commit abc123 "Fix payment validation"
└── PR: GitHub #67 [MERGED]
Endpoint: /api/codetracker/index.php
| Method | Endpoint | Descriere |
|---|---|---|
GET |
/ |
Lista toate code trackerele |
GET |
/{id} |
Detalii un code tracker |
POST |
/ |
Creaza code tracker nou |
PUT |
/{id} |
Actualizeaza code tracker |
DELETE |
/{id} |
Sterge code tracker |
GET |
/meta/types |
Tipuri disponibile |
POST |
/test_github |
Testeaza conexiunea GitHub (repo/token) fara salvare (Reț.: GitHub nativ) |
GET |
/{id}/branches |
Branch-urile repo-ului (JSON) |
GET |
/{id}/tags |
Tag-urile repo-ului (JSON) |
GET |
/{id}/commits?branch=… |
Commit-urile pe branch (JSON) |
GET |
/{id}/pulls?state=open |
Pull request-urile (JSON) |
curl -b cookies.txt http://localhost:8082/api/codetracker/index.php{
"status": "ok",
"items": [
{
"id": 1,
"name": "GitHub - TLU Repository",
"type": 1,
"typeLabel": "github (Interface: rest)",
"cfg": "..."
}
],
"total": 1
}Pana la #433, GitHub (și toate sistemele git) foloseau o interfață generică modelată
după stash, configurată manual prin XML (câmpul uribase/apikey). Aceasta a fost
înlocuită pentru GitHub cu o interfață REST nativă:
-
Tip nou:
github (Interface: rest)— cod intern200 -
Clasă:
lib/codetrackerintegration/githubrestCodeTrackerInterface.class.php(înregistrată întlCodeTracker::$systemsși mapată explicit detlCodeTracker::getImplementationForType(200)) -
Metode:
connect(),isConnected(),getBranches(),getTags(),getCommits(),getPullRequests(),buildViewCodeURL()(blob/commit/pull),buildViewCodeLink()(PR-N,#N)
Când Type = github, modala de creare/editare afișează un formular dedicat și ascunde câmpul XML generic:
| Câmp | Descriere | Exemplu |
|---|---|---|
| Repository URL | URL-ul complet al repo-ului | https://github.com/sebiboga/testlink-upgraded |
| Branch | Branch-ul implicit de legat codul |
main / sebiboga
|
| Access Token | PAT opțional (obligatoriu pt. repo-uri private) | ghp_xxxxxxxx |
| Test Connection | Buton — verifică conectivitatea live înainte de salvare | — |
Verificare înainte de salvare: Test Connection face POST /api/codetracker/index.php/test_github
și afișează un banner verde/roșu. GitHub API (api.github.com) este interogat direct
prin cURL; repo-urile publice funcționează fără token.
![]()
![]()
Stocare: config-ul se salvează tot în XML în codetrackers.cfg
(<codetracker><repository>…</repository><branch>…</branch><token>…</token></codetracker>),
fără schimbare de schemă; interfața acceptă și JSON.
| Tablou | Remarcă |
|---|---|
githubrestInterface (issue-tracker) |
NU reutilizați acest nume pentru code-tracker — lib/issuetrackerintegration/githubrestInterface.class.php există deja și include_path-l rezolvă acel nume primul. Code-tracker-ul folosește githubrestCodeTrackerInterface. |
curl -b cookies.txt -H "X-Requested-With: XMLHttpRequest" \
-H "Content-Type: application/json" \
-d '{"repository":"https://github.com/sebiboga/testlink-upgraded","token":"","branch":""}' \
http://localhost:8082/api/codetracker/index.php/test_githubRăspuns reușit (repo public):
{ "status": "ok", "connected": true,
"defaultBranch": "sebiboga",
"branches": ["fix/issue-616","fix/issue-765-get-last-child-info","sebiboga"],
"branchCount": 3 }Pe eșec (404 / token invalid / fără rețea): "connected": false, iar trackerToJSON
maschează token-ul ca ******** la citire.
Fix (aug 2026): campul Configuration accepta EXCLUSIV XML valid. Un URL simplu
(https://github.com/user/repo.git) este respins — interfetele parsheaza configuratia
cu SimpleXML si asteapta taguri <uribase> / <apikey>.
<codetracker>
<uribase>https://git.example.com/</uribase>
<apikey>your-token</apikey>
</codetracker>De la fix, modala de creare/editare afiseaza permanent text de ajutor + exemplul de mai
sus sub camp (ct.cfgHelp), iar un config care nu incepe cu < este blocat client-side
cu mesaj prietenos localizat (ct.validation.xmlFormat), fara sa mai ajunga pe server.

| Defect | Simptom inainte | Dupa fix |
|---|---|---|
tlCodeTracker::create() isi suprascria starea de eroare cu rezultatul lui checkXMLCfg
|
nume duplicat + cfg valid ⇒ HTTP 200 "ok" cu item fantoma gol, nimic insertuit |
name already exists, HTTP 400 |
checkXMLCfg() testa rezultatul SimpleXML prin truthiness |
<codetracker></codetracker> (radacina goala) respins ca XML invalid, fara niciun detaliu libxml |
acceptat corect ($cfg === false) |

Detalii complete: Issue #432.
API-ul BFFAccepta orice integer la type, inclusiv un sistem inexistent. Un
request create cu type: 6 returna HTTP 200 si stoca o linie imposibil de
serializat; listarea si stergerea generau zece avertismente PHP 8.3.
Incepand cu #1577:
- tipul este normalizat strict si verificat impotriva sistemelor enabled;
- un tip necunoscut sau dezactivat returneaza HTTP 400
{"status":"error","code":"invalid_type","type":N}; - un corp JSON care nu este obiect returneaza HTTP 400
invalid_body; - la PUT, omiterea lui
typeramane o actualizare partiala si pastreaza tipul; - drepturile
codetracker_managementsunt verificate inaintea validarii; - UI-ul afiseaza mesajul localizat
ct.msg.invalidType.
Matricea de regresie a acoperit CRUD pentru tipurile 1/200, tranzitii
1↔200, tipuri invalide, corpuri malformate, permisiuni 401/403 si Event
Viewer: cele 10 avertismente pre-fix au ramas neschimbate, fara avertismente
noi.
Detalii complete si capturi: Bug fix Issue #1577.
In implementarea #970, lista modernă a primit deja link_count, iar UI-ul ascunde
iconița Trash pentru un tracker legat de un proiect. Pentru ca și răspunsul
de detaliu să păstreze contractul legacy, GET /api/codetracker/index.php/{id}
apelează acum relația getLinks($id) și numără proiectele legate înainte de
serializare. Astfel, un consumator al detail API nu mai vede valoarea implicită
0 pentru un tracker legat.
Verificarea browser a acoperit listă, detaliu, UI și apărarea server-side:
trackerul legat nu are Trash și DELETE direct răspunde HTTP 400. Detalii,
matricea de test și capturile sunt în
Task 971 — linked Code Tracker delete gating.
Un singur rand din codetrackers cu un type in afara setului
{1 = stash/rest, 200 = github/rest} (lib/functions/tlCodeTracker.class.php:28-29)
anula toata lista: GET /api/codetracker/index.php raspundea HTTP 500 cu
corp gol (0 octeti), gridul afisa doar cele 6 antete fara randuri si fara
footer, iar in Event Viewer se adaugau 6 avertismente log_level = 2 la fiecare
incarcare. Cauza: getImplementationForType() citea
$this->systems[$type] fara isset (:115), asa ca un tip necunoscut returna
stringul literal "Interface", iar sonda checkEnv (:569) arunca
Error: Class "Interface" not found in interiorul lui getAll() — inainte de
out(), deci pentru orice rol, inclusiv utilizatori cu doar
codetracker_view.
Starea poate aparea doar printr-o migrare/import, o baza de date editata manual
sau o implementatie eliminata intr-o versiune ulterioara: POST/PUT valideaza
tipul (api/codetracker/index.php:308), iar rutele cu token stocat il re-verifica
(:463 si githubInterfaceFor() :370).
Corectia este in biblioteca, nu in BFF:
-
getImplementationForType()returneazanullpentru un tip din afara mapei (:115-127) — elimina cele 4 avertismente de offset si acopera sigetByID():358(detaliu/edit) si legacycodeTrackerView.php:23; - blocul
checkEnvdingetAll()(:578-599) degradeaza doar randul respectiv (env_check_ok = false, mesaj gol) si continua lista;class_exists()este apelat cu@pentru ca autoloaderul (lib/functions/common.php:122) sa nu mai logeze cele doua avertismenteFailed opening …class.php(acelasi rationament ca inapi/reqmgrsystems/index.php:107); -
trackerToJSON()expunetypeKnown, iar gridul afiseaza in coloana Type mesajul deja localizatct.msg.invalidType(„Code Tracker type 5 is unknown.”) si in coloana Environment fallback-ulct.envKo— fara cheie i18n noua ( exista deja in toate cele 10 bundle-uri).
Randul degradat este si reparabil din UI: modalul de editare se deschide,
lista de tipuri ofera doar valori valide, iar salvarea cu un tip valid rescrie
randul (type 5 → 1), dupa care acesta apare verde OK.
Verificat pe baza noua: pre-fix HTTP 500 len=0 + fatal + 6 avertismente, post-fix
HTTP 200 len=314 cu randul listat, tipurile valide 1/200 in continuare verzi
OK, GET /{id} cu implementation: "", test_connection tot 400
„Unknown code tracker type”, console fara erori, events cu log_level = 2
neschimbat. Suita de regresie 1597: 7/7 PASS.
Detalii complete si capturi before/after: Bug fix Issue #1597.
![]()
![]()