Add unique role index when query is null.

Change-Id: I183ca09d968afa777a6f0fd77edd34a3c39123a5
diff --git a/src/main/java/de/ids_mannheim/korap/entity/Role.java b/src/main/java/de/ids_mannheim/korap/entity/Role.java
index 6e78582..dba9d3c 100644
--- a/src/main/java/de/ids_mannheim/korap/entity/Role.java
+++ b/src/main/java/de/ids_mannheim/korap/entity/Role.java
@@ -103,6 +103,17 @@
         if (this.name.equals(r.getName())
                 && this.privilege.equals(r.getPrivilege())
                 && this.userGroup.equals(r.getUserGroup())) {
+            if (this.query != null && r.getQuery() == null) {
+                return false;
+            }
+            if (this.query == null && r.getQuery() != null) {
+                return false;
+            }
+            if(this.query != null && r.getQuery() != null
+                    && !this.query.equals(r.getQuery())) {
+                return false;
+            }
+
             return true;
         }
         return false;
diff --git a/src/main/resources/db/sqlite/V1.13__user_group_alteration.sql b/src/main/resources/db/sqlite/V1.13__user_group_alteration.sql
index e167bde..f977c4e 100644
--- a/src/main/resources/db/sqlite/V1.13__user_group_alteration.sql
+++ b/src/main/resources/db/sqlite/V1.13__user_group_alteration.sql
@@ -61,5 +61,9 @@
 DROP TABLE privilege;
 DROP TABLE query_access;
 
+CREATE UNIQUE INDEX IF NOT EXISTS role_index_null_query
+ON role (name, privilege, group_id)
+WHERE query_id IS 0;
+
 CREATE UNIQUE INDEX IF NOT EXISTS role_index on role(name, 
   privilege, group_id, query_id);
\ No newline at end of file
diff --git a/src/test/java/de/ids_mannheim/korap/dao/RoleDaoTest.java b/src/test/java/de/ids_mannheim/korap/dao/RoleDaoTest.java
new file mode 100644
index 0000000..fc81b5e
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/dao/RoleDaoTest.java
@@ -0,0 +1,84 @@
+package de.ids_mannheim.korap.dao;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.sqlite.SQLiteException;
+
+import de.ids_mannheim.korap.constant.PredefinedRole;
+import de.ids_mannheim.korap.constant.PrivilegeType;
+import de.ids_mannheim.korap.entity.QueryDO;
+import de.ids_mannheim.korap.entity.Role;
+import de.ids_mannheim.korap.entity.UserGroup;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import jakarta.persistence.PersistenceException;
+
+@Disabled
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration("classpath:test-config.xml")
+public class RoleDaoTest extends DaoTestBase {
+
+    @Autowired
+    private RoleDao roleDao;
+    @Autowired
+    private QueryDao queryDao;
+
+    @Test
+    public void testUniqueRoleWithoutQuery () throws KustvaktException {
+        UserGroup group = createDoryGroup();
+
+        Role r = new Role(PredefinedRole.GROUP_ADMIN, PrivilegeType.READ_MEMBER,
+                group);
+
+        Exception exception = assertThrows(PersistenceException.class, () -> {
+            roleDao.addRole(r);
+        });
+
+        Throwable rootCause = exception;
+        while (rootCause.getCause() != null) {
+            rootCause = rootCause.getCause();
+        }
+
+        assertEquals(SQLiteException.class, rootCause.getClass());
+        assertTrue(rootCause.getMessage()
+                .startsWith("[SQLITE_CONSTRAINT_UNIQUE]"));
+
+        deleteUserGroup(group.getId(), "dory");
+    }
+
+    @Test
+    public void testUniqueRoleWithQuery () throws KustvaktException {
+        QueryDO query = queryDao.retrieveQueryByName("dory-vc", "dory");
+
+        UserGroup group = createDoryGroup();
+
+        Role r1 = new Role(PredefinedRole.GROUP_ADMIN,
+                PrivilegeType.READ_MEMBER, group, query);
+        roleDao.addRole(r1);
+
+        Role r2 = new Role(PredefinedRole.GROUP_ADMIN,
+                PrivilegeType.READ_MEMBER, group, query);
+
+        Exception exception = assertThrows(PersistenceException.class, () -> {
+            roleDao.addRole(r2);
+        });
+        
+        Throwable rootCause = exception;
+        while (rootCause.getCause() != null) {
+            rootCause = rootCause.getCause();
+        }
+
+        assertEquals(SQLiteException.class, rootCause.getClass());
+        assertTrue(rootCause.getMessage()
+                .startsWith("[SQLITE_CONSTRAINT_UNIQUE]"));
+
+        deleteUserGroup(group.getId(), "dory");
+    }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/dao/UserGroupMemberDaoTest.java b/src/test/java/de/ids_mannheim/korap/dao/UserGroupMemberDaoTest.java
deleted file mode 100644
index 1943118..0000000
--- a/src/test/java/de/ids_mannheim/korap/dao/UserGroupMemberDaoTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package de.ids_mannheim.korap.dao;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.List;
-import java.util.Set;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-import de.ids_mannheim.korap.constant.PredefinedRole;
-import de.ids_mannheim.korap.constant.PrivilegeType;
-import de.ids_mannheim.korap.entity.Role;
-import de.ids_mannheim.korap.entity.UserGroup;
-import de.ids_mannheim.korap.entity.UserGroupMember;
-import de.ids_mannheim.korap.exceptions.KustvaktException;
-
-@ExtendWith(SpringExtension.class)
-@ContextConfiguration("classpath:test-config.xml")
-public class UserGroupMemberDaoTest extends DaoTestBase {
-
-    @Autowired
-    private UserGroupMemberDao dao;
-
-    @Autowired
-    private RoleDao roleDao;
-    
-    @Test
-    public void testRetrieveMemberByRole () throws KustvaktException {
-        UserGroup group = createDoryGroup();
-        // dory group
-        List<UserGroupMember> groupAdmins = dao.retrieveMemberByRole(
-                group.getId(), PredefinedRole.GROUP_ADMIN);
-        // System.out.println(vcaAdmins);
-        assertEquals(1, groupAdmins.size());
-        assertEquals(groupAdmins.get(0).getUserId(), "dory");
-
-        deleteUserGroup(group.getId(), "dory");
-    }
-
-    // EM: now it is possible to add duplicate member role !
-    @Test
-    public void testAddSameMemberRole () throws KustvaktException {
-        UserGroup group = createDoryGroup();
-        int groupId = group.getId();
-        
-        Role newRole = new Role(PredefinedRole.GROUP_ADMIN,
-                PrivilegeType.DELETE_MEMBER, group);
-        roleDao.addRole(newRole);
-        
-        UserGroupMember member = dao.retrieveMemberById("dory", groupId);
-        Set<Role> roles = member.getRoles();
-        assertEquals(5, roles.size());
-        
-        roles.add(newRole);
-        member.setRoles(roles);
-        dao.updateMember(member);
-        member = dao.retrieveMemberById("dory", groupId);
-        member.getRoles();
-        assertEquals(6, roles.size());
-        
-        deleteUserGroup(group.getId(), "dory");
-    }
-}