From b68d3832b8dbcc810e379a7e1003c1ff1777bca9 Mon Sep 17 00:00:00 2001 From: Pavel Raiskup Date: Apr 08 2021 13:48:30 +0000 Subject: frontend: assure unique Copr name for group/user in DB Fix the testsuite -- don't change the ownership of the 'c1', that leads to situation that user 'u2' owns two coprs of 'foocopr' name. Fixes: #172 --- diff --git a/frontend/coprs_frontend/alembic/versions/808912fe46d3_unique_name_in_copr.py b/frontend/coprs_frontend/alembic/versions/808912fe46d3_unique_name_in_copr.py new file mode 100644 index 0000000..d836b9f --- /dev/null +++ b/frontend/coprs_frontend/alembic/versions/808912fe46d3_unique_name_in_copr.py @@ -0,0 +1,35 @@ +""" +Add a Copr "name" unique constraint, issue #172 + +Revision ID: 808912fe46d3 +Revises: 6b48324e9264 +Create Date: 2021-04-07 12:21:29.814687 +""" + +import sqlalchemy as sa +from alembic import op + + +revision = '808912fe46d3' +down_revision = '6b48324e9264' + +def upgrade(): + text = sa.text('deleted is not true and group_id is null') + op.create_index( + 'copr_name_for_user_uniq', 'copr', ['user_id', 'name'], + unique=True, + postgresql_where=text, + sqlite_where=text, + ) + + text = sa.text('deleted is not true and group_id is not null') + op.create_index( + 'copr_name_in_group_uniq', 'copr', ['group_id', 'name'], + unique=True, + postgresql_where=text, + sqlite_where=text, + ) + +def downgrade(): + op.drop_index('copr_name_in_group_uniq', table_name='copr') + op.drop_index('copr_name_for_user_uniq', table_name='copr') diff --git a/frontend/coprs_frontend/coprs/models.py b/frontend/coprs_frontend/coprs/models.py index 031538d..86e1cca 100644 --- a/frontend/coprs_frontend/coprs/models.py +++ b/frontend/coprs_frontend/coprs/models.py @@ -7,7 +7,7 @@ import uuid from fnmatch import fnmatch import modulemd_tools.yaml -from sqlalchemy import outerjoin +from sqlalchemy import outerjoin, text from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm import column_property, validates from sqlalchemy.event import listens_for @@ -251,6 +251,8 @@ class CoprScore(db.Model, helpers.Serializer): name="copr_score_copr_id_user_id_uniq"), ) +_group_unique_where = text("deleted is not true and group_id is not null") +_user_unique_where = text("deleted is not true and group_id is null") class _CoprPublic(db.Model, helpers.Serializer, CoprSearchRelatedData): """ @@ -262,6 +264,16 @@ class _CoprPublic(db.Model, helpers.Serializer, CoprSearchRelatedData): __table_args__ = ( db.Index('copr_name_group_id_idx', 'name', 'group_id'), db.Index('copr_deleted_name', 'deleted', 'name'), + db.Index("copr_name_in_group_uniq", + "group_id", "name", + unique=True, + postgresql_where=_group_unique_where, + sqlite_where=_group_unique_where), + db.Index("copr_name_for_user_uniq", + "user_id", "name", + unique=True, + postgresql_where=_user_unique_where, + sqlite_where=_user_unique_where), ) id = db.Column(db.Integer, primary_key=True) diff --git a/frontend/coprs_frontend/tests/test_logic/test_builds_logic.py b/frontend/coprs_frontend/tests/test_logic/test_builds_logic.py index e53bd72..e7c9248 100644 --- a/frontend/coprs_frontend/tests/test_logic/test_builds_logic.py +++ b/frontend/coprs_frontend/tests/test_logic/test_builds_logic.py @@ -354,15 +354,19 @@ class TestBuildsLogic(CoprsTestCase): for build_id in [self.b1.id, self.b2.id, self.b3.id, self.b4.id]: BuildsLogic.get(build_id).one() - self.c1.user = self.u2 build_ids = [self.b1.id, self.b4.id] + with pytest.raises(BadRequest) as err_msg: + BuildsLogic.delete_builds(self.u1, build_ids) + assert "Can not delete builds from more project" in str(err_msg.value) + + self.b3.source_status = StatusEnum("failed") + build_ids = [self.b3.id, self.b4.id] BuildsLogic.delete_builds(self.u2, build_ids) - self.db.session.commit() assert len(ActionsLogic.get_many().all()) == 1 with pytest.raises(NoResultFound): - BuildsLogic.get(self.b1.id).one() + BuildsLogic.get(self.b3.id).one() with pytest.raises(NoResultFound): BuildsLogic.get(self.b4.id).one()