diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql index 304e7337..d8d1393e 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-asset/7010-hs-hosting-asset.sql @@ -41,6 +41,8 @@ create table if not exists hs_hosting.asset config jsonb not null, alarmContactUuid uuid null references hs_office.contact(uuid) initially deferred, + unique (type, identifier), -- at least as long as we need to be compatible to the legacy system + constraint hosting_asset_has_booking_item_or_parent_asset check (bookingItemUuid is not null or parentAssetUuid is not null or type in ('DOMAIN_SETUP', 'IPV4_NUMBER', 'IPV6_NUMBER')) ); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRepositoryIntegrationTest.java index e04591c7..90ac5bf6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetRepositoryIntegrationTest.java @@ -10,6 +10,7 @@ import net.hostsharing.hsadminng.rbac.role.RawRbacRoleRepository; import net.hostsharing.hsadminng.mapper.Array; import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; +import org.hibernate.exception.ConstraintViolationException; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -163,6 +164,31 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu assertThat(realAssetRepo.count()).isEqualTo(count + 1); } + @Test + public void identifiersForTheSameTypeAreUnique() { + // given + context("superuser-alex@hostsharing.net"); // TODO.test: remove context(...) once all entities have real entities + final var count = realAssetRepo.count(); + final var givenManagedServer = givenHostingAsset("D-1000111 default project", MANAGED_SERVER); + final var newWebspaceBookingItem = newBookingItem(givenManagedServer.getBookingItem(), HsBookingItemType.MANAGED_WEBSPACE, "fir01"); + + // when + final var result = attempt(em, () -> { + final var newAsset = HsHostingAssetRbacEntity.builder() + .bookingItem(newWebspaceBookingItem) + .parentAsset(givenManagedServer) + .caption("some managed webspace with existing identifier") + .type(MANAGED_WEBSPACE) + .identifier("fir01") + .build(); + return toCleanup(rbacAssetRepo.save(newAsset)); + }); + + // then + result.assertExceptionWithRootCauseMessage(ConstraintViolationException.class, + "duplicate key value violates unique constraint \"asset_type_identifier_key\""); + } + @Test public void createsAndGrantsRoles() { // given