Add tests for the NotificationService 06/138606/4
authorFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Wed, 31 Jul 2024 13:35:45 +0000 (15:35 +0200)
committerFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Thu, 1 Aug 2024 07:46:49 +0000 (09:46 +0200)
- change approach how ValidationService is injected into NotificationService
- add tests for NotificationService
- assert invokation of NotificationService instead of ValidationService in HttpEntryTest now

Issue-ID: AAI-3940
Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de>
Change-Id: I4476857f7192f53de216f0515f0e69c642c33d1c

aai-core/src/main/java/org/onap/aai/prevalidation/ValidationConfiguration.java
aai-core/src/main/java/org/onap/aai/rest/notification/NotificationService.java
aai-core/src/main/java/org/onap/aai/web/KafkaConfig.java
aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryNotificationIntegrationTest.java [new file with mode: 0644]
aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryTest.java
aai-core/src/test/java/org/onap/aai/rest/notification/NotificationServiceTest.java [new file with mode: 0644]
aai-core/src/test/java/org/onap/aai/rest/notification/UEBNotificationTest.java [moved from aai-core/src/test/java/org/onap/aai/rest/ueb/UEBNotificationTest.java with 98% similarity]

index 7030cf7..c1a4f2a 100644 (file)
@@ -26,10 +26,9 @@ import org.onap.aai.restclient.RestClient;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Profile;
 
-@Profile("pre-validation")
 @Configuration
+@ConditionalOnProperty(name = "aai.notification.validation.enabled", havingValue = "true")
 public class ValidationConfiguration {
 
     @Bean(name = "validationRestClient")
index d6a3f89..9c3dde1 100644 (file)
@@ -35,7 +35,6 @@ import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.Introspector;
 import org.onap.aai.introspection.LoaderFactory;
 import org.onap.aai.prevalidation.ValidationService;
-import org.onap.aai.rest.db.HttpEntry;
 import org.onap.aai.serialization.db.DBSerializer;
 import org.onap.aai.serialization.engines.query.QueryEngine;
 import org.onap.aai.setup.SchemaVersion;
@@ -43,8 +42,8 @@ import org.onap.aai.util.AAIConfig;
 import org.onap.aai.util.delta.DeltaEvents;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -52,27 +51,22 @@ public class NotificationService {
 
   public static final Logger LOGGER = LoggerFactory.getLogger(NotificationService.class);
 
+  private final ValidationService validationService;
   private final LoaderFactory loaderFactory;
   private final boolean isDeltaEventsEnabled;
   private final String basePath;
 
   public NotificationService(
+    @Nullable ValidationService validationService,
     LoaderFactory loaderFactory,
     @Value("${schema.uri.base.path}") String basePath,
     @Value("${delta.events.enabled:false}") boolean isDeltaEventsEnabled) {
+    this.validationService = validationService;
     this.loaderFactory = loaderFactory;
     this.basePath = basePath;
     this.isDeltaEventsEnabled = isDeltaEventsEnabled;
   }
 
-  /**
-   * Inject the validation service if the profile pre-valiation is enabled,
-   * Otherwise this variable will be set to null and thats why required=false
-   * so that it can continue even if pre validation isn't enabled
-   */
-  @Autowired(required = false)
-  private ValidationService validationService;
-
   /**
    * Generate notification events for the resulting db requests.
    */
@@ -100,9 +94,7 @@ public class NotificationService {
       LOGGER.warn("Encountered exception generating events", e);
     }
 
-    // Since @Autowired required is set to false, we need to do a null check
-    // for the existence of the validationService since its only enabled if profile
-    // is enabled
+    // validation is configurable via aai.notification.validation.enabled
     if (validationService != null) {
       validationService.validate(notification.getEvents());
     }
index 05e4768..c1e8357 100644 (file)
@@ -176,9 +176,10 @@ public class KafkaConfig {
     }
 
     @Bean
+    @ConditionalOnMissingBean
     public NotificationService notificationService(LoaderFactory loaderFactory,
     @Value("${schema.uri.base.path}") String basePath,
     @Value("${delta.events.enabled:false}") boolean isDeltaEventsEnabled) {
-        return new NotificationService(loaderFactory, basePath, isDeltaEventsEnabled);
+        return new NotificationService(null, loaderFactory, basePath, isDeltaEventsEnabled);
     }
 }
diff --git a/aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryNotificationIntegrationTest.java b/aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryNotificationIntegrationTest.java
new file mode 100644 (file)
index 0000000..e8e3d69
--- /dev/null
@@ -0,0 +1,212 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2024 Deutsche Telekom. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aai.rest.db;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.javatuples.Pair;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.onap.aai.AAISetup;
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.introspection.Introspector;
+import org.onap.aai.introspection.Loader;
+import org.onap.aai.introspection.ModelType;
+import org.onap.aai.parsers.query.QueryParser;
+import org.onap.aai.query.builder.QueryOptions;
+import org.onap.aai.rest.notification.UEBNotification;
+import org.onap.aai.restcore.HttpMethod;
+import org.onap.aai.serialization.engines.TransactionalGraphEngine;
+import org.springframework.test.annotation.DirtiesContext;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
+public class HttpEntryNotificationIntegrationTest extends AAISetup {
+
+  private static final MediaType APPLICATION_JSON = MediaType.valueOf("application/json");
+  private Loader loader;
+  private TransactionalGraphEngine dbEngine;
+  private GraphTraversalSource traversal;
+  private HttpHeaders httpHeaders;
+  private UriInfo uriInfo;
+  private MultivaluedMap<String, String> headersMultiMap;
+  private MultivaluedMap<String, String> queryParameters;
+  private List<String> aaiRequestContextList;
+  private List<MediaType> outputMediaTypes;
+
+  ObjectMapper mapper = new ObjectMapper();
+
+  @Before
+  public void setup() {
+
+    httpHeaders = Mockito.mock(HttpHeaders.class);
+    uriInfo = Mockito.mock(UriInfo.class);
+
+    headersMultiMap = new MultivaluedHashMap<>();
+    queryParameters = Mockito.spy(new MultivaluedHashMap<>());
+
+    headersMultiMap.add("X-FromAppId", "JUNIT");
+    headersMultiMap.add("X-TransactionId", UUID.randomUUID().toString());
+    headersMultiMap.add("Real-Time", "true");
+    headersMultiMap.add("Accept", "application/json");
+    headersMultiMap.add("aai-request-context", "");
+
+    outputMediaTypes = new ArrayList<>();
+    outputMediaTypes.add(APPLICATION_JSON);
+
+    aaiRequestContextList = new ArrayList<>();
+    aaiRequestContextList.add("");
+
+    traversalHttpEntry.setHttpEntryProperties(schemaVersions.getDefaultVersion());
+    loader = traversalHttpEntry.getLoader();
+    dbEngine = traversalHttpEntry.getDbEngine();
+    traversal = dbEngine.tx().traversal();
+
+    when(httpHeaders.getAcceptableMediaTypes()).thenReturn(outputMediaTypes);
+    when(httpHeaders.getRequestHeaders()).thenReturn(headersMultiMap);
+
+    when(httpHeaders.getRequestHeader("aai-request-context")).thenReturn(aaiRequestContextList);
+
+    when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
+    when(uriInfo.getQueryParameters(false)).thenReturn(queryParameters);
+
+    // TODO - Check if this is valid since RemoveDME2QueryParameters seems to be
+    // very unreasonable
+    Mockito.doReturn(null).when(queryParameters).remove(any());
+
+    when(httpHeaders.getMediaType()).thenReturn(APPLICATION_JSON);
+  }
+
+  @After
+  public void rollback() {
+    dbEngine.rollback();
+  }
+
+  @Test
+  public void notificationOnRelatedToTest() throws UnsupportedEncodingException, AAIException {
+
+    Loader ld = loaderFactory.createLoaderForVersion(ModelType.MOXY, schemaVersions.getDefaultVersion());
+    UEBNotification uebNotification = Mockito.spy(new UEBNotification(ld, loaderFactory, schemaVersions));
+    traversalHttpEntry.setHttpEntryProperties(schemaVersions.getDefaultVersion(), uebNotification);
+
+    Loader loader = traversalHttpEntry.getLoader();
+    TransactionalGraphEngine dbEngine = traversalHttpEntry.getDbEngine();
+    // Put pserver
+    String uri = "/cloud-infrastructure/pservers/pserver/junit-edge-test-pserver";
+    String content = "{\"hostname\":\"junit-edge-test-pserver\"}";
+    doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.PUT, uri, content);
+    // Put complex
+    uri = "/cloud-infrastructure/complexes/complex/junit-edge-test-complex";
+    content = "{\"physical-location-id\":\"junit-edge-test-complex\",\"physical-location-type\":\"AAIDefault\",\"street1\":\"AAIDefault\",\"city\":\"AAIDefault\",\"state\":\"NJ\",\"postal-code\":\"07748\",\"country\":\"USA\",\"region\":\"US\"}";
+    doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.PUT, uri, content);
+
+    // PutEdge
+    uri = "/cloud-infrastructure/complexes/complex/junit-edge-test-complex/relationship-list/relationship";
+    content = "{\"related-to\":\"pserver\",\"related-link\":\"/aai/" + schemaVersions.getDefaultVersion().toString()
+        + "/cloud-infrastructure/pservers/pserver/junit-edge-test-pserver\",\"relationship-label\":\"org.onap.relationships.inventory.LocatedIn\"}";
+
+    doNothing().when(uebNotification).triggerEvents();
+    Response response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.PUT_EDGE, uri,
+        content);
+
+    assertEquals("Expected the pserver relationship to be deleted", 200, response.getStatus());
+    assertEquals("Two notifications", 2, uebNotification.getEvents().size());
+    assertEquals("Notification generated for PUT edge", "UPDATE",
+        uebNotification.getEvents().get(0).getEventHeader().getValue("action").toString());
+    assertThat("Event body for the edge create has the related to",
+        uebNotification.getEvents().get(0).getObj().marshal(false),
+        containsString("cloud-infrastructure/pservers/pserver/junit-edge-test-pserver"));
+
+    response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.DELETE_EDGE, uri, content);
+    assertEquals("Expected the pserver relationship to be deleted", 204, response.getStatus());
+    assertEquals("Two notifications", 2, uebNotification.getEvents().size());
+    assertEquals("Notification generated for DELETE edge", "UPDATE",
+        uebNotification.getEvents().get(0).getEventHeader().getValue("action").toString());
+    assertThat("Event body for the edge delete does not have the related to",
+        uebNotification.getEvents().get(0).getObj().marshal(false),
+        not(containsString("cloud-infrastructure/pservers/pserver/junit-edge-test-pserver")));
+    dbEngine.rollback();
+
+  }
+
+  private Response doRequest(HttpEntry httpEntry, Loader loader, TransactionalGraphEngine dbEngine, HttpMethod method,
+      String uri, String requestBody) throws UnsupportedEncodingException, AAIException {
+    return doRequest(httpEntry, loader, dbEngine, method, uri, requestBody, null);
+  }
+
+  private Response doRequest(HttpEntry httpEntry, Loader loader, TransactionalGraphEngine dbEngine, HttpMethod method,
+      String uri, String requestBody, QueryOptions queryOptions) throws UnsupportedEncodingException, AAIException {
+    URI uriObject = UriBuilder.fromPath(uri).build();
+    QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uriObject);
+    String objType;
+    if (!uriQuery.getContainerType().equals("")) {
+      objType = uriQuery.getContainerType();
+    } else {
+      objType = uriQuery.getResultType();
+    }
+    if (uri.endsWith("relationship")) {
+      objType = "relationship";
+    }
+    Introspector obj;
+    if (method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP)) {
+      obj = loader.introspectorFromName(objType);
+    } else {
+      obj = loader.unmarshal(objType, requestBody, org.onap.aai.restcore.MediaType.getEnum("application/json"));
+    }
+
+    DBRequest.Builder builder = new DBRequest.Builder(method, uriObject, uriQuery, obj, httpHeaders, uriInfo,
+        "JUNIT-TRANSACTION");
+    DBRequest dbRequest = requestBody != null
+        ? builder.rawRequestContent(requestBody).build()
+        : builder.build();
+
+    List<DBRequest> dbRequestList = Collections.singletonList(dbRequest);
+
+    Pair<Boolean, List<Pair<URI, Response>>> responsesTuple = httpEntry.process(dbRequestList, "JUNIT",
+        Collections.emptySet(), true, queryOptions);
+    return responsesTuple.getValue1().get(0).getValue1();
+  }
+}
index ccc2c74..5ecffbc 100644 (file)
  import static org.junit.Assert.assertThat;
  import static org.junit.Assert.assertTrue;
  import static org.mockito.ArgumentMatchers.any;
- import static org.mockito.Mockito.doNothing;
- import static org.mockito.Mockito.times;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
  import static org.mockito.Mockito.verify;
  import static org.mockito.Mockito.when;
 
@@ -95,6 +97,7 @@
  import org.onap.aai.rest.db.responses.Relationship;
  import org.onap.aai.rest.db.responses.RelationshipWrapper;
  import org.onap.aai.rest.db.responses.ServiceException;
+import org.onap.aai.rest.notification.NotificationService;
 import org.onap.aai.rest.notification.UEBNotification;
 import org.onap.aai.restcore.HttpMethod;
  import org.onap.aai.serialization.engines.QueryStyle;
@@ -110,6 +113,7 @@ import org.onap.aai.restcore.HttpMethod;
  public class HttpEntryTest extends AAISetup {
 
      @MockBean ValidationService validationService;
+     @MockBean NotificationService notificationService;
 
      protected static final MediaType APPLICATION_JSON = MediaType.valueOf("application/json");
 
@@ -213,7 +217,7 @@ import org.onap.aai.restcore.HttpMethod;
 
          JSONAssert.assertEquals(expectedResponseBody, actualResponseBody, JSONCompareMode.NON_EXTENSIBLE);
          assertEquals("Expected the pserver to be returned", 200, response.getStatus());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -249,7 +253,7 @@ import org.onap.aai.restcore.HttpMethod;
 
          JSONAssert.assertEquals(expectedResponseBody, actualResponseBody, JSONCompareMode.NON_EXTENSIBLE);
          assertEquals("Expected the pservers to be returned", 200, response.getStatus());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -283,7 +287,7 @@ import org.onap.aai.restcore.HttpMethod;
          assertNull(response.getHeaderString("total-results"));
          assertEquals(1, actualResponseBody.getJSONArray("pserver").length());
          assertEquals("Expected the pservers to be returned", 200, response.getStatus());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
 
          queryOptions = QueryOptions.builder().pageable(new Pageable(0,5).includeTotalCount()).build();
          response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.GET, uri, queryOptions);
@@ -307,6 +311,7 @@ import org.onap.aai.restcore.HttpMethod;
 
          assertTrue( response.getEntity().toString().contains("Node Not Found:No Node of type pserver found at: /cloud-infrastructure/pservers"));
          assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatus());
+         verify(notificationService, never()).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
 
      }
 
@@ -344,7 +349,7 @@ import org.onap.aai.restcore.HttpMethod;
          assertEquals(2, Integer.parseInt(totalPages));
          assertEquals(1, actualResponseBody.getJSONArray("pserver").length());
          assertEquals("Expected the pservers to be returned", 200, response.getStatus());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
 
          queryOptions = QueryOptions.builder().pageable(new Pageable(0, 2)).build();
          response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.GET, uri, queryOptions);
@@ -392,7 +397,6 @@ import org.onap.aai.restcore.HttpMethod;
          actualResponseBody = new JSONObject(response.getEntity().toString());
          assertEquals("theEquipType2", actualResponseBody.getJSONArray("pserver").getJSONObject(0).getString("equip-type"));
          assertEquals("theEquipType", actualResponseBody.getJSONArray("pserver").getJSONObject(1).getString("equip-type"));
-
      }
 
      @Test
@@ -401,6 +405,7 @@ import org.onap.aai.restcore.HttpMethod;
 
          Response response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.GET, uri);
          assertEquals("The pserver is not found", 404, response.getStatus());
+         verify(notificationService, never()).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -410,7 +415,7 @@ import org.onap.aai.restcore.HttpMethod;
 
          Response response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.PUT, uri, requestBody);
          assertEquals("Expecting the pserver to be created", 201, response.getStatus());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -428,6 +433,7 @@ import org.onap.aai.restcore.HttpMethod;
          assertEquals(
                  "Resource version specified on create:resource-version passed for create of /cloud-infrastructure/pservers/pserver/theHostname",
                  errorResponseEntity.getRequestError().getServiceException().getVariables().get(2));
+        verify(notificationService, never()).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -449,7 +455,7 @@ import org.onap.aai.restcore.HttpMethod;
          assertEquals("Expecting the pserver to be updated", 200, response.getStatus());
          assertTrue("That old properties are removed",
                  traversal.V().has("hostname", "updatedHostname").hasNot("number-of-cpus").hasNext());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -473,6 +479,7 @@ import org.onap.aai.restcore.HttpMethod;
          assertEquals(
                  "Precondition Failed:resource-version MISMATCH for update of /cloud-infrastructure/pservers/pserver/updatedHostname",
                  errorResponseEntity.getRequestError().getServiceException().getVariables().get(2));
+        verify(notificationService, never()).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -518,7 +525,7 @@ import org.onap.aai.restcore.HttpMethod;
                  traversal.V().has("aai-node-type", "p-interface").has("aai-uri", uri).has("interface-name", "p1")
                          .out("tosca.relationships.network.BindsTo").has("aai-node-type", "pserver")
                          .has("hostname", "hostname").hasNext());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -538,7 +545,7 @@ import org.onap.aai.restcore.HttpMethod;
          assertTrue("object should be updated while keeping old properties",
                  traversal.V().has("aai-node-type", "pserver").has("hostname", "new-hostname")
                          .has("equip-type", "the-equip-type").hasNext());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -555,7 +562,7 @@ import org.onap.aai.restcore.HttpMethod;
                  doDelete(resourceVersion, uri, "pserver").getStatus());
          assertTrue("Expecting the pserver to be deleted",
                  !traversal.V().has("aai-node-type", "pserver").has("hostname", "the-hostname").hasNext());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -605,7 +612,7 @@ import org.onap.aai.restcore.HttpMethod;
                  .has(EdgeProperty.PREVENT_DELETE.toString(), "IN");
          assertTrue("p-server has incoming edge from complex", vertexQuery.hasNext());
          assertTrue("Created Edge has expected properties", edgeQuery.hasNext());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -724,7 +731,7 @@ import org.onap.aai.restcore.HttpMethod;
          assertEquals("Expected get to succeed", 200, response.getStatus());
          assertThat(responseEntity, containsString("/cloud-infrastructure/pservers/pserver/pserver-1"));
          assertThat(responseEntity, containsString("/cloud-infrastructure/pservers/pserver/pserver-2"));
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -759,7 +766,7 @@ import org.onap.aai.restcore.HttpMethod;
          assertEquals("Expected the response to be successful", 200, response.getStatus());
          assertThat("Related pserver is returned", response.getEntity().toString(),
                  containsString("\"hostname\":\"related-to-pserver\""));
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
 
      }
 
@@ -788,7 +795,7 @@ import org.onap.aai.restcore.HttpMethod;
          Response response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.GET, uri);
          assertThat("Related to pserver is returned.", response.getEntity().toString(),
                  containsString("\"hostname\":\"abstract-pserver\""));
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -833,7 +840,7 @@ import org.onap.aai.restcore.HttpMethod;
                  relationships[0].getRelatedLink());
          assertEquals("complex.physical-location-id", relationships[0].getRelationshipData()[0].getRelationshipKey());
          assertEquals("related-to-complex", relationships[0].getRelationshipData()[0].getRelationshipValue());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      @Test
@@ -891,53 +898,7 @@ import org.onap.aai.restcore.HttpMethod;
 
          JSONAssert.assertEquals(expectedResponseBody, actualResponseBody, JSONCompareMode.NON_EXTENSIBLE);
          queryParameters.remove("format");
-         verify(validationService, times(1)).validate(any());
-     }
-
-     @Test
-     public void notificationOnRelatedToTest() throws UnsupportedEncodingException, AAIException {
-
-         Loader ld = loaderFactory.createLoaderForVersion(ModelType.MOXY, schemaVersions.getDefaultVersion());
-         UEBNotification uebNotification = Mockito.spy(new UEBNotification(ld, loaderFactory, schemaVersions));
-         traversalHttpEntry.setHttpEntryProperties(schemaVersions.getDefaultVersion(), uebNotification);
-
-         Loader loader = traversalHttpEntry.getLoader();
-         TransactionalGraphEngine dbEngine = traversalHttpEntry.getDbEngine();
-         // Put pserver
-         String uri = "/cloud-infrastructure/pservers/pserver/junit-edge-test-pserver";
-         String content = "{\"hostname\":\"junit-edge-test-pserver\"}";
-         doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.PUT, uri, content);
-         // Put complex
-         uri = "/cloud-infrastructure/complexes/complex/junit-edge-test-complex";
-         content = "{\"physical-location-id\":\"junit-edge-test-complex\",\"physical-location-type\":\"AAIDefault\",\"street1\":\"AAIDefault\",\"city\":\"AAIDefault\",\"state\":\"NJ\",\"postal-code\":\"07748\",\"country\":\"USA\",\"region\":\"US\"}";
-         doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.PUT, uri, content);
-
-         // PutEdge
-         uri = "/cloud-infrastructure/complexes/complex/junit-edge-test-complex/relationship-list/relationship";
-         content = "{\"related-to\":\"pserver\",\"related-link\":\"/aai/" + schemaVersions.getDefaultVersion().toString()
-                 + "/cloud-infrastructure/pservers/pserver/junit-edge-test-pserver\",\"relationship-label\":\"org.onap.relationships.inventory.LocatedIn\"}";
-
-         doNothing().when(uebNotification).triggerEvents();
-         Response response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.PUT_EDGE, uri, content);
-
-         assertEquals("Expected the pserver relationship to be deleted", 200, response.getStatus());
-         assertEquals("Two notifications", 2, uebNotification.getEvents().size());
-         assertEquals("Notification generated for PUT edge", "UPDATE",
-                 uebNotification.getEvents().get(0).getEventHeader().getValue("action").toString());
-         assertThat("Event body for the edge create has the related to",
-                 uebNotification.getEvents().get(0).getObj().marshal(false),
-                 containsString("cloud-infrastructure/pservers/pserver/junit-edge-test-pserver"));
-
-         response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.DELETE_EDGE, uri, content);
-         assertEquals("Expected the pserver relationship to be deleted", 204, response.getStatus());
-         assertEquals("Two notifications", 2, uebNotification.getEvents().size());
-         assertEquals("Notification generated for DELETE edge", "UPDATE",
-                 uebNotification.getEvents().get(0).getEventHeader().getValue("action").toString());
-         assertThat("Event body for the edge delete does not have the related to",
-                 uebNotification.getEvents().get(0).getObj().marshal(false),
-                 not(containsString("cloud-infrastructure/pservers/pserver/junit-edge-test-pserver")));
-         dbEngine.rollback();
-
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
 
      private Response doRequest(HttpEntry httpEntry, Loader loader, TransactionalGraphEngine dbEngine, HttpMethod method,
@@ -1035,6 +996,6 @@ import org.onap.aai.restcore.HttpMethod;
 
          JSONAssert.assertEquals(expectedResponseBody, actualResponseBody, JSONCompareMode.NON_EXTENSIBLE);
          assertEquals("Expected the pserver to be returned", 200, response.getStatus());
-         verify(validationService, times(1)).validate(any());
+         verify(notificationService, times(1)).generateEvents(any(), anyInt(), any(), any(), any(), any(), any(), any());
      }
  }
diff --git a/aai-core/src/test/java/org/onap/aai/rest/notification/NotificationServiceTest.java b/aai-core/src/test/java/org/onap/aai/rest/notification/NotificationServiceTest.java
new file mode 100644 (file)
index 0000000..b3556bb
--- /dev/null
@@ -0,0 +1,128 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2024 Deutsche Telekom. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.rest.notification;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.aai.AAISetup;
+import org.onap.aai.db.props.AAIProperties;
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.introspection.Introspector;
+import org.onap.aai.introspection.LoaderFactory;
+import org.onap.aai.prevalidation.ValidationService;
+import org.onap.aai.serialization.db.DBSerializer;
+import org.onap.aai.serialization.engines.query.QueryEngine;
+import org.onap.aai.setup.SchemaVersion;
+import org.onap.aai.setup.SchemaVersions;
+
+public class NotificationServiceTest extends AAISetup {
+
+  @Mock LoaderFactory loaderFactory;
+  @Mock SchemaVersions schemaVersions;
+  @Mock UEBNotification uebNotification;
+  @Mock ValidationService validationService;
+  @Mock DBSerializer dbSerializer;
+  @Mock QueryEngine queryEngine;
+  @Mock Introspector introspector;
+
+  boolean isDeltaEventsEnabled = false;
+  String basePath = "/aai";
+  NotificationService notificationService;
+
+  @Before
+  public void setup() throws UnsupportedEncodingException, AAIException {
+    MockitoAnnotations.openMocks(this);
+
+    when(dbSerializer.touchStandardVertexPropertiesForEdges()).thenReturn(Collections.emptySet());
+    when(dbSerializer.getLatestVersionView(any(),anyInt())).thenReturn(introspector);
+
+    notificationService = new NotificationService(validationService, loaderFactory, basePath, isDeltaEventsEnabled);
+    when(schemaVersions.getDefaultVersion()).thenReturn(new SchemaVersion("v29"));
+    doNothing().when(uebNotification).createNotificationEvent(any(),any(),any(),any(),any(),any(),any());
+  }
+
+  @Test
+  public void thatNotificationsCanBeCreatedWithoutEdges() throws AAIException, UnsupportedEncodingException {
+
+    Map<String, Object> properties = new HashMap<>();
+    properties.put(AAIProperties.NODE_TYPE, Collections.singletonList(new DetachedVertexProperty<String>("1", null, "pserver", new HashMap<>())));
+    properties.put(AAIProperties.AAI_URI, Collections.singletonList(new DetachedVertexProperty<String>("1", null, "/pservers/pserver/hostname", new HashMap<>())));
+    properties.put(AAIProperties.CREATED_TS, Collections.singletonList(new DetachedVertexProperty<String>("1", null, "12", new HashMap<>())));
+    properties.put(AAIProperties.LAST_MOD_TS, Collections.singletonList(new DetachedVertexProperty<String>("1", null, "34", new HashMap<>())));
+
+    Vertex vertex = new DetachedVertex("1","label", properties);
+    Set<Vertex> mainVertexesToNotifyOn = new HashSet<>();
+    mainVertexesToNotifyOn.add(vertex);
+    SchemaVersion schemaVersion = new SchemaVersion("v29");
+    when(dbSerializer.getUpdatedVertexes()).thenReturn(Collections.emptyMap());
+
+    notificationService.generateEvents(uebNotification, AAIProperties.MINIMUM_DEPTH, "sourceOfTruth", dbSerializer, "transactionId", queryEngine, mainVertexesToNotifyOn, schemaVersion);
+
+    verify(uebNotification, times(1)).createNotificationEvent(eq("transactionId"), eq("sourceOfTruth"), eq(Status.OK), eq(URI.create("/aai/v29/pservers/pserver/hostname")), eq(introspector), any(), eq("/aai"));
+    verify(validationService, times(1)).validate(anyList());
+    verify(uebNotification, times(1)).triggerEvents();
+  }
+
+  @Test
+  public void thatValidationCanBeDisabled() throws AAIException, UnsupportedEncodingException {
+
+    Map<String, Object> properties = new HashMap<>();
+    properties.put(AAIProperties.NODE_TYPE, Collections.singletonList(new DetachedVertexProperty<String>("1", null, "pserver", new HashMap<>())));
+    properties.put(AAIProperties.AAI_URI, Collections.singletonList(new DetachedVertexProperty<String>("1", null, "/pservers/pserver/hostname", new HashMap<>())));
+    properties.put(AAIProperties.CREATED_TS, Collections.singletonList(new DetachedVertexProperty<String>("1", null, "12", new HashMap<>())));
+    properties.put(AAIProperties.LAST_MOD_TS, Collections.singletonList(new DetachedVertexProperty<String>("1", null, "34", new HashMap<>())));
+
+    Vertex vertex = new DetachedVertex("1","label", properties);
+    Set<Vertex> mainVertexesToNotifyOn = new HashSet<>();
+    mainVertexesToNotifyOn.add(vertex);
+    SchemaVersion schemaVersion = new SchemaVersion("v29");
+    when(dbSerializer.getUpdatedVertexes()).thenReturn(Collections.emptyMap());
+
+    notificationService = new NotificationService(null, loaderFactory, basePath, isDeltaEventsEnabled);
+    notificationService.generateEvents(uebNotification, AAIProperties.MINIMUM_DEPTH, "sourceOfTruth", dbSerializer, "transactionId", queryEngine, mainVertexesToNotifyOn, schemaVersion);
+
+    verify(uebNotification, times(1)).triggerEvents();
+  }
+}
@@ -18,7 +18,7 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.aai.rest.ueb;
+package org.onap.aai.rest.notification;
 
 import static org.junit.Assert.assertEquals;
 
@@ -38,7 +38,6 @@ import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.Introspector;
 import org.onap.aai.introspection.Loader;
 import org.onap.aai.introspection.ModelType;
-import org.onap.aai.rest.notification.UEBNotification;
 import org.onap.aai.serialization.db.EdgeSerializer;
 import org.onap.aai.serialization.engines.QueryStyle;
 import org.onap.aai.setup.SchemaVersion;