Skip to content

Commit 433148b

Browse files
#89: add normalize-tests service and lineage handling (#90)
* feat: add normalize-tests service and lineage handling - Implemented the `normalize-tests` command to validate, build, and write UI test catalogs. - Introduced a lineage table to map input scenarios to user stories and functionalities. - Created unit tests for the new service, ensuring proper functionality and lineage coverage. - Added shared fixtures for testing and established expected output for the UI test catalog. - Updated requirements to include the new normalize_tests package. * fix: refine logic for qualified scenario IDs and enhance unit tests for fallback behavior * feat: implement centralized error handling for CLI commands and update documentation
1 parent c7b8e5f commit 433148b

41 files changed

Lines changed: 1671 additions & 130 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/commands/implement-task.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Task-list source (optional override): `$2`
4747
## 4. Run the `make qa` loop until green
4848

4949
- Run `make qa` for a full-repo change, or `make qa-<alias>` for the package you touched
50-
(`<alias>` ∈ `core`, `normalize-docs`, `cli`) — the same targets CI runs.
50+
(`<alias>` ∈ `core`, `normalize-docs`, `normalize-tests`, `cli`) — the same targets CI runs.
5151
- Fix every failure and re-run. Repeat until QA exits clean per package:
5252
Pylint ≥ 9.5, Black clean, mypy clean, deptry clean, `pytest --cov-fail-under=80` passing,
5353
and `make no-vendored-schemas` clean.

‎.github/agents/devops-engineer.agent.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Repo specifics
8787
- Python 3.10+ (supported floor); the CI matrix runs `3.10` through `3.14`.
8888
- Quality gates (use the `Makefile` targets — `.github/workflows/test.yml` and `integration.yml` run the same)
8989
- Full gate, all packages: `make qa`
90-
- Per package: `make qa-<alias>` (`<alias>` ∈ `core`, `normalize-docs`, `cli`)
90+
- Per package: `make qa-<alias>` (`<alias>` ∈ `core`, `normalize-docs`, `normalize-tests`, `cli`)
9191
- Individual: `make lint-<alias>` (Pylint ≥ 9.5), `make format-check-<alias>` (Black, line length 120), `make types-<alias>` (mypy), `make deptry-<alias>` (deptry), `make test-<alias>`, `make coverage-<alias>` (`--cov-fail-under=80`)
9292
- Whole repository: `make no-vendored-schemas` (R12 check 1, in `make qa`)
9393
- Must keep the per-package invocation (each gate runs from inside the package directory) — do not flatten it to a repo-root command.

‎.github/agents/senior-developer.agent.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ Repo specifics
101101
- Must keep Python imports at top of file (not inside functions/methods).
102102
- Quality gates (use the `Makefile` targets — `.github/workflows/test.yml` runs the same)
103103
- Full gate, all packages: `make qa`
104-
- Per package: `make qa-<alias>` (`<alias>` ∈ `core`, `normalize-docs`, `cli`)
104+
- Per package: `make qa-<alias>` (`<alias>` ∈ `core`, `normalize-docs`, `normalize-tests`, `cli`)
105105
- Individual: `make format-<alias>` (check-only `make format-check-<alias>`), `make lint-<alias>` (Pylint ≥ 9.5), `make types-<alias>`, `make deptry-<alias>`, `make test-<alias>`, `make coverage-<alias>` (`--cov-fail-under=80`)
106106
- Contract-sensitive outputs
107107
- CLI argument names/defaults; the action's inputs and `output-path` output; `main.py`'s error lines.

‎.github/copilot-instructions.md‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ Tooling and quality gates → Common pitfalls → Learned rules.
2121
(`living-doc`) and one composite GitHub Action. It sits between the upstream `living-doc-*`
2222
collector actions and the downstream generator actions: it reads collector output as
2323
`living-doc-utilities` contract artifacts, validates a command's inputs together, and
24-
transforms them into the contracts the generators read. `normalize-docs` runs;
25-
`normalize-tests` and `coverage-matrix` each land in a later change.
24+
transforms them into the contracts the generators read. `normalize-docs` and `normalize-tests` run;
25+
`coverage-matrix` lands in a later change.
2626

2727
- Must treat the `living-doc` CLI running as a step in a GitHub Actions workflow as the supported path; running `living-doc <command>` locally is a development and debugging affordance only.
2828
- Must keep the whole collect → normalize → generate pipeline AI-free — deterministic Python only, no LLM call and no network call anywhere in that path.
@@ -31,20 +31,21 @@ transforms them into the contracts the generators read. `normalize-docs` runs;
3131

3232
## Repo specifics
3333

34-
Module map — three packages under `packages/` and `apps/`, each with its own `pyproject.toml`,
34+
Module map — four packages under `packages/` and `apps/`, each with its own `pyproject.toml`,
3535
`src/` layout, and `tests/` directory:
3636

3737
| Path | Package (`pip` name) | Responsibility |
3838
|---|---|---|
39-
| `packages/core` | `living-doc-core` | `logging_config.py` (`setup_logging`), `readers.py` (the reader table, `read_documentation`, `read_tests`), `input_validation.py` (`validate_inputs`, the shared input validation), `views.py` (`select_records`, the view filter), `run_context.py` (`run_context`, the CI run for `metadata.run`). Depends on `living-doc-utilities==0.8.0`. |
39+
| `packages/core` | `living-doc-core` | `logging_config.py` (`setup_logging`), `envelope.py` (`PRODUCER_NAME`, `TOOLKIT_VERSION`, `forwarded`, the producer identity and warning forwarding every transform shares), `readers.py` (the reader table, `read_documentation`, `read_tests`), `input_validation.py` (`validate_inputs`, the shared input validation), `views.py` (`select_records`, the view filter), `run_context.py` (`run_context`, the CI run for `metadata.run`). Depends on `living-doc-utilities==0.8.0`. |
4040
| `packages/services/normalize_docs` | `living-doc-service-normalize-docs` | `builder.py` (`build_generator_ready`), `lineage.py` (`LINEAGE_TABLE`), `service.py` (`normalize_docs`, the command, and `check_no_field_loss`). |
41-
| `apps/cli` | `living-doc-cli` | CLI entry point — `main.py` (`cli()` click group), one module per command under `commands/`. Registers `normalize-docs`. |
41+
| `packages/services/normalize_tests` | `living-doc-service-normalize-tests` | `builder.py` (`build_ui_test_catalog`), `lineage.py` (`LINEAGE_TABLE`), `service.py` (`normalize_tests`, the command, and the runtime field-loss check). |
42+
| `apps/cli` | `living-doc-cli` | CLI entry point — `main.py` (`cli()` click group), one module per command under `commands/`. Registers `normalize-docs` and `normalize-tests`. |
4243

4344
- Must treat the repository root as the action — `action.yml`, its entry point `main.py` (input checks, `COMMAND_INPUTS`, `NOT_AVAILABLE_YET`), and the root `tests/`.
4445
- Must treat `apps/cli/src/living_doc_cli/main.py` `cli()` as the CLI entry point — each command is registered there as it lands.
4546
- Must read every contract artifact through `living_doc_core.readers`, which calls `living-doc-utilities`' `read_artifact`, and must write one only through `write_artifact` (R12 check 2).
4647
- Must run `living_doc_core.input_validation.validate_inputs` over a command's inputs before it transforms them.
47-
- Must select a view's records only through `living_doc_core.views.select_records`, and Must keep each command's lineage table in its own service package, checked by `assert_complete` in its tests and never at run time.
48+
- Must select view records only through `living_doc_core.views.select_records`, and Must keep each command's lineage table in its own service package, checked by `assert_complete` in its tests and never at run time; `normalize-tests` has no view input and always writes `document.view` as `inner`.
4849
- Must declare the toolkit's one version in every package, and Must read `metadata.producer.version` from the installed `living-doc-core`, never from a literal.
4950

5051
Inputs — the action's inputs (full reference in [`README.md`](../README.md#inputs)):
@@ -65,7 +66,7 @@ Contract-sensitive outputs:
6566
QA commands — the per-package `Makefile` targets (`.github/workflows/test.yml` runs the same targets):
6667

6768
- Full gate, every package: `make qa` (runs `format-check` → `lint` → `types` → `deptry` → `test` per package, then the root checks, then `no-vendored-schemas`, failing on the first).
68-
- Per package: `make qa-<alias>` where `<alias>` is one of `core`, `normalize-docs`, `cli`.
69+
- Per package: `make qa-<alias>` where `<alias>` is one of `core`, `normalize-docs`, `normalize-tests`, `cli`.
6970
- Individual gate per package: `make lint-<alias>`, `make format-<alias>`, `make format-check-<alias>`, `make types-<alias>`, `make deptry-<alias>`, `make test-<alias>`, `make coverage-<alias>`.
7071
- `lint-<alias>` runs ruff (`E` / `F` / `I` / `B`, `tests` excluded per each package's `[tool.ruff]`) then Pylint; `format-<alias>` runs `ruff check --fix` then Black.
7172
- Must not use repo-root `pylint $(git ls-files '*.py')` / `pytest tests/` as the QA command — CI gates changes per package from inside each package directory.

‎.github/dependabot.yml‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,29 @@ updates:
9191
patterns:
9292
- "*"
9393

94+
- package-ecosystem: "pip"
95+
directory: "/packages/services/normalize_tests"
96+
target-branch: "master"
97+
schedule:
98+
interval: "weekly"
99+
day: "sunday"
100+
labels:
101+
- "auto update"
102+
- "infrastructure"
103+
- "no RN"
104+
open-pull-requests-limit: 3
105+
commit-message:
106+
prefix: "chore"
107+
include: "scope"
108+
allow:
109+
- dependency-type: "direct"
110+
ignore:
111+
- dependency-name: "living-doc-utilities"
112+
groups:
113+
pip-normalize-tests:
114+
patterns:
115+
- "*"
116+
94117
- package-ecosystem: "pip"
95118
directory: "/apps/cli"
96119
target-branch: "master"

‎.github/workflows/integration.yml‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,45 @@ jobs:
8686
' "$OUTPUT_PATH" "$GITHUB_RUN_ID"
8787
echo '::notice::normalize-docs wrote a valid generator-ready file in the release view, with this run, at the output-path.'
8888
89+
- name: Run normalize-tests
90+
id: normalize-tests
91+
uses: ./
92+
with:
93+
command: normalize-tests
94+
input: tests/fixtures/collector-gh/ui-tests.json
95+
output: output/ui-test-catalog.json
96+
97+
- name: normalize-tests wrote a valid catalog with this run, and output-path names it
98+
env:
99+
VENV: ${{ runner.temp }}/living-doc-toolkit-venv
100+
OUTPUT_PATH: ${{ steps.normalize-tests.outputs.output-path }}
101+
run: |
102+
set -euo pipefail
103+
if [[ "$OUTPUT_PATH" != "$GITHUB_WORKSPACE/output/ui-test-catalog.json" ]]; then
104+
echo "::error::output-path must be the absolute path of the file written; it is '$OUTPUT_PATH'."; exit 1
105+
fi
106+
"$VENV/bin/python" -I -c '
107+
import sys
108+
from living_doc_utilities.contracts.io import read_artifact
109+
from living_doc_utilities.contracts.ui_test_catalog import UiTestCatalogResult
110+
result = read_artifact(sys.argv[1], expected="ui-test-catalog")
111+
assert isinstance(result, UiTestCatalogResult), type(result)
112+
assert result.document.view == "inner", result.document
113+
assert [feature_file.feature_file for feature_file in result.feature_files] == [
114+
"AbsaOSS/living-doc/docs/examples/gherkin/liv_doc_func/func-001-validate-password-strength.feature",
115+
"AbsaOSS/living-doc/docs/examples/gherkin/liv_doc_us/us-001-customer-login.feature",
116+
]
117+
functionality_file, user_story_file = result.feature_files
118+
assert [(group.entity_id, len(group.scenarios)) for group in functionality_file.linked_to_functionality] == [("FUNC-001", 4)]
119+
assert [(group.entity_id, len(group.scenarios)) for group in user_story_file.linked_to_user_story] == [("US-001", 2)]
120+
assert not any(feature_file.unlinked for feature_file in result.feature_files)
121+
assert result.metadata.run.run_id == sys.argv[2], (result.metadata.run, sys.argv[2])
122+
assert result.metadata.producer.build is None, result.metadata.producer
123+
print(result.document.view)
124+
print(result.metadata.run)
125+
' "$OUTPUT_PATH" "$GITHUB_RUN_ID"
126+
echo '::notice::normalize-tests wrote a valid ui-test-catalog file with this run at the output-path.'
127+
89128
action-venv-isolation:
90129
runs-on: ubuntu-latest
91130
name: Action Venv - Isolation and Reuse

0 commit comments

Comments
 (0)