Organizing Cases: Projects & Sections
Once you have more than a handful of YAMLs on the agent, the flat Test cases list becomes hard to navigate. Four orthogonal axes keep it tidy:
-
Project — a first-class container (TestRail-like), stored in a registry. One case belongs to at most one project. Used by the Project filter.
-
Section — free-text metadata copied from the TestRail section (e.g.
TR-181,TR-181 Mesh). The list sorts on it. -
TestRail run membership — which runs (e.g.
Run 319) a case belongs to, held in its own field and filled from TestRail, not inferred. -
Required ISP mode — whether the case needs Multi-Domain (ISP 1) or Multi-ISP (ISP 2), so it is only run where it can pass.
All four are pure organisation — they never change how a case converts or runs.
|
Run membership is not the project label
A case’s project records the import path; its When you need "everything in run 319", filter by the TestRail run axis or
backfill it (Sync runs… in the Library menu of Test cases,
The backfill rewrites case metadata non-atomically — never run it while a batch is executing. |
|
At a glance
|
Projects Registry
A project is a row in the projects SQLite table (scripts/case_db.py), sharing
the same DB file as the auth layer. Columns: name (primary key), description,
testrail_project_id, created_by, created_at.
| Endpoint | Role | Purpose |
|---|---|---|
|
any authed |
List projects, each with a live
|
|
admin / editor |
Create a project ( |
|
admin / editor |
Rename and/or set
|
|
admin / editor |
Delete the project (see Unassigned below). |
|
admin / editor |
Bulk-assign a list of files to one project. |
The "Unassigned" bucket
There is no literal Unassigned row in the registry. A case whose project is
empty ("") is unassigned, and the UI surfaces those under a virtual
Unassigned option in the Project filter.
Deleting a project does not delete its cases. The
DELETE /api/projects/{name} handler first walks every YAML, and for each case
that belonged to the deleted project it clears the project field (orphans
it to Unassigned), then removes the registry row. No test is ever lost by a
project delete.
|
Importing from TestRail auto-registers the project (insert-if-absent via
ensure_project, created_by="testrail-import"), so you rarely create one by
hand. New project in the Library menu of the Test cases header
(POST /api/projects) is for projects you want to exist before any case lands
in them.
|
Section Grouping & the Project Filter
The Test cases list is sorted by section first, then by case ID by
default, so cases sharing a section sit together. The Sort select in the
filter row offers the other orders — Title, Type, Priority, Reference,
Last run — and the section itself is one of the filters. The section value
comes straight from TestRail (test_case.section) and is editable in the
Editor; Set section… in the selection
bar writes one section onto every ticked case at once.
In the filter row a Project dropdown narrows what you see:
-
All projects — no project filter (default).
-
Unassigned — only cases with an empty
project. -
<name> (N) — only that project’s cases;
Nis the live count.
The filter is independent of the Portal filter and the search box — they compose (project AND portal AND text match). The same dropdown appears in the Editor sidebar.
Case Metadata Fields
Beyond section, each case carries a small set of organisational fields.
project and section live at the top level of test_case; the rest live under
test_case.metadata. They surface in the list as pills/links and in the Editor
as form inputs.
| Field | Stored as | Rendering |
|---|---|---|
Project |
|
Drives the Project filter and |
Section |
|
Free text; groups the list. |
Reference |
|
A Jira issue key (e.g. |
Priority |
|
A coloured pill — Critical (red), High (amber), Medium (blue), Low (green). |
Type |
|
A grey pill (e.g. |
Author |
|
Who imported vs. who last saved an edit. Set automatically; not editable by hand. |
TestRail runs |
|
Which TestRail runs the case
belongs to. Drives the TestRail run filter (behind More filters). Filled on
a run import and by Sync runs…; kept deliberately separate from Project.
The case list exposes it as |
Required ISP mode |
|
|
Claims |
computed |
|
Blocked |
|
Present only on
blocked cases. Rendered as a |
The jira_base_url is read from Settings and exposed (read-only,
non-secret) at GET /api/ui-config. Leave it blank and references render as
plain text instead of links.
|
Populating the Fields
There are four ways org fields get filled, from most to least automatic.
1. TestRail import (automatic)
Importing a case resolves and registers its
Project from the case’s TestRail project/suite, and maps Priority /
Type / Reference from the TestRail priority_id / type_id / refs
into metadata. Bare-number refs are key-ified by the converter using a Jira
prefix (e.g. 11278 → DEV-11278).
2. Manual edit (Editor)
The Editor form has Project (dropdown),
Section, Priority, Type, and Reference inputs. Save persists them
into the YAML and stamps edited_by.
3. Bulk assign (multi-select)
Tick several rows and choose Add to project… — in the bulk bar of Test cases, or in the Editor sidebar’s bulk actions — to assign them all in one shot:
curl -X POST http://ai.friendly-tech.com:8083/api/tests/assign-project \
-H 'Content-Type: application/json' \
-d '{"files":["C7677.yaml","C7681.yaml"],"project":"TR-181 Mesh"}'
Path components in files are rejected (basename-only, no traversal). The
response reports {"assigned": N}.
4. Find / replace (multi-select)
Find / replace… in the same bulk bar rewrites one of title,
section, project, priority, type or reference across every ticked
case, previewed before it is written — see
test-management.adoc#bulk-update.
|
Which of these writes a version
Find / replace… and Set section… go through the case version recorder, so
each edit lands in the case’s change history with a Add to project…, a project rename or delete (both rewrite
|
Backfilling Org Fields (no re-convert)
After you have already imported a corpus, you often want to enrich the existing YAMLs with Project / Reference / Priority / Type pulled fresh from TestRail — without re-running Stage 2 LLM enrichment (which would re-cost and risk jitter) and without bumping the converter cache.
POST /api/testrail/backfill-fields does exactly that. It matches local YAMLs to
TestRail cases by numeric ID, computes the org-field diff, and patches only those
fields.
| Field | Meaning |
|---|---|
|
Required — the TestRail project + suite to read. |
|
Optional — narrow to one section. |
|
Defaults to |
|
Prefixes bare-number refs with a Jira key (e.g. |
# Preview (no write) — see matched / changed counts + per-case diffs
curl -X POST http://ai.friendly-tech.com:8083/api/testrail/backfill-fields \
-H 'Content-Type: application/json' \
-d '{"project_id":1,"suite_id":3,"ref_prefix":"DEV-"}'
# Apply
curl -X POST http://ai.friendly-tech.com:8083/api/testrail/backfill-fields \
-H 'Content-Type: application/json' \
-d '{"project_id":1,"suite_id":3,"ref_prefix":"DEV-","dry_run":false}'
The response carries matched (local YAMLs found in the suite), changed
(cases whose org fields actually differ), and a details list of per-file
diffs. Always run a dry_run first and read the diff before applying.
| Backfill is the right tool after a TestRail re-org (a case moved project, got a priority, gained a Jira ref). Use reimport only when the converted output (tree / parameters / steps) needs to change. |
See Also
-
Importing Tests — auto project/priority/type/ref assignment happens during import.
-
Editor Guide → Core Fields — editing these fields by hand.
-
Test Management — renaming these fields across many cases at once, and the trash, history and attachments a case gains alongside them.
-
API Reference — the full
/api/projectsand/api/testrail/backfill-fieldssurface.