Skip to content
CreateNodeControllerTest.java 19.1 KiB
Newer Older
Sonia Zorba's avatar
Sonia Zorba committed
/*
 * This file is part of vospace-rest
 * Copyright (C) 2021 Istituto Nazionale di Astrofisica
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
Sara Bertocco's avatar
Sara Bertocco committed
package it.inaf.oats.vospace;

import it.inaf.oats.vospace.persistence.NodeDAO;
Sara Bertocco's avatar
Sara Bertocco committed
import java.io.InputStream;
import java.net.URI;
Sara Bertocco's avatar
Sara Bertocco committed
import java.nio.charset.StandardCharsets;
import net.ivoa.xml.vospace.v2.Property;
import it.inaf.oats.vospace.datamodel.NodeProperties;
Sara Bertocco's avatar
Sara Bertocco committed
import net.ivoa.xml.vospace.v2.UnstructuredDataNode;
import org.junit.jupiter.api.Test;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
Sara Bertocco's avatar
Sara Bertocco committed
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Sara Bertocco's avatar
Sara Bertocco committed
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
Sara Bertocco's avatar
Sara Bertocco committed
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.mockito.ArgumentMatchers.any;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import net.ivoa.xml.vospace.v2.ContainerNode;
import net.ivoa.xml.vospace.v2.LinkNode;
import java.util.List;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.mockito.ArgumentCaptor;
import static org.mockito.Mockito.times;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@ContextConfiguration(classes = {TokenFilterConfig.class})
@TestPropertySource(properties = "spring.main.allow-bean-definition-overriding=true")
@AutoConfigureMockMvc
Sara Bertocco's avatar
Sara Bertocco committed
public class CreateNodeControllerTest {
Sara Bertocco's avatar
Sara Bertocco committed
    private CreateNodeController controller;
Sara Bertocco's avatar
Sara Bertocco committed
    private MockMvc mockMvc;
    private ContainerNode getContainerParentNode(String path) {
        ContainerNode parentNode = new ContainerNode();
        // Set parent node address at /
        parentNode.setUri("vos://example.com!vospace" + path);
        // Set groupwrite property
        Property groups = new Property();
        groups.setUri("ivo://ivoa.net/vospace/core#groupwrite");
        parentNode.setProperties(List.of(groups));
        return parentNode;
    }
    private ContainerNode getContainerParentNodeWithCreator(String path) {
        ContainerNode parentNode = new ContainerNode();
        // Set parent node address at /
        parentNode.setUri("vos://example.com!vospace" + path);
        Property creator = new Property();
        creator.setUri("ivo://ivoa.net/vospace/core#creator");
        creator.setValue("user2");
        parentNode.setProperties(List.of(creator));
    private LinkNode getLinkParentNode(String path) {
        LinkNode parentNode = new LinkNode();
        // Set parent node address at /
        parentNode.setUri("vos://example.com!vospace" + path);
        // Set groupwrite property
        Property groups = new Property();
        groups.setUri("ivo://ivoa.net/vospace/core#groupwrite");
        parentNode.setProperties(List.of(groups));
        return parentNode;
    }
Sara Bertocco's avatar
Sara Bertocco committed
    @Test
    public void testFromJsonToXml() throws Exception {
        String requestBody
                = getResourceFileContent("create-unstructured-data-node.json");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));
                .header("Authorization", "Bearer user2_token")
Sara Bertocco's avatar
Sara Bertocco committed
                .content(requestBody)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().isOk());
Sara Bertocco's avatar
Sara Bertocco committed
        verifyArguments();
    }
Sara Bertocco's avatar
Sara Bertocco committed
    @Test
    public void testFromXmlToJson() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));
                .header("Authorization", "Bearer user2_token")
Sara Bertocco's avatar
Sara Bertocco committed
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk());
Sara Bertocco's avatar
Sara Bertocco committed
        verifyArguments();
        verify(nodeDao, times(1)).createNode(any());
Sara Bertocco's avatar
Sara Bertocco committed
    }
Sara Bertocco's avatar
Sara Bertocco committed
    @Test
    public void testFromXmlToXml() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));
                .header("Authorization", "Bearer user2_token")
Sara Bertocco's avatar
Sara Bertocco committed
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().isOk());
Sara Bertocco's avatar
Sara Bertocco committed
        verifyArguments();
        verify(nodeDao, times(1)).createNode(any());
Sara Bertocco's avatar
Sara Bertocco committed
    }
Sara Bertocco's avatar
Sara Bertocco committed
    @Test
    public void testFromJsonToJson() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.json");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));
                .header("Authorization", "Bearer user2_token")
Sara Bertocco's avatar
Sara Bertocco committed
                .content(requestBody)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk());
Sara Bertocco's avatar
Sara Bertocco committed
        verifyArguments();
        verify(nodeDao, times(1)).createNode(any());
Sara Bertocco's avatar
Sara Bertocco committed
    }
    @Test
    public void testURIConsistence() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is4xxClientError());

    }

    @Test
    public void testCreateInternalLinkNode() throws Exception {
        String requestBody = getResourceFileContent("create-internal-link-node.xml");

        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));

        mockMvc.perform(put("/nodes/myInternalLink")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().isOk());

        verifyLinkArguments("vos://example.com!vospace/myDummyDataNode1");

    }

    @Test
    public void testCreateExternalLinkNode() throws Exception {
        String requestBody = getResourceFileContent("create-external-link-node.xml");

        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));

        mockMvc.perform(put("/nodes/myExternalLink")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is4xxClientError());

        verifyLinkArguments("vos://external.com!vospace/myDummyDataNode1");

    }

    @Test
    public void testCreateLinkNodeNoTarget() throws Exception {
        String requestBody = getResourceFileContent("create-link-node-notarget.xml");

        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));

        mockMvc.perform(put("/nodes/myNoTargetLink")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is4xxClientError());

        verifyLinkArguments(null);

    
    @Test
    public void testCreateLinkNodeExternalHttp() throws Exception {
        String requestBody = getResourceFileContent("create-link-node-external-http.xml");

        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));

        mockMvc.perform(put("/nodes/myExternalHttpLink")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().isOk());

        verifyLinkArguments("http://www.external.com/files/file.txt");

    }
    @Test
    public void testNodeAlreadyExisting() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNode("/")));
        when(nodeDao.listNode(eq("/mydata1")))
                .thenReturn(Optional.of(getContainerParentNode("/mydata1")));
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is4xxClientError());
    @Test
    public void testContainerNotFound() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.ofNullable(null));
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is4xxClientError());
    @Test
    public void testLinkNodeFound() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getLinkParentNode("/")));
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is4xxClientError());
    @Test
    public void testPermissionDenied() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
                .thenReturn(Optional.of(getContainerParentNode("/")));
                .header("Authorization", "Bearer user1_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is4xxClientError());
    @Test
    public void testWriteWithOnlyOwnership() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(getContainerParentNodeWithCreator("/")));
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is2xxSuccessful());
        verify(nodeDao, times(1)).createNode(any());
    @Test
    public void testWriteOwnerAbsent() throws Exception {
        String requestBody
                = getResourceFileContent("create-unstructured-data-node.xml");
                .thenReturn(Optional.of(getContainerParentNodeWithCreator("/")));

        // no node creator specified in xml file
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is2xxSuccessful());
        // assert creator properties now matches user2
        verify(nodeDao, times(1)).createNode(argThat(node -> {
            UnstructuredDataNode udn = (UnstructuredDataNode) node;
            String creator = NodeProperties.getNodePropertyByURI(
                    udn, NodeProperties.CREATOR_URI);
            return (creator != null && creator.equals("user2"));
    @Test
    public void testGroupPropertiesAbsent() throws Exception {
        String requestBody
                = getResourceFileContent("create-unstructured-data-node.xml");
        ContainerNode cdn = getContainerParentNode("/");
        when(nodeDao.listNode(eq("/")))
                .thenReturn(Optional.of(cdn));

        // no node creator specified in xml file
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is2xxSuccessful());

        // assert creator properties now matches user2
        verify(nodeDao, times(1)).createNode(argThat(node -> {
            UnstructuredDataNode udn = (UnstructuredDataNode) node;
            String groupRead = NodeProperties.getNodePropertyByURI(
                    udn, NodeProperties.GROUP_READ_URI);
            String groupWrite = NodeProperties.getNodePropertyByURI(
                    udn, NodeProperties.GROUP_WRITE_URI);
            return (groupRead == null && groupWrite.equals("group1 group2"));
    @Test
    public void testWriteOwnerMismatch() throws Exception {
        String requestBody
                = getResourceFileContent("create-unstructured-data-node-user1.xml");
                .thenReturn(Optional.of(getContainerParentNodeWithCreator("/")));

        // no node creator specified in xml file
        mockMvc.perform(put("/nodes/mydata1")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().is4xxClientError());
        verify(nodeDao, times(0)).createNode(any());
    public void testSubPath() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml")
                .replace("/mydata1", "/mydata1/anothernode");
        mockMvc.perform(put("/nodes/mydata1/anothernode")
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().isNotFound());

        // Using ArgumentCaptor for verifying multiple method invocations
        ArgumentCaptor<String> argCaptor = ArgumentCaptor.forClass(String.class);
        verify(nodeDao, times(2)).listNode(argCaptor.capture());
        assertEquals("/mydata1/anothernode", argCaptor.getAllValues().get(0));
        assertEquals("/mydata1", argCaptor.getAllValues().get(1));
    }
    @Test
    public void testIllegalChars() throws Exception {
        String requestBody = getResourceFileContent("create-unstructured-data-node.xml")
                .replace("/mydata1", "/mydata1/?anothernode");
        String message = mockMvc.perform(put(new URI("/nodes/mydata1/%3Fanothernode"))
                .header("Authorization", "Bearer user2_token")
                .content(requestBody)
                .contentType(MediaType.APPLICATION_XML)
                .accept(MediaType.APPLICATION_XML))
                .andDo(print())
                .andExpect(status().isBadRequest())
                .andReturn().getResolvedException().getMessage();
        assertTrue(message.contains("contains an illegal character"));
    }
Sara Bertocco's avatar
Sara Bertocco committed
    private void verifyArguments() {
Sara Bertocco's avatar
Sara Bertocco committed
                argThat(node -> {
                    UnstructuredDataNode udn = (UnstructuredDataNode) node;
                    Property property = udn.getProperties().get(0);
Sara Bertocco's avatar
Sara Bertocco committed
                    return "vos:UnstructuredDataNode".equals(udn.getType())
                            && "test value".equals(property.getValue())
                            && "ivo://ivoa.net/vospace/core#description".equals(property.getUri());
Sara Bertocco's avatar
Sara Bertocco committed
    }

    private void verifyLinkArguments(String target) {
        verify(controller).createNode(
                argThat(node -> {
                    LinkNode linkNode = (LinkNode) node;
                    Property property = linkNode.getProperties().get(0);
                    return "vos:LinkNode".equals(linkNode.getType())
                            && "test value".equals(property.getValue())
                            && "ivo://ivoa.net/vospace/core#description".equals(property.getUri())
                            && Objects.equals(target, linkNode.getTarget());
                }), any());
    }

Sara Bertocco's avatar
Sara Bertocco committed
    protected static String getResourceFileContent(String fileName) throws Exception {
        try (InputStream in = CreateNodeControllerTest.class.getClassLoader().getResourceAsStream(fileName)) {
Sara Bertocco's avatar
Sara Bertocco committed
            return new String(in.readAllBytes(), StandardCharsets.UTF_8);
        }
    }
}