/* * This file is part of vospace-rest * Copyright (C) 2021 Istituto Nazionale di Astrofisica * SPDX-License-Identifier: GPL-3.0-or-later */ package it.inaf.oats.vospace; import com.fasterxml.jackson.databind.ObjectMapper; import it.inaf.ia2.aa.data.User; import it.inaf.oats.vospace.FileServiceClient.ArchiveRequest; import it.inaf.oats.vospace.datamodel.Views; import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.parent.exchange.ArchiveEntryDescriptor; import it.inaf.oats.vospace.persistence.NodeDAO; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; import java.util.List; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.vospace.v2.ContainerNode; import net.ivoa.xml.vospace.v2.DataNode; import net.ivoa.xml.vospace.v2.Param; import net.ivoa.xml.vospace.v2.Transfer; import net.ivoa.xml.vospace.v2.View; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpResponse; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestTemplate; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) public class FileServiceClientTest { private static final ObjectMapper MAPPER = new ObjectMapper(); @Mock private RestTemplate restTemplate; @Mock private HttpServletRequest request; @Mock private LinkService linkService; @Mock private NodeDAO nodeDAO; @InjectMocks private FileServiceClient fileServiceClient; @BeforeEach public void setUp() { ReflectionTestUtils.setField(fileServiceClient, "authority", "example.com!vospace"); ReflectionTestUtils.setField(fileServiceClient, "fileServiceUrl", "http://file-service"); } @Test public void testTarArchiveJob() { testStartArchiveJob(Views.TAR_VIEW_URI); } @Test public void testZipArchiveJob() { testStartArchiveJob(Views.ZIP_VIEW_URI); } @Test public void testInvalidArchiveJob() { try { testStartArchiveJob("foo"); fail("IllegalArgumentException was expected"); } catch (IllegalArgumentException ex) { } } @Test public void testArchiveNoInclude() { Transfer transfer = new Transfer(); transfer.setDirection("pullFromVoSpace"); transfer.setTarget("vos://example.com!vospace/mydir(%20+%20)x"); View view = new View(); view.setUri(Views.ZIP_VIEW_URI); transfer.setView(view); ContainerNode node = Mockito.mock(ContainerNode.class); when(node.getNodes()).thenReturn(List.of()); when(node.getUri()).thenReturn("vos://example.com!vospace/mydir(%20+%20)x"); when(nodeDAO.listNode(eq("/mydir( + )x"))).thenReturn(Optional.of(node)); when(linkService.followLinksForArchiveService(any())) .thenReturn(List.of(new ArchiveEntryDescriptor("/mydir( + )x"))); ArchiveRequest archiveRequest = testStartArchiveJob(transfer); assertEquals(1, archiveRequest.getEntryDescriptors().size()); assertEquals("/mydir( + )x", archiveRequest.getEntryDescriptors().get(0).getTargetNodeVosPath()); } @Test public void testInvalidViewParam() { Transfer transfer = new Transfer(); transfer.setDirection("pullFromVoSpace"); transfer.setTarget("vos://example.com!vospace/parent_dir"); View view = new View(); view.setUri(Views.TAR_VIEW_URI); transfer.setView(view); Param param1 = new Param(); param1.setUri("invalid"); param1.setValue("file1"); view.getParam().add(param1); assertThrows(InvalidArgumentException.class, () -> testStartArchiveJob(transfer)); } @Test public void testInvalidViewParamPath() { Transfer transfer = new Transfer(); transfer.setDirection("pullFromVoSpace"); transfer.setTarget("vos://example.com!vospace/parent_dir"); View view = new View(); view.setUri(Views.TAR_VIEW_URI); transfer.setView(view); Param param1 = new Param(); param1.setUri(Views.TAR_VIEW_URI + "/include"); param1.setValue("../file1"); view.getParam().add(param1); assertThrows(InvalidArgumentException.class, () -> testStartArchiveJob(transfer)); } private void testStartArchiveJob(String viewUri) { Transfer transfer = new Transfer(); transfer.setDirection("pullFromVoSpace"); transfer.setTarget("vos://example.com!vospace/parent_dir"); View view = new View(); view.setUri(viewUri); transfer.setView(view); Param param1 = new Param(); param1.setUri(viewUri + "/include"); param1.setValue("file1"); view.getParam().add(param1); Param param2 = new Param(); param2.setUri(viewUri + "/include"); param2.setValue("file2"); view.getParam().add(param2); DataNode node1 = Mockito.mock(DataNode.class); when(node1.getUri()).thenReturn("vos://example.com!vospace/parent_dir/file1"); when(nodeDAO.listNode(eq("/parent_dir/file1"))).thenReturn(Optional.of(node1)); DataNode node2 = Mockito.mock(DataNode.class); when(node2.getUri()).thenReturn("vos://example.com!vospace/parent_dir/file2"); when(nodeDAO.listNode(eq("/parent_dir/file2"))).thenReturn(Optional.of(node2)); when(linkService.followLinksForArchiveService(any())).thenReturn( List.of(new ArchiveEntryDescriptor("/parent_dir/file1"), new ArchiveEntryDescriptor("/parent_dir/file2") )); ArchiveRequest archiveRequest = testStartArchiveJob(transfer); assertEquals(2, archiveRequest.getEntryDescriptors().size()); assertEquals("/parent_dir/file1", archiveRequest.getEntryDescriptors().get(0).getTargetNodeVosPath()); assertEquals("/parent_dir/file2", archiveRequest.getEntryDescriptors().get(1).getTargetNodeVosPath()); } private ArchiveRequest testStartArchiveJob(Transfer transfer) { User user = mock(User.class); when(user.getAccessToken()).thenReturn(""); when(request.getUserPrincipal()).thenReturn(user); ByteArrayOutputStream baos = new ByteArrayOutputStream(); doAnswer(invocation -> { RequestCallback requestCallback = invocation.getArgument(2); ClientHttpRequest mockedRequest = mock(ClientHttpRequest.class); HttpHeaders mockedRequestHeaders = mock(HttpHeaders.class); when(mockedRequest.getBody()).thenReturn(baos); when(mockedRequest.getHeaders()).thenReturn(mockedRequestHeaders); requestCallback.doWithRequest(mockedRequest); ResponseExtractor responseExtractor = invocation.getArgument(3); ClientHttpResponse mockedResponse = mock(ClientHttpResponse.class); HttpHeaders mockedResponseHeaders = mock(HttpHeaders.class); when(mockedResponseHeaders.getLocation()).thenReturn(new URI("http://file-service/archive/result")); when(mockedResponse.getHeaders()).thenReturn(mockedResponseHeaders); responseExtractor.extractData(mockedResponse); return mockedResponseHeaders.getLocation().toString(); }).when(restTemplate).execute(eq("http://file-service/archive"), eq(HttpMethod.POST), any(RequestCallback.class), any(ResponseExtractor.class), any(Object[].class)); String redirect = fileServiceClient.startArchiveJob(transfer, "job123"); assertEquals("http://file-service/archive/result", redirect); try { return MAPPER.readValue(baos.toByteArray(), ArchiveRequest.class); } catch (IOException ex) { throw new UncheckedIOException(ex); } } }