fix(data-integrity): address PR #10990 review feedback (PR #8179)

- Fix CI lint failure: remove unused IntegrityError import in BDD steps
- Append session.rollback() before session.close() in all NamespacedProjectRepository methods that own the session outside UoW
- Merge duplicate CHANGELOG ### Fixed sections into single header
- Apply @tdd_issue @tdd_issue_8179 @tdd_expected_fail tags to new BDD scenarios
- Consolidate near-duplicate step definitions; fix misleading docstring (NsP operates outside UoW)

ISSUES CLOSED: #8179
This commit is contained in:
2026-05-07 21:05:38 +00:00
committed by drew
parent b6dd773203
commit d15155acbf
3 changed files with 15 additions and 12 deletions
+3 -1
View File
@@ -149,11 +149,13 @@ Feature: Namespaced project repository operations
# ---------- Data integrity: rollback removal verification ----------
@tdd_issue @tdd_issue_8179 @tdd_expected_fail
Scenario: IntegrityError raises DatabaseError through repository create method (no explicit rollback)
Given a namespaced project "local/integrity-verify" exists in the repository
When I create a namespaced project "local/integrity-verify" via the repository expecting an error
Then the repository error type should be "DatabaseError"
Then the repository error should be "DatabaseError"
@tdd_issue @tdd_issue_8179 @tdd_expected_fail
Scenario: Update non-existent project raises ProjectNotFoundError without leaving transaction dirty
Given a namespaced project "local/missing-update-check" exists in the repository
When I update a non-existent project "local/missing-update-check" expecting an error
+8 -11
View File
@@ -570,15 +570,20 @@ def step_pr_removed_link_absent_new_session(context: Any) -> None:
# Data integrity BDD step extensions (PR #8179)
# ---------------------------------------------------------------------------
# Note: `step_pr_update_not_found` (line ~236, non-duplicate) already handles the
# "when I update a non-existent project … expecting an error" Scenario step.
@when('I create a namespaced project "{ns_name}" via the repository expecting an error')
def step_pr_create_with_error(context: Any, ns_name: str) -> None:
"""Attempt to create a duplicate project through the repository.
This exercises the Repository's own IntegrityError handling path,
verifying that rollback is delegated to the UoW outer-layer handler.
Exercises the NamespacedProjectRepository's own IntegrityError handling path
(the repo owns its session via session-factory and is NOT wired into any
UnitOfWork). Verifies that error propagation works correctly after the
redundant ``session.rollback()`` was removed from exception handlers —
``session.rollback()`` now fires in the ``finally`` block instead.
"""
from sqlalchemy.exc import IntegrityError as _IntegrityError
project = _make_project(ns_name)
try:
@@ -586,11 +591,3 @@ def step_pr_create_with_error(context: Any, ns_name: str) -> None:
context.pr_error = None
except Exception as exc:
context.pr_error = exc
@then('the repository error type should be "{error_type}"')
def step_pr_check_error_class(context: Any, error_type: str) -> None:
"""Verify the caught error is of the expected class."""
assert context.pr_error is not None, "Expected an error but none was raised"
actual_type = type(context.pr_error).__name__
assert actual_type == error_type, f"Expected {error_type}, got {actual_type}"
@@ -3064,6 +3064,7 @@ class NamespacedProjectRepository(ProjectRepositoryProtocol):
except (OperationalError, SQLAlchemyDatabaseError) as exc:
raise DatabaseError(f"Failed to create project: {exc}") from exc
finally:
session.rollback()
session.close()
@database_retry
@@ -3157,6 +3158,7 @@ class NamespacedProjectRepository(ProjectRepositoryProtocol):
f"Failed to load project context policy for '{namespaced_name}': {exc}"
) from exc
finally:
session.rollback()
session.close()
@database_retry
@@ -3208,6 +3210,7 @@ class NamespacedProjectRepository(ProjectRepositoryProtocol):
except (OperationalError, SQLAlchemyDatabaseError) as exc:
raise DatabaseError(f"Failed to update project '{ns_name}': {exc}") from exc
finally:
session.rollback()
session.close()
@database_retry
@@ -3243,6 +3246,7 @@ class NamespacedProjectRepository(ProjectRepositoryProtocol):
f"Failed to delete project '{namespaced_name}': {exc}"
) from exc
finally:
session.rollback()
session.close()