diff --git a/backend/app/routes/saved_views.py b/backend/app/routes/saved_views.py index f258b230..2af427ea 100644 --- a/backend/app/routes/saved_views.py +++ b/backend/app/routes/saved_views.py @@ -128,7 +128,8 @@ def update_view( db.commit() except IntegrityError: 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) diff --git a/backend/app/routes/tags.py b/backend/app/routes/tags.py index 47e7e8cc..250206ec 100644 --- a/backend/app/routes/tags.py +++ b/backend/app/routes/tags.py @@ -107,7 +107,8 @@ def update_tag( db.commit() except IntegrityError: 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 { "ok": True, "id": tag.id, diff --git a/backend/tests/test_name_taken_detail.py b/backend/tests/test_name_taken_detail.py new file mode 100644 index 00000000..e1f75acb --- /dev/null +++ b/backend/tests/test_name_taken_detail.py @@ -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"}