From 60ad07a02c766b54673d7cbbdd7e7bdcd08d129e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 07:29:22 +0900 Subject: [PATCH] fix(services): make governed service tests runnable from any working directory The central coverage sandbox runs plain 'pytest tests' inside each project directory without a workspace install, so service tests that import sibling workspace packages failed collection with ModuleNotFoundError, and tests reading repository files relative to the process CWD failed with FileNotFoundError. Root-cause repairs: - services/{job-analysis-api,people-api}/conftest.py add every workspace src tree to sys.path for sessions rooted under the service, keeping production runtime behavior untouched. - Repository-file reads in job-analysis-api and people-api test modules now anchor at Path(__file__) instead of the process CWD. Both suites pass fully from their own directories (69+72 subtests and 146+235 subtests), which is also how hosted exact-head quality workflows and the central review coverage sandbox invoke them. --- services/job-analysis-api/conftest.py | 25 +++++++++++++++++++ .../tests/test_http_content_type.py | 2 +- .../tests/test_http_error_contract.py | 2 +- .../tests/test_openapi_route_contract.py | 2 +- .../tests/test_openapi_snapshot_schema.py | 4 +-- .../tests/test_postgres_contract_script.py | 8 +++--- .../tests/test_workflow_contract.py | 5 +++- services/people-api/conftest.py | 25 +++++++++++++++++++ .../tests/test_workflow_contract.py | 5 +++- 9 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 services/job-analysis-api/conftest.py create mode 100644 services/people-api/conftest.py diff --git a/services/job-analysis-api/conftest.py b/services/job-analysis-api/conftest.py new file mode 100644 index 000000000..21c723088 --- /dev/null +++ b/services/job-analysis-api/conftest.py @@ -0,0 +1,25 @@ +"""Make sibling Orgmetra workspace packages importable for this service's tests. + +The service depends on sibling workspace packages (for example +``orgmetra_hris_kernel``). Hosted quality workflows install those dependencies +into isolated environments, but plain ``pytest`` sessions started inside this +service directory (such as the central coverage sandbox) do not perform a +workspace install. This conftest adds every workspace ``src`` tree to the +import path so test collection succeeds without changing production behavior. +""" + +from __future__ import annotations + +from pathlib import Path +import sys + +_WORKSPACE_ROOT = Path(__file__).resolve().parents[2] + +for _source_dir in sorted( + str(path) + for pattern in ("packages/*/src", "services/*/src") + for path in _WORKSPACE_ROOT.glob(pattern) + if path.is_dir() +): + if _source_dir not in sys.path: + sys.path.insert(0, _source_dir) diff --git a/services/job-analysis-api/tests/test_http_content_type.py b/services/job-analysis-api/tests/test_http_content_type.py index 3082fa74a..9fa3e64ad 100644 --- a/services/job-analysis-api/tests/test_http_content_type.py +++ b/services/job-analysis-api/tests/test_http_content_type.py @@ -108,7 +108,7 @@ async def send(message: dict[str, object]) -> None: def test_openapi_publishes_the_unsupported_media_type_response(self) -> None: """Keep generated clients aligned with the runtime 415 contract.""" - schema = Path("schemas/openapi.yaml").read_text(encoding="utf-8") + schema = (Path(__file__).resolve().parents[3] / "schemas" / "openapi.yaml").read_text(encoding="utf-8") collection = schema.split( " /tenants/{tenant_record_id}/job-analysis-snapshots:", 1 )[1].split( diff --git a/services/job-analysis-api/tests/test_http_error_contract.py b/services/job-analysis-api/tests/test_http_error_contract.py index b8ce63d6f..41d5363e7 100644 --- a/services/job-analysis-api/tests/test_http_error_contract.py +++ b/services/job-analysis-api/tests/test_http_error_contract.py @@ -112,7 +112,7 @@ async def send(message: dict[str, object]) -> None: def test_openapi_allows_the_deprecated_error_alias_without_weakening_required_fields(self) -> None: """Preserve current clients while requiring the governed four-field envelope.""" - schema = Path("schemas/openapi.yaml").read_text(encoding="utf-8") + schema = (Path(__file__).resolve().parents[3] / "schemas" / "openapi.yaml").read_text(encoding="utf-8") error_schema = schema.split(" ErrorResponse:\n", 1)[1].split( " responses:\n", 1 )[0] diff --git a/services/job-analysis-api/tests/test_openapi_route_contract.py b/services/job-analysis-api/tests/test_openapi_route_contract.py index c1eb49138..3ca0485ab 100644 --- a/services/job-analysis-api/tests/test_openapi_route_contract.py +++ b/services/job-analysis-api/tests/test_openapi_route_contract.py @@ -5,7 +5,7 @@ def test_openapi_matches_the_path_tenant_and_authenticated_actor_authority() -> None: """Do not publish a global route or duplicate tenant/actor header authority.""" - schema = Path("schemas/openapi.yaml").read_text(encoding="utf-8") + schema = (Path(__file__).resolve().parents[3] / "schemas" / "openapi.yaml").read_text(encoding="utf-8") collection_path = " /tenants/{tenant_record_id}/job-analysis-snapshots:" item_path = " /tenants/{tenant_record_id}/job-analysis-snapshots/{analysis_record_id}:" assert collection_path in schema diff --git a/services/job-analysis-api/tests/test_openapi_snapshot_schema.py b/services/job-analysis-api/tests/test_openapi_snapshot_schema.py index a32527229..07b630295 100644 --- a/services/job-analysis-api/tests/test_openapi_snapshot_schema.py +++ b/services/job-analysis-api/tests/test_openapi_snapshot_schema.py @@ -5,7 +5,7 @@ def test_snapshot_arrays_publish_runtime_cardinality_and_item_types() -> None: """Keep client schemas aligned with bounded runtime parsing and evidence shapes.""" - schema = Path("schemas/openapi.yaml").read_text(encoding="utf-8") + schema = (Path(__file__).resolve().parents[3] / "schemas" / "openapi.yaml").read_text(encoding="utf-8") command = schema.split(" PersistJobAnalysisSnapshotCommand:", 1)[1].split( " JobAnalysisSnapshotDocument:", 1 )[0] @@ -31,7 +31,7 @@ def test_snapshot_arrays_publish_runtime_cardinality_and_item_types() -> None: def test_snapshot_get_publishes_dedicated_not_found_response() -> None: """Do not document a missing snapshot as generic invalid-command validation.""" - schema = Path("schemas/openapi.yaml").read_text(encoding="utf-8") + schema = (Path(__file__).resolve().parents[3] / "schemas" / "openapi.yaml").read_text(encoding="utf-8") item_path = " /tenants/{tenant_record_id}/job-analysis-snapshots/{analysis_record_id}:" item_block = schema.split(item_path, 1)[1].split("components:", 1)[0] diff --git a/services/job-analysis-api/tests/test_postgres_contract_script.py b/services/job-analysis-api/tests/test_postgres_contract_script.py index 34da009e5..6aab7aeca 100644 --- a/services/job-analysis-api/tests/test_postgres_contract_script.py +++ b/services/job-analysis-api/tests/test_postgres_contract_script.py @@ -5,9 +5,11 @@ def test_missing_position_failure_requires_the_expected_foreign_key_reason() -> None: """Do not accept an unrelated SQL failure as position-scope evidence.""" - contract = Path("tests/test_job_analysis_snapshot_postgres.sh").read_text( - encoding="utf-8" - ) + contract = ( + Path(__file__).resolve().parents[3] + / "tests" + / "test_job_analysis_snapshot_postgres.sh" + ).read_text(encoding="utf-8") assert '"${missing_position_output}" != *"foreign key"*' in contract assert '"${missing_position_output}" != *"job_analysis_snapshot_position_tenant_fk"*' in contract assert "missing position failed for an unexpected reason" in contract diff --git a/services/job-analysis-api/tests/test_workflow_contract.py b/services/job-analysis-api/tests/test_workflow_contract.py index 441ca21f9..72e01b00d 100644 --- a/services/job-analysis-api/tests/test_workflow_contract.py +++ b/services/job-analysis-api/tests/test_workflow_contract.py @@ -3,9 +3,12 @@ from pathlib import Path +REPOSITORY_ROOT = Path(__file__).resolve().parents[3] + + def _workflow(path: str) -> str: """Read one reviewed repository-local workflow as UTF-8 text.""" - return Path(path).read_text(encoding="utf-8") + return (REPOSITORY_ROOT / path).read_text(encoding="utf-8") def test_job_analysis_api_quality_runs_on_current_default_branch_pull_requests() -> None: diff --git a/services/people-api/conftest.py b/services/people-api/conftest.py new file mode 100644 index 000000000..21c723088 --- /dev/null +++ b/services/people-api/conftest.py @@ -0,0 +1,25 @@ +"""Make sibling Orgmetra workspace packages importable for this service's tests. + +The service depends on sibling workspace packages (for example +``orgmetra_hris_kernel``). Hosted quality workflows install those dependencies +into isolated environments, but plain ``pytest`` sessions started inside this +service directory (such as the central coverage sandbox) do not perform a +workspace install. This conftest adds every workspace ``src`` tree to the +import path so test collection succeeds without changing production behavior. +""" + +from __future__ import annotations + +from pathlib import Path +import sys + +_WORKSPACE_ROOT = Path(__file__).resolve().parents[2] + +for _source_dir in sorted( + str(path) + for pattern in ("packages/*/src", "services/*/src") + for path in _WORKSPACE_ROOT.glob(pattern) + if path.is_dir() +): + if _source_dir not in sys.path: + sys.path.insert(0, _source_dir) diff --git a/services/people-api/tests/test_workflow_contract.py b/services/people-api/tests/test_workflow_contract.py index b456df1e8..0566711f7 100644 --- a/services/people-api/tests/test_workflow_contract.py +++ b/services/people-api/tests/test_workflow_contract.py @@ -3,9 +3,12 @@ from pathlib import Path +REPOSITORY_ROOT = Path(__file__).resolve().parents[3] + + def _workflow(path: str) -> str: """Read one reviewed repository-local workflow as UTF-8 text.""" - return Path(path).read_text(encoding="utf-8") + return (REPOSITORY_ROOT / path).read_text(encoding="utf-8") def test_people_api_quality_runs_on_current_default_branch_pull_requests() -> None: