diff --git a/openex-api/pom.xml b/openex-api/pom.xml
index 694af60994..56492d2d1c 100644
--- a/openex-api/pom.xml
+++ b/openex-api/pom.xml
@@ -188,6 +188,7 @@
             <artifactId>opensaml-saml-impl</artifactId>
             <version>${opensaml.version}</version>
         </dependency>
+        <!-- TEST -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
@@ -199,6 +200,11 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
diff --git a/openex-api/src/main/java/io/openex/rest/asset/endpoint/EndpointApi.java b/openex-api/src/main/java/io/openex/rest/asset/endpoint/EndpointApi.java
index bfc4312182..d1294867fa 100644
--- a/openex-api/src/main/java/io/openex/rest/asset/endpoint/EndpointApi.java
+++ b/openex-api/src/main/java/io/openex/rest/asset/endpoint/EndpointApi.java
@@ -27,6 +27,7 @@ public class EndpointApi {
   public Endpoint createEndpoint(@Valid @RequestBody final EndpointInput input) {
     Endpoint endpoint = new Endpoint();
     endpoint.setUpdateAttributes(input);
+    endpoint.setOs(Endpoint.OS_TYPE.valueOf(input.getOs()));
     return this.endpointService.createEndpoint(endpoint);
   }
 
@@ -43,6 +44,7 @@ public Endpoint updateEndpoint(
       @Valid @RequestBody final EndpointInput input) {
     Endpoint endpoint = this.endpointService.endpoint(endpointId);
     endpoint.setUpdateAttributes(input);
+    endpoint.setOs(Endpoint.OS_TYPE.valueOf(input.getOs()));
     return this.endpointService.updateEndpoint(endpoint);
   }
 
diff --git a/openex-api/src/main/java/io/openex/service/UserService.java b/openex-api/src/main/java/io/openex/service/UserService.java
index a9d25bdbdc..f7691fa6d9 100644
--- a/openex-api/src/main/java/io/openex/service/UserService.java
+++ b/openex-api/src/main/java/io/openex/service/UserService.java
@@ -8,6 +8,7 @@
 import io.openex.database.specification.GroupSpecification;
 import io.openex.rest.user.form.user.CreateUserInput;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.SecurityContext;
@@ -18,6 +19,7 @@
 import org.springframework.util.StringUtils;
 
 import jakarta.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -70,28 +72,9 @@ public boolean isUserPasswordValid(User user, String password) {
   }
 
   public void createUserSession(User user) {
-    List<SimpleGrantedAuthority> roles = new ArrayList<>();
-    roles.add(new SimpleGrantedAuthority(ROLE_USER));
-    if (user.isAdmin()) {
-      roles.add(new SimpleGrantedAuthority(ROLE_ADMIN));
-    }
+    Authentication authentication = buildAuthenticationToken(user);
     SecurityContext context = SecurityContextHolder.createEmptyContext();
-    context.setAuthentication(new PreAuthenticatedAuthenticationToken(new OpenexPrincipal() {
-      @Override
-      public String getId() {
-        return user.getId();
-      }
-
-      @Override
-      public Collection<? extends GrantedAuthority> getAuthorities() {
-        return roles;
-      }
-
-      @Override
-      public boolean isAdmin() {
-        return user.isAdmin();
-      }
-    }, "", roles));
+    context.setAuthentication(authentication);
     SecurityContextHolder.setContext(context);
   }
 
@@ -134,4 +117,28 @@ public User user(@NotBlank final String userId) {
   }
 
   // endregion
+
+  public static PreAuthenticatedAuthenticationToken buildAuthenticationToken(@NotNull final User user) {
+    List<SimpleGrantedAuthority> roles = new ArrayList<>();
+    roles.add(new SimpleGrantedAuthority(ROLE_USER));
+    if (user.isAdmin()) {
+      roles.add(new SimpleGrantedAuthority(ROLE_ADMIN));
+    }
+    return new PreAuthenticatedAuthenticationToken(new OpenexPrincipal() {
+      @Override
+      public String getId() {
+        return user.getId();
+      }
+
+      @Override
+      public Collection<? extends GrantedAuthority> getAuthorities() {
+        return roles;
+      }
+
+      @Override
+      public boolean isAdmin() {
+        return user.isAdmin();
+      }
+    }, "", roles);
+  }
 }
diff --git a/openex-api/src/test/java/io/openex/rest/AssetGroupApiTest.java b/openex-api/src/test/java/io/openex/rest/AssetGroupApiTest.java
new file mode 100644
index 0000000000..62340c4177
--- /dev/null
+++ b/openex-api/src/test/java/io/openex/rest/AssetGroupApiTest.java
@@ -0,0 +1,160 @@
+package io.openex.rest;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import io.openex.database.model.AssetGroup;
+import io.openex.database.repository.AssetGroupRepository;
+import io.openex.database.repository.EndpointRepository;
+import io.openex.rest.asset_group.form.AssetGroupInput;
+import io.openex.rest.utils.WithMockObserverUser;
+import io.openex.rest.utils.WithMockPlannerUser;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.util.List;
+
+import static io.openex.rest.asset_group.AssetGroupApi.ASSET_GROUP_URI;
+import static io.openex.rest.utils.JsonUtils.asJsonString;
+import static io.openex.rest.utils.JsonUtils.asStringJson;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+@TestMethodOrder(OrderAnnotation.class)
+@TestInstance(PER_CLASS)
+public class AssetGroupApiTest {
+
+  @Autowired
+  private MockMvc mvc;
+
+  @Autowired
+  private AssetGroupRepository assetGroupRepository;
+  @Autowired
+  private EndpointRepository endpointRepository;
+
+  @AfterAll
+  public void teardown() {
+    this.assetGroupRepository.deleteAll();
+    this.endpointRepository.deleteAll();
+  }
+
+  @DisplayName("Create asset group succeed")
+  @Test
+  @Order(1)
+  @WithMockUser(roles = {"ADMIN"})
+  void createAssetGroupTest() throws Exception {
+    // -- PREPARE --
+    AssetGroupInput assetGroupInput = new AssetGroupInput();
+    String name = "Zone";
+    assetGroupInput.setName(name);
+
+    // -- EXECUTE --
+    String response = this.mvc
+        .perform(post(ASSET_GROUP_URI)
+            .content(asJsonString(assetGroupInput))
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is2xxSuccessful())
+        .andReturn()
+        .getResponse()
+        .getContentAsString();
+    AssetGroup assetGroupResponse = asStringJson(response, AssetGroup.class);
+
+    // -- ASSERT --
+    assertEquals(name, assetGroupResponse.getName());
+  }
+
+  @DisplayName("Retrieve asset group succeed")
+  @Test
+  @Order(2)
+  @WithMockObserverUser
+  void retrieveAssetGroupTest() throws Exception {
+    // -- EXECUTE --
+    String response = this.mvc
+        .perform(get(ASSET_GROUP_URI).accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is2xxSuccessful())
+        .andReturn()
+        .getResponse()
+        .getContentAsString();
+    List<AssetGroup> assetGroupsResponse = asStringJson(response, new TypeReference<>() {
+    });
+
+    // -- ASSERT --
+    assertEquals(1, assetGroupsResponse.size());
+  }
+
+  @DisplayName("Update asset group succeed")
+  @Test
+  @Order(3)
+  @WithMockUser(roles = {"ADMIN"})
+  void updateGroupAssetTest() throws Exception {
+    // -- PREPARE --
+    AssetGroup assetGroupReponse = getFirstAssetGroup();
+    AssetGroupInput assetGroupInput = new AssetGroupInput();
+    String name = "Change zone name";
+    assetGroupInput.setName(name);
+
+    // -- EXECUTE --
+    String response = this.mvc
+        .perform(put(ASSET_GROUP_URI + "/" + assetGroupReponse.getId())
+            .content(asJsonString(assetGroupInput))
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is2xxSuccessful())
+        .andReturn()
+        .getResponse()
+        .getContentAsString();
+    assetGroupReponse = asStringJson(response, AssetGroup.class);
+
+    // -- ASSERT --
+    assertEquals(name, assetGroupReponse.getName());
+  }
+
+  @DisplayName("Delete asset group failed")
+  @Test
+  @Order(4)
+  @WithMockPlannerUser
+  void deleteAssetGroupFailedTest() throws Exception {
+    // -- PREPARE --
+    AssetGroup assetGroupReponse = getFirstAssetGroup();
+
+    // -- EXECUTE & ASSERT --
+    this.mvc.perform(delete(ASSET_GROUP_URI + "/" + assetGroupReponse.getId())
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is4xxClientError());
+  }
+
+  @DisplayName("Delete asset group succeed")
+  @Test
+  @Order(5)
+  @WithMockUser(roles = {"ADMIN"})
+  void deleteAssetGroupSucceedTest() throws Exception {
+    // -- PREPARE --
+    AssetGroup assetGroupReponse = getFirstAssetGroup();
+
+    // -- EXECUTE --
+    this.mvc.perform(delete(ASSET_GROUP_URI + "/" + assetGroupReponse.getId())
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is2xxSuccessful());
+
+    // -- ASSERT --
+    assertEquals(0, this.assetGroupRepository.count());
+  }
+
+  // -- PRIVATE --
+
+  private AssetGroup getFirstAssetGroup() {
+    return this.assetGroupRepository.findAll().iterator().next();
+  }
+
+}
diff --git a/openex-api/src/test/java/io/openex/rest/EndpointApiTest.java b/openex-api/src/test/java/io/openex/rest/EndpointApiTest.java
new file mode 100644
index 0000000000..a3003b5ad5
--- /dev/null
+++ b/openex-api/src/test/java/io/openex/rest/EndpointApiTest.java
@@ -0,0 +1,164 @@
+package io.openex.rest;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import io.openex.database.model.Endpoint;
+import io.openex.database.repository.EndpointRepository;
+import io.openex.rest.asset.endpoint.form.EndpointInput;
+import io.openex.rest.utils.WithMockObserverUser;
+import io.openex.rest.utils.WithMockPlannerUser;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.util.List;
+
+import static io.openex.database.model.Endpoint.OS_TYPE.LINUX;
+import static io.openex.rest.asset.endpoint.EndpointApi.ENDPOINT_URI;
+import static io.openex.rest.utils.JsonUtils.asJsonString;
+import static io.openex.rest.utils.JsonUtils.asStringJson;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+@TestMethodOrder(OrderAnnotation.class)
+@TestInstance(PER_CLASS)
+public class EndpointApiTest {
+
+  @Autowired
+  private MockMvc mvc;
+
+  @Autowired
+  private EndpointRepository endpointRepository;
+
+  @AfterAll
+  void teardown() {
+    this.endpointRepository.deleteAll();
+  }
+
+  @DisplayName("Create endpoint succeed")
+  @Test
+  @Order(1)
+  @WithMockUser(roles = {"ADMIN"})
+  void createEndpointTest() throws Exception {
+    // -- PREPARE --
+    EndpointInput endpointInput = new EndpointInput();
+    String name = "Personal PC";
+    endpointInput.setName(name);
+    endpointInput.setIp("127.0.0.1");
+    endpointInput.setHostname("hostname");
+    endpointInput.setOs(LINUX.name());
+
+    // -- EXECUTE --
+    String response = this.mvc
+        .perform(post(ENDPOINT_URI)
+            .content(asJsonString(endpointInput))
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is2xxSuccessful())
+        .andReturn()
+        .getResponse()
+        .getContentAsString();
+    Endpoint endpointResponse = asStringJson(response, Endpoint.class);
+
+    // -- ASSERT --
+    assertEquals(name, endpointResponse.getName());
+  }
+
+  @DisplayName("Retrieve endpoint succeed")
+  @Test
+  @Order(2)
+  @WithMockObserverUser
+  void retrieveEndpointTest() throws Exception {
+    // -- EXECUTE --
+    String response = this.mvc
+        .perform(get(ENDPOINT_URI).accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is2xxSuccessful())
+        .andReturn()
+        .getResponse()
+        .getContentAsString();
+    List<Endpoint> endpoints = asStringJson(response, new TypeReference<>() {
+    });
+
+    // -- ASSERT --
+    assertEquals(1, endpoints.size());
+  }
+
+  @DisplayName("Update endpoint succeed")
+  @Test
+  @Order(3)
+  @WithMockUser(roles = {"ADMIN"})
+  void updateEndpointTest() throws Exception {
+    // -- PREPARE --
+    Endpoint endpointResponse = getFirstEndpoint();
+    EndpointInput endpointInput = new EndpointInput();
+    String name = "Professional PC";
+    endpointInput.setName(name);
+    endpointInput.setIp(endpointResponse.getIp());
+    endpointInput.setHostname(endpointResponse.getHostname());
+    endpointInput.setOs(endpointResponse.getOs().name());
+
+    // -- EXECUTE --
+    String response = this.mvc
+        .perform(put(ENDPOINT_URI + "/" + endpointResponse.getId())
+            .content(asJsonString(endpointInput))
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is2xxSuccessful())
+        .andReturn()
+        .getResponse()
+        .getContentAsString();
+    endpointResponse = asStringJson(response, Endpoint.class);
+
+    // -- ASSERT --
+    assertEquals(name, endpointResponse.getName());
+  }
+
+
+  @DisplayName("Delete endpoint failed")
+  @Test
+  @Order(3)
+  @WithMockPlannerUser
+  void deleteEndpointFailedTest() throws Exception {
+    // -- PREPARE --
+    Endpoint endpointResponse = getFirstEndpoint();
+
+    // -- EXECUTE & ASSERT --
+    this.mvc.perform(delete(ENDPOINT_URI + "/" + endpointResponse.getId())
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is4xxClientError());
+  }
+
+  @DisplayName("Delete endpoint succeed")
+  @Test
+  @Order(5)
+  @WithMockUser(roles = {"ADMIN"})
+  void deleteEndpointSucceedTest() throws Exception {
+    // -- PREPARE --
+    Endpoint endpointResponse = getFirstEndpoint();
+
+    // -- EXECUTE --
+    this.mvc.perform(delete(ENDPOINT_URI + "/" + endpointResponse.getId())
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_JSON))
+        .andExpect(status().is2xxSuccessful());
+
+    // -- ASSERT --
+    assertEquals(0, this.endpointRepository.count());
+  }
+
+  // -- PRIVATE --
+
+  private Endpoint getFirstEndpoint() {
+    return this.endpointRepository.findAll().iterator().next();
+  }
+
+}
diff --git a/openex-api/src/test/java/io/openex/rest/utils/JsonUtils.java b/openex-api/src/test/java/io/openex/rest/utils/JsonUtils.java
new file mode 100644
index 0000000000..b2cdc8053b
--- /dev/null
+++ b/openex-api/src/test/java/io/openex/rest/utils/JsonUtils.java
@@ -0,0 +1,47 @@
+package io.openex.rest.utils;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+public class JsonUtils {
+
+  private static ObjectMapper mapper;
+
+  private static ObjectMapper getMapper() {
+    if (mapper != null) {
+      return mapper;
+    }
+    mapper = new ObjectMapper();
+    mapper.registerModule(new JavaTimeModule());
+    return mapper;
+  }
+
+  public static String asJsonString(@NotNull final Object obj) {
+    try {
+      return getMapper().writeValueAsString(obj);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static <T> T asStringJson(@NotBlank final String obj, @NotNull final Class<T> clazz) {
+    try {
+      return getMapper().readValue(obj, clazz);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static <T> T asStringJson(@NotBlank final String obj, @NotNull final TypeReference<T> typeReference) {
+    try {
+      return getMapper().readValue(obj, typeReference);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+}
diff --git a/openex-api/src/test/java/io/openex/rest/utils/WithMockObserverUser.java b/openex-api/src/test/java/io/openex/rest/utils/WithMockObserverUser.java
new file mode 100644
index 0000000000..d5a0159b30
--- /dev/null
+++ b/openex-api/src/test/java/io/openex/rest/utils/WithMockObserverUser.java
@@ -0,0 +1,14 @@
+package io.openex.rest.utils;
+
+import org.springframework.security.test.context.support.WithSecurityContext;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import static io.openex.rest.utils.WithMockObserverUserSecurityContextFactory.MOCK_USER_OBSERVER_EMAIL;
+
+@Retention(RetentionPolicy.RUNTIME)
+@WithSecurityContext(factory = WithMockObserverUserSecurityContextFactory.class)
+public @interface WithMockObserverUser {
+    String email() default MOCK_USER_OBSERVER_EMAIL;
+}
diff --git a/openex-api/src/test/java/io/openex/rest/utils/WithMockObserverUserSecurityContextFactory.java b/openex-api/src/test/java/io/openex/rest/utils/WithMockObserverUserSecurityContextFactory.java
new file mode 100644
index 0000000000..6a715da207
--- /dev/null
+++ b/openex-api/src/test/java/io/openex/rest/utils/WithMockObserverUserSecurityContextFactory.java
@@ -0,0 +1,73 @@
+package io.openex.rest.utils;
+
+import io.openex.database.model.Grant;
+import io.openex.database.model.Group;
+import io.openex.database.model.User;
+import io.openex.database.repository.GrantRepository;
+import io.openex.database.repository.GroupRepository;
+import io.openex.database.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.test.context.support.WithSecurityContextFactory;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.List;
+
+import static io.openex.database.model.Grant.GRANT_TYPE.OBSERVER;
+import static io.openex.service.UserService.buildAuthenticationToken;
+
+@Component
+public class WithMockObserverUserSecurityContextFactory implements WithSecurityContextFactory<WithMockObserverUser> {
+
+  public static final String MOCK_USER_OBSERVER_EMAIL = "observer@opencti.io";
+  @Autowired
+  private GrantRepository grantRepository;
+  @Autowired
+  private GroupRepository groupRepository;
+  @Autowired
+  private UserRepository userRepository;
+
+  @Override
+  public SecurityContext createSecurityContext(WithMockObserverUser customUser) {
+    User user = this.userRepository.findByEmail(customUser.email()).orElseThrow();
+    Authentication authentication = buildAuthenticationToken(user);
+    SecurityContext context = SecurityContextHolder.createEmptyContext();
+    context.setAuthentication(authentication);
+    return context;
+  }
+
+  @PostConstruct
+  private void postConstruct() {
+    this.createObserverMockUser();
+  }
+
+  @PreDestroy
+  public void preDestroy() {
+    this.userRepository.deleteById(this.userRepository.findByEmail(MOCK_USER_OBSERVER_EMAIL).orElseThrow().getId());
+  }
+
+  private void createObserverMockUser() {
+    if (this.userRepository.findByEmail(MOCK_USER_OBSERVER_EMAIL).isPresent()) {
+      return;
+    }
+
+    // Create group
+    Group group = new Group();
+    group.setName("Observer group");
+    group = this.groupRepository.save(group);
+    // Create grant
+    Grant grant = new Grant();
+    grant.setName(OBSERVER);
+    grant.setGroup(group);
+    this.grantRepository.save(grant);
+    // Create user
+    User user = new User();
+    user.setGroups(List.of(group));
+    user.setEmail(MOCK_USER_OBSERVER_EMAIL);
+    this.userRepository.save(user);
+  }
+}
diff --git a/openex-api/src/test/java/io/openex/rest/utils/WithMockPlannerUser.java b/openex-api/src/test/java/io/openex/rest/utils/WithMockPlannerUser.java
new file mode 100644
index 0000000000..85db0f29a5
--- /dev/null
+++ b/openex-api/src/test/java/io/openex/rest/utils/WithMockPlannerUser.java
@@ -0,0 +1,14 @@
+package io.openex.rest.utils;
+
+import org.springframework.security.test.context.support.WithSecurityContext;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import static io.openex.rest.utils.WithMockPlannerUserSecurityContextFactory.MOCK_USER_PLANNER_EMAIL;
+
+@Retention(RetentionPolicy.RUNTIME)
+@WithSecurityContext(factory = WithMockPlannerUserSecurityContextFactory.class)
+public @interface WithMockPlannerUser {
+    String email() default MOCK_USER_PLANNER_EMAIL;
+}
diff --git a/openex-api/src/test/java/io/openex/rest/utils/WithMockPlannerUserSecurityContextFactory.java b/openex-api/src/test/java/io/openex/rest/utils/WithMockPlannerUserSecurityContextFactory.java
new file mode 100644
index 0000000000..f663f9efe6
--- /dev/null
+++ b/openex-api/src/test/java/io/openex/rest/utils/WithMockPlannerUserSecurityContextFactory.java
@@ -0,0 +1,73 @@
+package io.openex.rest.utils;
+
+import io.openex.database.model.Grant;
+import io.openex.database.model.Group;
+import io.openex.database.model.User;
+import io.openex.database.repository.GrantRepository;
+import io.openex.database.repository.GroupRepository;
+import io.openex.database.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.test.context.support.WithSecurityContextFactory;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.List;
+
+import static io.openex.database.model.Grant.GRANT_TYPE.PLANNER;
+import static io.openex.service.UserService.buildAuthenticationToken;
+
+@Component
+public class WithMockPlannerUserSecurityContextFactory implements WithSecurityContextFactory<WithMockPlannerUser> {
+
+  public static final String MOCK_USER_PLANNER_EMAIL = "planner@opencti.io";
+  @Autowired
+  private GrantRepository grantRepository;
+  @Autowired
+  private GroupRepository groupRepository;
+  @Autowired
+  private UserRepository userRepository;
+
+  @Override
+  public SecurityContext createSecurityContext(WithMockPlannerUser customUser) {
+    User user = this.userRepository.findByEmail(customUser.email()).orElseThrow();
+    Authentication authentication = buildAuthenticationToken(user);
+    SecurityContext context = SecurityContextHolder.createEmptyContext();
+    context.setAuthentication(authentication);
+    return context;
+  }
+
+  @PostConstruct
+  private void postConstruct() {
+    this.createPlannerMockUser();
+  }
+
+  @PreDestroy
+  public void preDestroy() {
+    this.userRepository.deleteById(this.userRepository.findByEmail(MOCK_USER_PLANNER_EMAIL).orElseThrow().getId());
+  }
+
+  private void createPlannerMockUser() {
+    if (this.userRepository.findByEmail(MOCK_USER_PLANNER_EMAIL).isPresent()) {
+      return;
+    }
+
+    // Create group
+    Group group = new Group();
+    group.setName("Planner group");
+    group = this.groupRepository.save(group);
+    // Create grant
+    Grant grant = new Grant();
+    grant.setName(PLANNER);
+    grant.setGroup(group);
+    this.grantRepository.save(grant);
+    // Create user
+    User user = new User();
+    user.setGroups(List.of(group));
+    user.setEmail(MOCK_USER_PLANNER_EMAIL);
+    this.userRepository.save(user);
+  }
+}
diff --git a/openex-api/src/test/java/io/openex/service/EndpointServiceTest.java b/openex-api/src/test/java/io/openex/service/EndpointServiceTest.java
index c4b338b576..a65f0a3a7f 100644
--- a/openex-api/src/test/java/io/openex/service/EndpointServiceTest.java
+++ b/openex-api/src/test/java/io/openex/service/EndpointServiceTest.java
@@ -73,7 +73,7 @@ void retrieveEndpointTest() {
 
     List<Endpoint> endpoints = this.endpointService.endpoints();
     assertNotNull(endpoints);
-    assertEquals(ENDPOINT_ID, endpoints.get(0).getId());
+    assertTrue(endpoints.stream().map(Endpoint::getId).toList().contains(ENDPOINT_ID));
   }
 
   @DisplayName("Update endpoint")