Skip to content

Commit 3b11799

Browse files
jopemachineclaude
andcommitted
fix(BA-5827): select only id in Fragment update/purge; expect typed FK error in tests
Mirrors the BA-5814 Policy fix (d78f510): - `db_source.update` / `purge` now select only `AppConfigFragmentRow.id` when resolving the natural key, so `execute_updater`'s UPDATE ... RETURNING does not collide with a pre-loaded ORM instance in the session's identity map. - `test_create_without_matching_policy_violates_fk` now expects `AppConfigFragmentPolicyMissing` — that is what the creator spec translates the underlying `ForeignKeyViolationError` into. Drop the stray `import sqlalchemy as sa` that became unused. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7ccf0d2 commit 3b11799

2 files changed

Lines changed: 14 additions & 12 deletions

File tree

src/ai/backend/manager/repositories/app_config_fragment/db_source/db_source.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,18 @@ async def update(
133133
Returns `None` when no row exists for the key.
134134
"""
135135
async with self._db.begin_session() as db_sess:
136-
row = await db_sess.scalar(
137-
sa.select(AppConfigFragmentRow).where(
136+
pk_value = await db_sess.scalar(
137+
sa.select(AppConfigFragmentRow.id).where(
138138
AppConfigFragmentRow.scope_type == key.scope_type,
139139
AppConfigFragmentRow.scope_id == key.scope_id,
140140
AppConfigFragmentRow.name == key.name,
141141
)
142142
)
143-
if row is None:
143+
if pk_value is None:
144144
return None
145-
updater: Updater[AppConfigFragmentRow] = Updater(spec=spec, pk_value=row.id)
145+
updater: Updater[AppConfigFragmentRow] = Updater(spec=spec, pk_value=pk_value)
146146
result = await execute_updater(db_sess, updater)
147-
return result.row.to_data() if result is not None else row.to_data()
147+
return result.row.to_data() if result is not None else None
148148

149149
@app_config_fragment_db_source_resilience.apply()
150150
async def purge(self, key: AppConfigFragmentKey) -> bool:
@@ -155,17 +155,17 @@ async def purge(self, key: AppConfigFragmentKey) -> bool:
155155
actually removed.
156156
"""
157157
async with self._db.begin_session() as db_sess:
158-
row = await db_sess.scalar(
159-
sa.select(AppConfigFragmentRow).where(
158+
pk_value = await db_sess.scalar(
159+
sa.select(AppConfigFragmentRow.id).where(
160160
AppConfigFragmentRow.scope_type == key.scope_type,
161161
AppConfigFragmentRow.scope_id == key.scope_id,
162162
AppConfigFragmentRow.name == key.name,
163163
)
164164
)
165-
if row is None:
165+
if pk_value is None:
166166
return False
167167
purger: Purger[AppConfigFragmentRow] = Purger(
168-
row_class=AppConfigFragmentRow, pk_value=row.id
168+
row_class=AppConfigFragmentRow, pk_value=pk_value
169169
)
170170
result = await execute_purger(db_sess, purger)
171171
return result is not None

tests/unit/manager/repositories/app_config_fragment/test_app_config_fragment.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
from collections.abc import AsyncGenerator
66

77
import pytest
8-
import sqlalchemy as sa
98

109
from ai.backend.manager.data.app_config_fragment.types import (
1110
AppConfigFragmentKey,
1211
AppConfigScopeType,
1312
)
13+
from ai.backend.manager.errors.app_config import AppConfigFragmentPolicyMissing
1414
from ai.backend.manager.models.app_config_fragment.row import AppConfigFragmentRow
1515
from ai.backend.manager.models.app_config_policy.row import AppConfigPolicyRow
1616
from ai.backend.manager.models.utils import ExtendedAsyncSAEngine
@@ -221,8 +221,10 @@ async def test_create_without_matching_policy_violates_fk(
221221
admin_repository: AppConfigFragmentAdminRepository,
222222
) -> None:
223223
# Required-policy invariant (BEP-1052 §1) — DB-level FK is the
224-
# backstop when the service layer's check is bypassed.
225-
with pytest.raises(sa.exc.IntegrityError):
224+
# backstop when the service layer's check is bypassed. The
225+
# creator spec translates the `ForeignKeyViolationError` into
226+
# the typed `AppConfigFragmentPolicyMissing` domain error.
227+
with pytest.raises(AppConfigFragmentPolicyMissing):
226228
await admin_repository.create(
227229
key=AppConfigFragmentKey(
228230
scope_type=AppConfigScopeType.DOMAIN,

0 commit comments

Comments
 (0)