diff --git a/provider/legal-azure/lombok.config b/provider/legal-azure/lombok.config new file mode 100644 index 0000000000000000000000000000000000000000..df71bb6a0fb87825475c75620c4d5e30088ade7f --- /dev/null +++ b/provider/legal-azure/lombok.config @@ -0,0 +1,2 @@ +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true diff --git a/provider/legal-azure/pom.xml b/provider/legal-azure/pom.xml index 64de20bf3a2a4c4e12698a246da713721a5463be..8487918e7fa5b02f908eab75f22bbce2d2544d13 100644 --- a/provider/legal-azure/pom.xml +++ b/provider/legal-azure/pom.xml @@ -116,6 +116,13 @@ <scope>compile</scope> </dependency> + <!-- test --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <!-- Override the spring-boot version of these dependencies to the ones required by the azure-core library. This needs to be done for each diff --git a/provider/legal-azure/src/main/java/org/opengroup/osdu/legal/azure/jobs/LegalTagPublisherImpl.java b/provider/legal-azure/src/main/java/org/opengroup/osdu/legal/azure/jobs/LegalTagPublisherImpl.java index 302e1ecc3a4c45003891e5c465f089432b16851f..72bb8c75770e93b6714306c4d559d35ff03554be 100644 --- a/provider/legal-azure/src/main/java/org/opengroup/osdu/legal/azure/jobs/LegalTagPublisherImpl.java +++ b/provider/legal-azure/src/main/java/org/opengroup/osdu/legal/azure/jobs/LegalTagPublisherImpl.java @@ -30,9 +30,9 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; - @Component public class LegalTagPublisherImpl implements ILegalTagPublisher { + @Inject private ITopicClientFactory topicClientFactory; diff --git a/provider/legal-azure/src/main/java/org/opengroup/osdu/legal/azure/security/WhoamiController.java b/provider/legal-azure/src/main/java/org/opengroup/osdu/legal/azure/security/WhoamiController.java index 5a538259e9942e1d74577b4023a7a637c3fb812c..d3eb5ba7c72021b9a25c30a39e5bc7fe8fcc0013 100644 --- a/provider/legal-azure/src/main/java/org/opengroup/osdu/legal/azure/security/WhoamiController.java +++ b/provider/legal-azure/src/main/java/org/opengroup/osdu/legal/azure/security/WhoamiController.java @@ -14,18 +14,32 @@ package org.opengroup.osdu.legal.azure.security; +import lombok.NoArgsConstructor; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; +@NoArgsConstructor @Controller public class WhoamiController { + + private SecurityContext securityContext; + + // Constructor made for unit testing + WhoamiController(SecurityContext securityContext) { + this.securityContext = securityContext; + } + @RequestMapping(value = "/whoami") @ResponseBody public String whoami() { - final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + // Added for unit testing + if (securityContext == null) + securityContext = SecurityContextHolder.getContext(); + final Authentication auth = securityContext.getAuthentication(); String userName = auth.getName(); String roles = String.valueOf(auth.getAuthorities()); diff --git a/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/jobs/LegalTagPublisherImplTest.java b/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/jobs/LegalTagPublisherImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5f71d4104d2c2b1818f805dc84ac0ed87f19f17b --- /dev/null +++ b/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/jobs/LegalTagPublisherImplTest.java @@ -0,0 +1,103 @@ +// Copyright © Microsoft Corporation +// +// 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. + +package org.opengroup.osdu.legal.azure.jobs; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.microsoft.azure.servicebus.Message; +import com.microsoft.azure.servicebus.MessageBody; +import com.microsoft.azure.servicebus.TopicClient; +import com.microsoft.azure.servicebus.primitives.ServiceBusException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.opengroup.osdu.azure.servicebus.ITopicClientFactory; +import org.opengroup.osdu.core.common.logging.JaxRsDpsLog; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.core.common.model.legal.StatusChangedTags; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class LegalTagPublisherImplTest { + + private static final String DATA_PARTITION_WITH_FALLBACK_ACCOUNT_ID = "data-partition-account-id"; + private static final String CORRELATION_ID = "correlation-id"; + private static final String USER_EMAIL = "user@email.com"; + private static final String PARTITION_ID = "partition-id"; + + @Mock + private JaxRsDpsLog logger; + + @Mock + private ITopicClientFactory topicClientFactory; + + @Mock + private TopicClient topicClient; + + @Mock + private DpsHeaders headers; + + @InjectMocks + private LegalTagPublisherImpl sut; + + @Before + public void init() throws ServiceBusException, InterruptedException { + doReturn(DATA_PARTITION_WITH_FALLBACK_ACCOUNT_ID).when(headers).getPartitionIdWithFallbackToAccountId(); + doReturn(CORRELATION_ID).when(headers).getCorrelationId(); + doReturn(USER_EMAIL).when(headers).getUserEmail(); + doReturn(PARTITION_ID).when(headers).getPartitionId(); + doReturn(topicClient).when(topicClientFactory).getClient(eq(PARTITION_ID), any()); + } + + @Test + public void testPublishLegalTag() throws Exception { + StatusChangedTags tags = new StatusChangedTags(); + sut.publish("project-id", headers, tags); + ArgumentCaptor<Message> msg = ArgumentCaptor.forClass(Message.class); + ArgumentCaptor<String> log = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<Exception> exception = ArgumentCaptor.forClass(Exception.class); + + verify(logger).info(log.capture()); + assertEquals("Storage publishes message " + CORRELATION_ID, log.getValue()); + + verify(topicClient).send(msg.capture()); + Map<String, Object> properties = msg.getValue().getProperties(); + + assertEquals(DATA_PARTITION_WITH_FALLBACK_ACCOUNT_ID, properties.get(DpsHeaders.ACCOUNT_ID)); + assertEquals(DATA_PARTITION_WITH_FALLBACK_ACCOUNT_ID, properties.get(DpsHeaders.DATA_PARTITION_ID)); + assertEquals(CORRELATION_ID, properties.get(DpsHeaders.CORRELATION_ID)); + assertEquals(USER_EMAIL, properties.get(DpsHeaders.USER_EMAIL)); + + MessageBody messageBody = msg.getValue().getMessageBody(); + Gson gson = new Gson(); + String messageKey = "message"; + String dataKey = "data"; + JsonObject jsonObjectMessage = gson.fromJson(new String(messageBody.getBinaryData().get(0)), JsonObject.class); + JsonObject jsonObject = (JsonObject) jsonObjectMessage.get(messageKey); + assertEquals(DATA_PARTITION_WITH_FALLBACK_ACCOUNT_ID, jsonObject.get(DpsHeaders.ACCOUNT_ID).getAsString()); + assertEquals(DATA_PARTITION_WITH_FALLBACK_ACCOUNT_ID, jsonObject.get(DpsHeaders.DATA_PARTITION_ID).getAsString()); + assertEquals(CORRELATION_ID, jsonObject.get(DpsHeaders.CORRELATION_ID).getAsString()); + assertEquals(USER_EMAIL, jsonObject.get(DpsHeaders.USER_EMAIL).getAsString()); + assertEquals(gson.toJsonTree(tags), jsonObject.get(dataKey)); + } +} diff --git a/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/security/WhoamiControllerTest.java b/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/security/WhoamiControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..756e375e2b3f34563afae4930817405f258e5072 --- /dev/null +++ b/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/security/WhoamiControllerTest.java @@ -0,0 +1,70 @@ +// Copyright © Microsoft Corporation +// +// 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. + +package org.opengroup.osdu.legal.azure.security; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; + +import java.util.*; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class WhoamiControllerTest { + + private static final String userName = "username"; + private static final String roles = "roles"; + private static final String details = "details"; + + @InjectMocks + private WhoamiController sut; + + private class DummyPrincipal { + @Override + public String toString() { + return details; + } + } + + public class DummyAuthority extends ArrayList { + @Override + public String toString() { + return roles; + } + } + + @Before + public void init() { + Authentication auth = mock(Authentication.class); + SecurityContext securityContext = mock(SecurityContext.class); + doReturn(auth).when(securityContext).getAuthentication(); + doReturn(userName).when(auth).getName(); + doReturn(new DummyAuthority()).when(auth).getAuthorities(); + doReturn(new DummyPrincipal()).when(auth).getPrincipal(); + sut = new WhoamiController(securityContext); + } + + @Test + public void testWhoamiResponse() { + String response = sut.whoami(); + assertEquals("user: " + userName + "<BR>roles: " + roles + "<BR>details: " + details + "<BR>", response); + } +} diff --git a/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/tags/dataaccess/LegalTagRepositoryImplTest.java b/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/tags/dataaccess/LegalTagRepositoryImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..70cfae98617e6ab1127bb8abdd190902a6bd864f --- /dev/null +++ b/provider/legal-azure/src/test/java/org/opengroup/osdu/legal/azure/tags/dataaccess/LegalTagRepositoryImplTest.java @@ -0,0 +1,220 @@ +// Copyright © Microsoft Corporation +// +// 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. + +package org.opengroup.osdu.legal.azure.tags.dataaccess; + +import com.azure.cosmos.FeedOptions; +import com.azure.cosmos.SqlParameter; +import com.azure.cosmos.SqlParameterList; +import com.azure.cosmos.SqlQuerySpec; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.opengroup.osdu.azure.cosmosdb.CosmosStore; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.core.common.model.legal.LegalTag; +import org.opengroup.osdu.core.common.model.legal.ListLegalTagArgs; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class LegalTagRepositoryImplTest { + + private static final String dataPartitionId = "data-partition-id"; + + @Mock + private CosmosStore cosmosStore; + + @Mock + private DpsHeaders headers; + + @InjectMocks + private LegalTagRepositoryImpl sut; + + @Before + public void init() { + lenient().doReturn(dataPartitionId).when(headers).getPartitionId(); + } + + @Test(expected = AppException.class) + public void testCreateLegalTagAlreadyExisting_throwsException() { + long id = 1234; + String strId = String.valueOf(id); + LegalTag legalTag = getLegalTagWithId(id); + Optional<LegalTag> optionalLegalTag = Optional.of(legalTag); + doReturn(optionalLegalTag).when(cosmosStore).findItem(eq(dataPartitionId), any(), any(), eq(strId), eq(strId), any()); + try { + sut.create(legalTag); + } catch (AppException e) { + int errorCode = 409; + String errorMessage = "LegalTag already exists"; + validateAppException(e, errorCode, errorMessage); + throw (e); + } + } + + @Test + public void testCreateLegalTag_upsertItem_executesCorrectQuery() { + long id = 1234; + String strId = String.valueOf(id); + LegalTag legalTag = getLegalTagWithId(id); + long obtainedId = sut.create(legalTag); + + ArgumentCaptor<LegalTagDoc> arg = ArgumentCaptor.forClass(LegalTagDoc.class); + verify(cosmosStore).upsertItem(eq(dataPartitionId), any(), any(), arg.capture()); + + assertEquals(arg.getValue().getId(), strId); + assertEquals(obtainedId, id); + } + + @Test + public void testGetLegalTagCollections_whenIdsIsNull() { + long[] ids = null; + List<LegalTag> output = (List<LegalTag>) sut.get(ids); + assertEquals(output.size(), 0); + } + + @Test + public void testGetLegalTagCollections_whenIdsIsNotNull() { + long[] ids = {1234, 9876}; + String[] strIds = Arrays.stream(ids).mapToObj(String::valueOf).toArray(String[]::new); + Optional[] legalTagDocs = new Optional[2]; + legalTagDocs[0] = Optional.of(new LegalTagDoc(strIds[0], getLegalTagWithId(ids[0]))); + legalTagDocs[1] = Optional.of(new LegalTagDoc(strIds[0], getLegalTagWithId(ids[1]))); + + doReturn(legalTagDocs[0]).when(cosmosStore).findItem(eq(dataPartitionId), any(), any(), eq(strIds[0]), eq(strIds[0]), any()); + doReturn(legalTagDocs[1]).when(cosmosStore).findItem(eq(dataPartitionId), any(), any(), eq(strIds[1]), eq(strIds[1]), any()); + + List<LegalTag> output = (List<LegalTag>) sut.get(ids); + assertEquals(output.size(), 2); + assertEquals(output.get(0).getId().longValue(), ids[0]); + assertEquals(output.get(1).getId().longValue(), ids[1]); + } + + @Test + public void testDeleteLegalTag_whenLegalTagDoesNotExist() { + long id = 1234; + LegalTag legalTag = getLegalTagWithId(id); + boolean status = sut.delete(legalTag); + assertEquals(status, false); + } + + @Test + public void testDeleteLegalTag_whenLegalTagExists() { + long id = 1234; + String strId = String.valueOf(id); + LegalTag legalTag = getLegalTagWithId(id); + Optional<LegalTag> optionalLegalTag = Optional.of(legalTag); + doReturn(optionalLegalTag).when(cosmosStore).findItem(eq(dataPartitionId), any(), any(), eq(strId), eq(strId), any()); + boolean status = sut.delete(legalTag); + + ArgumentCaptor<String> arg1 = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<String> arg2 = ArgumentCaptor.forClass(String.class); + verify(cosmosStore).deleteItem(eq(dataPartitionId), any(), any(), arg1.capture(), arg2.capture()); + + assertEquals(status, true); + assertEquals(arg1.getValue(), strId); + assertEquals(arg2.getValue(), strId); + } + + @Test + public void testUpdateLegalTag_whenProvidedLegalTagIsNull() { + LegalTag legalTag = sut.update(null); + assertNull(legalTag); + } + + @Test(expected = AppException.class) + public void testUpdateLegalTag_whenValidItemDoesNotExist_throwsException() { + long id = 1234; + LegalTag legalTag = getLegalTagWithId(id); + try { + sut.update(legalTag); + } catch (AppException e) { + int errorCode = 404; + String errorMessage = "Cannot update a LegalTag that does not exist"; + validateAppException(e, errorCode, errorMessage); + throw (e); + } + } + + @Test + public void testUpdateLegalTag_whenValidItemExists() { + long id = 1234; + String strId = String.valueOf(id); + LegalTag legalTag = getLegalTagWithId(id); + Optional<LegalTag> optionalLegalTag = Optional.of(legalTag); + doReturn(optionalLegalTag).when(cosmosStore).findItem(eq(dataPartitionId), any(), any(), eq(strId), eq(strId), any()); + LegalTag obtainedLegalTag = sut.update(legalTag); + + ArgumentCaptor<LegalTagDoc> arg = ArgumentCaptor.forClass(LegalTagDoc.class); + verify(cosmosStore).upsertItem(eq(dataPartitionId), any(), any(), arg.capture()); + + assertEquals(arg.getValue().getId(), strId); + assertEquals(obtainedLegalTag.getId().longValue(), id); + } + + @Test + public void testListLegalTags_queryItems_executesCorrectQuery() { + long[] ids = {1234, 9876}; + String[] strIds = Arrays.stream(ids).mapToObj(String::valueOf).toArray(String[]::new); + List<LegalTagDoc> legalTagDocs = Arrays.asList(new LegalTagDoc(strIds[0], getLegalTagWithId(ids[0])), new LegalTagDoc(strIds[1], getLegalTagWithId(ids[1]))); + + ArgumentCaptor<SqlQuerySpec> query = ArgumentCaptor.forClass(SqlQuerySpec.class); + ArgumentCaptor<FeedOptions> feedOptions = ArgumentCaptor.forClass(FeedOptions.class); + + doReturn(legalTagDocs).when(cosmosStore).queryItems(eq(dataPartitionId), any(), any(), any(), any(), any()); + ListLegalTagArgs legalTagArgs = new ListLegalTagArgs(); + legalTagArgs.setIsValid(true); + List<LegalTag> output = (List<LegalTag>) sut.list(legalTagArgs); + + assertEquals(output.size(), 2); + assertEquals(output.get(0).getId().longValue(), ids[0]); + assertEquals(output.get(1).getId().longValue(), ids[1]); + + verify(cosmosStore).queryItems(eq(dataPartitionId), any(), any(), query.capture(), feedOptions.capture(), any()); + assertEquals(query.getValue().getQueryText(), "SELECT * FROM c WHERE c.legalTag.isValid = @isValid"); + assertTrue(feedOptions.getValue().getEnableCrossPartitionQuery()); + + SqlParameterList parameters = query.getValue().getParameters(); + SqlParameter isValid = parameters.get(0); + + assertEquals(isValid.getName(), "@isValid"); + assertEquals(isValid.getValue(Boolean.class), true); + } + + private LegalTag getLegalTagWithId(long id) { + LegalTag legalTag = new LegalTag(); + legalTag.setId(id); + return legalTag; + } + + private void validateAppException(AppException e, int errorCode, String errorMessage) { + assertEquals(errorCode, e.getError().getCode()); + assertThat(e.getError().getMessage(), containsString(errorMessage)); + } +} diff --git a/provider/legal-azure/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/provider/legal-azure/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000000000000000000000000000000000..1f0955d450f0dc49ca715b1a0a88a5aa746ee11e --- /dev/null +++ b/provider/legal-azure/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline