fix(views,tags): a rename onto a taken name names the taken one, not the row's old name

The rollback after the conflict puts the old name back on the row, so the refusal read
"you already have a view named 'hh'" when the taken name was 'gaming'.
This commit is contained in:
2026-10-29 23:07:54 +01:00
parent 4ef772d37b
commit e5c75702e8
3 changed files with 50 additions and 2 deletions
+2 -1
View File
@@ -128,7 +128,8 @@ def update_view(
db.commit() db.commit()
except IntegrityError: except IntegrityError:
db.rollback() db.rollback()
raise refused(409, "view_name_taken", name=view.name) from None # The name asked for: the rollback has put the view's old name back on `view`.
raise refused(409, "view_name_taken", name=name) from None
return _serialize(view) return _serialize(view)
+2 -1
View File
@@ -107,7 +107,8 @@ def update_tag(
db.commit() db.commit()
except IntegrityError: except IntegrityError:
db.rollback() db.rollback()
raise refused(409, "tag_name_taken", name=tag.name) from None # The name asked for: the rollback has put the tag's old name back on `tag`.
raise refused(409, "tag_name_taken", name=name) from None
return { return {
"ok": True, "ok": True,
"id": tag.id, "id": tag.id,
+46
View File
@@ -0,0 +1,46 @@
"""A rename onto a name already taken says WHICH name is taken: the one asked for, not the row's
old name (the rollback that follows the conflict puts the old name back on the row). DB lane.
"""
from fastapi.testclient import TestClient
import app.state as state
from app.auth import current_user
from app.db import get_db
from app.main import app
from app.models import User
def _client(db, monkeypatch, override_dep):
u = User(email="taken@example.com")
db.add(u)
db.commit()
monkeypatch.setattr(
state, "_configured_cache", True
) # the request gate 503s an unconfigured app
override_dep(get_db, db.info["request"])
override_dep(current_user, lambda: u)
return TestClient(app)
def test_renaming_a_view_onto_a_taken_name_names_the_taken_one(db, monkeypatch, override_dep):
c = _client(db, monkeypatch, override_dep)
gaming = c.post("/api/saved-views", json={"name": "gaming", "filters": {}, "source": "feed"})
assert gaming.status_code == 200
hh = c.post("/api/saved-views", json={"name": "hh", "filters": {}, "source": "feed"}).json()
r = c.patch(f"/api/saved-views/{hh['id']}", json={"name": "gaming"})
assert r.status_code == 409
assert r.json()["detail"] == {"code": "refused", "reason": "view_name_taken", "name": "gaming"}
def test_renaming_a_tag_onto_a_taken_name_names_the_taken_one(db, monkeypatch, override_dep):
c = _client(db, monkeypatch, override_dep)
assert c.post("/api/tags", json={"name": "music"}).status_code == 200
news = c.post("/api/tags", json={"name": "news"}).json()
r = c.patch(f"/api/tags/{news['id']}", json={"name": "music"})
assert r.status_code == 409
assert r.json()["detail"] == {"code": "refused", "reason": "tag_name_taken", "name": "music"}