Example #1
0
    def test_copy(self, computed_md5_mock):
        # the md5sum of the file being copied
        md5sum = 'd41d8cd98f00b204e9800998ecf84eee'
        # patch the compute_md5 function in vos to return the above value
        computed_md5_mock.return_value = md5sum

        #mock the props of the corresponding node
        props = MagicMock()
        props.get.return_value = md5sum
        #add props to the mocked node
        node = MagicMock(spec=Node)
        node.props = props

        # mock one by one the chain of connection.session.response.headers
        conn = MagicMock(spec=Connection)
        session = MagicMock()
        response = MagicMock()
        headers = MagicMock()
        headers.get.return_value = md5sum
        response.headers = headers
        session.get.return_value = response
        conn.session = session

        test_client = Client()
        # use the mocked connection instead of the real one
        test_client.conn = conn
        get_node_url_mock = Mock(
            return_value=['http://cadc.ca/test', 'http://cadc.ca/test'])
        test_client.get_node_url = get_node_url_mock

        #patch Client.get_node to return our mocked node
        get_node_mock = Mock(return_value=node)
        test_client.get_node = get_node_mock

        # time to test...
        vospaceLocation = 'vos://test/foo'
        osLocation = '/tmp/foo'
        # copy from vospace
        test_client.copy(vospaceLocation, osLocation)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None,
                                                  view='data')
        computed_md5_mock.assert_called_once_with(osLocation)
        get_node_mock.assert_called_once_with(vospaceLocation)

        # copy to vospace
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        test_client.copy(osLocation, vospaceLocation)
        get_node_url_mock.assert_called_once_with(vospaceLocation, 'PUT')
        computed_md5_mock.assert_called_once_with(osLocation)

        # error tests - md5sum mismatch
        computed_md5_mock.return_value = '000bad000'
        with self.assertRaises(OSError):
            test_client.copy(vospaceLocation, osLocation)

        with self.assertRaises(OSError):
            test_client.copy(osLocation, vospaceLocation)
Example #2
0
    def test_update(self):
        node = Node(ElementTree.fromstring(NODE_XML))

        resp = Mock()
        resp.headers.get = Mock(return_value="https://www.canfar.phys.uvic.ca/vospace")

        client = Client()
        client.get_node_url = Mock(return_value='https://www.canfar.phys.uvic.ca/vospace')
        client.conn = Mock()
        client.conn.session.post = Mock(return_value=resp)
        client.get_transfer_error = Mock()
        client.protocol = 'https'

        data = str(node)
        property_url = 'https://www.canfar.phys.uvic.ca/vospace/nodeprops'
        result = client.update(node, False)
        self.assertEqual(result, 0)
        client.conn.session.post.assert_called_with('https://www.canfar.phys.uvic.ca/vospace',
                                                      data=data, allow_redirects=False)

        call1 = call(property_url, allow_redirects=False, data=data,
                     headers={'Content-type': 'text/xml'})
        call2 = call('https://www.canfar.phys.uvic.ca/vospace/phase', allow_redirects=False, data="PHASE=RUN",
                     headers={'Content-type': "text/text"})
        calls = [call1, call2]

        client.conn = Mock()
        client.conn.session.post = Mock(return_value=resp)
        with patch('vos.vos.EndPoints.properties', property_url):
            result = client.update(node, True)
        self.assertEqual(result, 0)
        client.conn.session.post.assert_has_calls(calls)
Example #3
0
    def test_open(self):
        # Invalid mode raises OSError
        with self.assertRaises(OSError):
            client = Client()
            client.open('vos://foo/bar', mode=-1)

        with self.assertRaises(OSError):
            client = Client()
            client.get_node_url = Mock(return_value=None)
            client.open(None, url=None)

        conn = Connection()
        mock_vofile = VOFile(['http://foo.com/bar'], conn, 'GET')
        client = Client()
        client.get_node_url = Mock(return_value=mock_vofile)
        vofile = client.open(None, url=None)
        self.assertEquals(vofile.url.URLs[0], 'http://foo.com/bar')
Example #4
0
    def test_open(self):
        # Invalid mode raises OSError
        with self.assertRaises(OSError):
            client = Client()
            client.open('vos://foo/bar', mode=-10000)

        with self.assertRaises(OSError):
            client = Client()
            client.get_node_url = Mock(return_value=None)
            client.open(None, url=None)

        conn = Connection()
        mock_vofile = VOFile(['http://foo.com/bar'], conn, 'GET')
        client = Client()
        client.get_node_url = Mock(return_value=mock_vofile)
        vofile = client.open(None, url=None)
        self.assertEquals(vofile.url.URLs[0], 'http://foo.com/bar')
Example #5
0
    def test_open(self):
        # Invalid mode raises OSError
        with self.assertRaises(OSError):
            client = Client()
            client.open('vos://foo/bar', mode=-10000)

        with self.assertRaises(OSError):
            client = Client()
            client.get_node_url = Mock(return_value=None)
            client.open(None, url=None)

        conn = Connection(resource_id='ivo://cadc.nrc.ca/vault')
        mock_vofile = VOFile(['http://foo.com/bar'], conn, 'GET')
        client = Client()
        client.get_node_url = Mock(return_value=mock_vofile)
        endpoints_mock = Mock(conn=conn)
        client.get_endpoints = Mock(return_value=endpoints_mock)
        vofile = client.open(None, url=None)
        self.assertEqual(vofile.url.URLs[0], 'http://foo.com/bar')
Example #6
0
def test_get_node_url():
    client = Client()
    with pytest.raises(TypeError):
        client.get_node_url('vos://cadc.nrc.ca!vospace/auser', sort='Blah')
    with pytest.raises(ValueError):
        client.get_node_url('vos://cadc.nrc.ca!vospace/auser', order='Blah')

    response = Mock(spec=requests.Response)
    response.status_code = 303
    resource_id = 'ivo://cadc.nrc.ca/vospace'
    session_mock = Mock(spec=requests.Session, get=Mock(return_value=response))
    session_mock.headers = Mock()
    client._endpoints[resource_id] = vos.EndPoints(resource_id_uri=resource_id)
    client._endpoints[resource_id].conn.ws_client._session = session_mock
    equery = urlparse(client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
                      sort=vos.SortNodeProperty.DATE)).query
    assert(urllib.parse.unquote(equery) ==
           'sort={}'.format(vos.SortNodeProperty.DATE.value))

    equery = urlparse(client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
                      sort=vos.SortNodeProperty.LENGTH, order='asc')).query
    args = urllib.parse.unquote(equery).split('&')
    assert(2 == len(args))
    assert('order=asc' in args)
    assert('sort={}'.format(vos.SortNodeProperty.LENGTH.value) in args)

    equery = urlparse(client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
                                          order='desc')).query
    assert('order=desc' == urllib.parse.unquote(equery))

    # test header view
    transfer_url = 'https://some.location/some/headers'
    response.headers = {'Location': transfer_url}
    client._endpoints[resource_id].conn.ws_client._session.get = \
        Mock(return_value=response)
    assert transfer_url == \
        client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
                            view='header')[0]
    # get the argument lists for client.conn.session.get
    args, kwargs = client._endpoints[
        resource_id].conn.ws_client._session.get.call_args_list[0]
    # check head is amongst the other parameters
    assert kwargs['params']['view'] == 'header'
Example #7
0
    def test_add_props(self):
        old_node = Node(ElementTree.fromstring(NODE_XML))
        new_node = Node(ElementTree.fromstring(NODE_XML))
        new_node.props['quota'] = '1000'
        new_node.create = Mock(return_value=new_node.node)

        data = str(new_node)
        headers = {'size': str(len(data))}

        client = Client()
        client.get_node = Mock(return_value=old_node)
        client.get_node_url = Mock(return_value='http://foo.com/bar')
        client.conn = Mock()

        with patch('vos.Client', client) as mock:
            mock.add_props(new_node)
            mock.conn.session.post.assert_called_with('http://foo.com/bar',
                                                      headers=headers, data=data)
Example #8
0
    def test_update(self):
        node = Node(ElementTree.fromstring(NODE_XML))

        resp = Mock()
        resp.headers.get = Mock(
            return_value="https://www.canfar.phys.uvic.ca/vospace")

        conn = Mock(spec=vos.Connection)
        conn.session.post = Mock(return_value=resp)
        client = Client(conn=conn)
        client.get_node_url = Mock(
            return_value='https://www.canfar.phys.uvic.ca/vospace')
        client.get_transfer_error = Mock()
        client.protocol = 'https'

        data = str(node)
        property_url = 'https://www.canfar.phys.uvic.ca/vospace/nodeprops'
        endpoints_mock = Mock()
        endpoints_mock.properties = property_url
        client.get_endpoints = Mock(return_value=endpoints_mock)
        result = client.update(node, False)
        self.assertEqual(result, 0)
        client.conn.session.post.assert_called_with(
            'https://www.canfar.phys.uvic.ca/vospace',
            data=data,
            allow_redirects=False)

        call1 = call(property_url,
                     allow_redirects=False,
                     data=data,
                     headers={'Content-type': 'text/xml'})
        call2 = call(
            'https://www.canfar.phys.uvic.ca/vospace/phase',
            allow_redirects=False,
            data="PHASE=RUN",
            headers={'Content-type': 'application/x-www-form-urlencoded'})
        calls = [call1, call2]

        client.conn = Mock(spec=vos.Connection)
        client.conn.session.post = Mock(return_value=resp)
        result = client.update(node, True)
        self.assertEqual(result, 0)
        client.conn.session.post.assert_has_calls(calls)
Example #9
0
    def test_add_props(self):
        old_node = Node(ElementTree.fromstring(NODE_XML))
        new_node = Node(ElementTree.fromstring(NODE_XML))
        new_node.props['quota'] = '1000'
        new_node.create = Mock(return_value=new_node.node)

        data = str(new_node)
        headers = {'size': str(len(data))}

        client = Client()
        client.get_node = Mock(return_value=old_node)
        client.get_node_url = Mock(return_value='http://foo.com/bar')
        client.conn = Mock()

        with patch('vos.Client', client) as mock:
            mock.add_props(new_node)
            mock.conn.session.post.assert_called_with('http://foo.com/bar',
                                                      headers=headers,
                                                      data=data)
Example #10
0
    def test_add_props(self):
        old_node = Node(ElementTree.fromstring(NODE_XML))
        old_node.uri = 'vos:sometest'
        new_node = Node(ElementTree.fromstring(NODE_XML))
        new_node.props['quota'] = '1000'
        new_node.create = Mock(return_value=new_node.node)

        data = str(new_node)
        headers = {'size': str(len(data))}

        client = Client()
        client.get_node = Mock(return_value=old_node)
        client.get_node_url = Mock(return_value='http://foo.com/bar')
        mock_session = Mock()
        client.get_session = Mock(return_value=mock_session)

        client.add_props(new_node)
        client.get_session.assert_called_with('vos://foo.com!vospace/bar')
        mock_session.post.assert_called_with('http://foo.com/bar',
                                             headers=headers,
                                             data=data)
Example #11
0
def test_get_node_url():
    client = Client()
    with pytest.raises(TypeError):
        client.get_node_url('vos://cadc.nrc.ca!vospace/auser', sort='Blah')
    with pytest.raises(ValueError):
        client.get_node_url('vos://cadc.nrc.ca!vospace/auser', order='Blah')

    response = Mock(spec=requests.Response)
    response.status_code = 303
    client.conn.session.get = Mock(return_value=response)
    equery = urlparse(client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
                      sort=vos.SortNodeProperty.DATE)).query
    assert(urllib.parse.unquote(equery) ==
           'sort={}'.format(vos.SortNodeProperty.DATE.value))

    equery = urlparse(client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
                      sort=vos.SortNodeProperty.LENGTH, order='asc')).query
    args = urllib.parse.unquote(equery).split('&')
    assert(2 == len(args))
    assert('order=asc' in args)
    assert('sort={}'.format(vos.SortNodeProperty.LENGTH.value) in args)

    equery = urlparse(client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
                                          order='desc')).query
    assert('order=desc' == urllib.parse.unquote(equery))

    # test header view
    transfer_url = 'https://some.location/some/headers'
    client.conn.session.get = Mock(return_value=response)
    response.headers = {'Location': transfer_url}
    assert transfer_url == \
        client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
                            view='header')[0]
    # get the argument lists for client.conn.session.get
    call = client.conn.session.get.call_args_list[0]
    args, kwargs = call
    # check head is amongst the other parameters
    assert kwargs['params']['view'] == 'header'
Example #12
0
    def test_update(self):
        node = Node(ElementTree.fromstring(NODE_XML))

        resp = Mock()
        resp.headers.get = Mock(
            return_value="https://www.canfar.phys.uvic.ca/vospace")

        conn = Mock(spec=vos.Connection)
        conn.session.post = Mock(return_value=resp)
        client = Client(conn=conn)
        client.get_node_url = Mock(
            return_value='https://www.canfar.phys.uvic.ca/vospace')
        client.get_transfer_error = Mock()
        client.protocol = 'https'

        data = str(node)
        property_url = 'https://www.canfar.phys.uvic.ca/vospace/nodeprops'
        endpoints_mock = Mock()
        endpoints_mock.properties = property_url
        client.get_endpoints = Mock(return_value=endpoints_mock)
        result = client.update(node, False)
        self.assertEqual(result, 0)
        client.conn.session.post.assert_called_with(
            'https://www.canfar.phys.uvic.ca/vospace',
            data=data, allow_redirects=False)

        call1 = call(property_url, allow_redirects=False, data=data,
                     headers={'Content-type': 'text/xml'})
        call2 = call('https://www.canfar.phys.uvic.ca/vospace/phase',
                     allow_redirects=False, data="PHASE=RUN",
                     headers={'Content-type': "text/text"})
        calls = [call1, call2]

        client.conn = Mock(spec=vos.Connection)
        client.conn.session.post = Mock(return_value=resp)
        result = client.update(node, True)
        self.assertEqual(result, 0)
        client.conn.session.post.assert_has_calls(calls)
Example #13
0
    def test_copy(self, computed_md5_mock):
        # the md5sum of the file being copied
        md5sum = 'd41d8cd98f00b204e9800998ecf84eee'
        # patch the compute_md5 function in vos to return the above value
        computed_md5_mock.return_value = md5sum

        # mock the props of the corresponding node
        props = MagicMock()
        props.get.return_value = md5sum
        # add props to the mocked node
        node = MagicMock(spec=Node)
        node.props = props

        # mock one by one the chain of connection.session.response.headers
        conn = MagicMock(spec=Connection)
        session = MagicMock()
        response = MagicMock()
        headers = MagicMock()
        headers.get.return_value = md5sum
        response.headers = headers
        session.get.return_value = response
        conn.session = session

        test_client = Client()
        # use the mocked connection instead of the real one
        test_client.conn = conn
        get_node_url_mock = Mock(
            return_value=['http://cadc.ca/test', 'http://cadc.ca/test'])
        test_client.get_node_url = get_node_url_mock
        mock_update = Mock()
        test_client.update = mock_update

        # patch Client.get_node to return our mocked node
        get_node_mock = Mock(return_value=node)
        test_client.get_node = get_node_mock

        # time to test...
        vospaceLocation = 'vos://test/foo'
        osLocation = '/tmp/foo'
        if os.path.isfile(osLocation):
            os.remove(osLocation)
        # copy from vospace
        test_client.copy(vospaceLocation, osLocation)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None,
                                                  view='data')
        computed_md5_mock.assert_called_once_with(osLocation)
        assert get_node_mock.called

        # repeat - local file and vospace file are now the same -> only
        # get_node is called to get the md5 of remote file
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        get_node_mock.reset_mock()
        test_client.copy(vospaceLocation, osLocation)
        assert not get_node_url_mock.called
        computed_md5_mock.assert_called_once_with(osLocation)
        get_node_mock.assert_called_once_with(vospaceLocation)

        # change the content of local files to trigger a new copy
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        computed_md5_mock.side_effect = ['d002233', md5sum]
        get_node_mock.reset_mock()
        test_client.copy(vospaceLocation, osLocation)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None,
                                                  view='data')
        computed_md5_mock.assert_called_with(osLocation)
        get_node_mock.assert_called_once_with(vospaceLocation)

        # copy to vospace when md5 sums are the same -> only update occurs
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        computed_md5_mock.side_effect = None
        computed_md5_mock.return_value = md5sum
        test_client.copy(osLocation, vospaceLocation)
        mock_update.assert_called_once()
        assert not get_node_url_mock.called

        # make md5 different
        get_node_url_mock.reset_mock()
        get_node_url_mock.return_value =\
            ['http://cadc.ca/test', 'http://cadc.ca/test']
        computed_md5_mock.reset_mock()
        mock_update.reset_mock()
        props.get.side_effect = ['d00223344', md5sum]
        test_client.copy(osLocation, vospaceLocation)
        assert not mock_update.called
        get_node_url_mock.assert_called_once_with(vospaceLocation, 'PUT')
        computed_md5_mock.assert_called_once_with(osLocation)

        # copy 0 size file -> delete and create on client but no bytes
        # transferred
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        computed_md5_mock.return_value = vos.ZERO_MD5
        props.get.side_effect = [md5sum]
        mock_delete = Mock()
        mock_create = Mock()
        test_client.delete = mock_delete
        test_client.create = mock_create
        test_client.copy(osLocation, vospaceLocation)
        mock_create.assert_called_once_with(vospaceLocation)
        mock_delete.assert_called_once_with(vospaceLocation)
        assert not get_node_url_mock.called

        # copy new 0 size file -> reate on client but no bytes transferred
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        mock_delete.reset_mock()
        mock_create.reset_mock()
        computed_md5_mock.return_value = vos.ZERO_MD5
        props.get.side_effect = [None]
        mock_delete = Mock()
        mock_create = Mock()
        test_client.delete = mock_delete
        test_client.create = mock_create
        test_client.copy(osLocation, vospaceLocation)
        mock_create.assert_called_once_with(vospaceLocation)
        assert not mock_delete.called
        assert not get_node_url_mock.called

        # error tests - md5sum mismatch
        props.get.side_effect = [md5sum]
        computed_md5_mock.return_value = '000bad000'
        with self.assertRaises(OSError):
            test_client.copy(vospaceLocation, osLocation)

        with self.assertRaises(OSError):
            test_client.copy(osLocation, vospaceLocation)

        # requests just the headers
        props.get.side_effect = [None]
        get_node_url_mock = Mock(
            return_value=['http://cadc.ca/test', 'http://cadc.ca/test'])
        test_client.get_node_url = get_node_url_mock
        computed_md5_mock.reset_mock()
        computed_md5_mock.side_effect = ['d002233', md5sum]
        get_node_mock.reset_mock()
        test_client.copy(vospaceLocation, osLocation, head=True)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None,
                                                  view='header')
Example #14
0
    def test_copy(self, computed_md5_mock):
        file_content = 'File content'.encode('utf-8')

        # the md5sum of the file being copied
        transfer_md5 = hashlib.md5()
        transfer_md5.update(file_content)
        md5sum = transfer_md5.hexdigest()
        # patch the compute_md5 function in vos to return the above value
        computed_md5_mock.return_value = md5sum

        # mock the props of the corresponding node
        props = MagicMock()
        props.get.return_value = md5sum
        # add props to the mocked node
        node = MagicMock(spec=Node)
        node.props = {'MD5': md5sum, 'length': 12}

        # mock one by one the chain of connection.session.response.headers
        session = MagicMock()
        response = MagicMock()
        headers = MagicMock()
        headers.get.return_value = md5sum
        response.headers = headers
        session.get.return_value = response
        response.iter_content.return_value = BytesIO(file_content)

        test_client = Client()
        test_client.get_session = Mock(return_value=session)
        # use the mocked connection instead of the real one
        get_node_url_mock = Mock(
            return_value=['http://cadc.ca/test', 'http://cadc.ca/test'])
        test_client.get_node_url = get_node_url_mock
        mock_update = Mock()
        test_client.update = mock_update

        # patch Client.get_node to return our mocked node
        get_node_mock = Mock(return_value=node)
        test_client.get_node = get_node_mock

        # time to test...
        vospaceLocation = 'vos://test/foo'
        osLocation = '/tmp/foo'
        if os.path.isfile(osLocation):
            os.remove(osLocation)
        # copy from vospace
        test_client.copy(vospaceLocation, osLocation)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None, view='data')
        assert not computed_md5_mock.called,\
            'MD5 should be computed on the fly'
        assert get_node_mock.called

        # repeat - local file and vospace file are now the same -> only
        # get_node is called to get the md5 of remote file
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        get_node_mock.reset_mock()
        props.reset_mock()
        props.get.return_value = md5sum
        test_client.copy(vospaceLocation, osLocation)
        assert not get_node_url_mock.called
        computed_md5_mock.assert_called_once_with(osLocation)
        get_node_mock.assert_called_once_with(vospaceLocation, force=True)

        # change the content of local files to trigger a new copy
        get_node_url_mock.reset_mock()
        get_node_mock.reset_mock()

        computed_md5_mock.reset_mock()
        computed_md5_mock.return_value = 'd002233'
        response.iter_content.return_value = BytesIO(file_content)
        test_client.copy(vospaceLocation, osLocation)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None, view='data')
        computed_md5_mock.assert_called_with(osLocation)
        get_node_mock.assert_called_once_with(vospaceLocation, force=True)

        # change the content of local files to trigger a new copy
        get_node_url_mock.reset_mock()
        get_node_url_mock.return_value = \
            ['https://mysite.com/node/node123/cutout']
        computed_md5_mock.reset_mock()
        computed_md5_mock.return_value = 'd002233'
        # computed_md5_mock.side_effect = ['d002233', md5sum]
        get_node_mock.reset_mock()
        response.iter_content.return_value = BytesIO(file_content)
        session.get.return_value = response
        test_client.get_session = Mock(return_value=session)
        test_client.copy('{}{}'.format(vospaceLocation,
                                       '[1][10:60]'), osLocation)
        get_node_url_mock.assert_called_once_with(
            vospaceLocation, method='GET', cutout='[1][10:60]', view='cutout')

        # copy to vospace when md5 sums are the same -> only update occurs
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        computed_md5_mock.side_effect = None
        computed_md5_mock.return_value = md5sum
        test_client.copy(osLocation, vospaceLocation)
        mock_update.assert_called_once()
        assert not get_node_url_mock.called

        # make md5 different on destination
        get_node_url_mock.reset_mock()
        get_node_url_mock.return_value =\
            ['http://cadc.ca/test', 'http://cadc.ca/test']
        computed_md5_mock.reset_mock()
        mock_update.reset_mock()
        computed_md5_mock.return_value = md5sum
        to_update_node = MagicMock()
        to_update_node.props = {'MD5': 'abcde', 'length': 12}
        test_client.get_node = Mock(side_effect=[to_update_node, node])
        test_client.copy(osLocation, vospaceLocation)
        assert not mock_update.called
        get_node_url_mock.assert_called_once_with(vospaceLocation, 'PUT',
                                                  content_length=12,
                                                  md5_checksum='abcde')
        computed_md5_mock.assert_called_once_with(osLocation)

        # copy 0 size file -> delete and create node but no bytes
        # transferred
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        test_client.get_node = Mock(return_value=node)
        node.props['length'] = 0
        mock_delete = Mock()
        mock_create = Mock()
        test_client.delete = mock_delete
        test_client.create = mock_create
        with patch('vos.vos.os.stat', Mock()) as stat_mock:
            stat_mock.return_value = Mock(st_size=0)
            test_client.copy(osLocation, vospaceLocation)
        mock_create.assert_called_once_with(vospaceLocation)
        mock_delete.assert_called_once_with(vospaceLocation)
        assert not get_node_url_mock.called

        # copy new 0 size file -> create node but no bytes transferred
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        mock_delete.reset_mock()
        mock_create.reset_mock()
        test_client.get_node = Mock(side_effect=[Exception(), node])
        mock_delete = Mock()
        mock_create = Mock()
        test_client.delete = mock_delete
        test_client.create = mock_create
        with patch('vos.vos.os.stat', Mock()) as stat_mock:
            stat_mock.return_value = Mock(st_size=0)
            test_client.copy(osLocation, vospaceLocation)
        mock_create.assert_called_once_with(vospaceLocation)
        assert not mock_delete.called
        assert not get_node_url_mock.called

        # error tests - md5sum mismatch
        node.props['length'] = 12
        computed_md5_mock.return_value = '000bad000'
        test_client.get_node = Mock(return_value=node)
        with self.assertRaises(OSError):
            test_client.copy(vospaceLocation, osLocation)

        # existing file
        mock_delete.reset_mock()
        with self.assertRaises(OSError):
            with patch('vos.vos.os.stat', Mock()) as stat_mock:
                stat_mock.return_value = Mock(st_size=12)
                test_client.copy(osLocation, vospaceLocation)
        assert not mock_delete.called  # server takes care of cleanup

        # new file
        mock_delete.reset_mock()
        with self.assertRaises(OSError):
            with patch('vos.vos.os.stat', Mock()) as stat_mock:
                stat_mock.return_value = Mock(st_size=12)
                node.props['MD5'] = None
                test_client.copy(osLocation, vospaceLocation)
        assert mock_delete.called  # cleanup required

        # requests just the headers when md5 not provided in the header
        props.get.side_effect = [None]
        get_node_url_mock = Mock(
            return_value=['http://cadc.ca/test', 'http://cadc.ca/test'])
        test_client.get_node_url = get_node_url_mock
        get_node_mock.reset_mock()
        headers.get.return_value = None
        test_client.copy(vospaceLocation, osLocation, head=True)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None, view='header')

        # repeat headers request when md5 provided in the header
        props.get.side_effect = md5sum
        get_node_url_mock = Mock(
            return_value=['http://cadc.ca/test', 'http://cadc.ca/test'])
        test_client.get_node_url = get_node_url_mock
        get_node_mock.reset_mock()
        response.iter_content.return_value = BytesIO(file_content)
        headers.get.return_value = None
        test_client.copy(vospaceLocation, osLocation, head=True)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None, view='header')
Example #15
0
    def test_copy(self, computed_md5_mock):
        # the md5sum of the file being copied
        md5sum = 'd41d8cd98f00b204e9800998ecf84eee'
        # patch the compute_md5 function in vos to return the above value
        computed_md5_mock.return_value = md5sum

        # mock the props of the corresponding node
        props = MagicMock()
        props.get.return_value = md5sum
        # add props to the mocked node
        node = MagicMock(spec=Node)
        node.props = props

        # mock one by one the chain of connection.session.response.headers
        conn = MagicMock(spec=Connection)
        session = MagicMock()
        response = MagicMock()
        headers = MagicMock()
        headers.get.return_value = md5sum
        response.headers = headers
        session.get.return_value = response
        conn.session = session

        test_client = Client()
        # use the mocked connection instead of the real one
        test_client.conn = conn
        get_node_url_mock = Mock(
            return_value=['http://cadc.ca/test', 'http://cadc.ca/test'])
        test_client.get_node_url = get_node_url_mock
        mock_update = Mock()
        test_client.update = mock_update

        # patch Client.get_node to return our mocked node
        get_node_mock = Mock(return_value=node)
        test_client.get_node = get_node_mock

        # time to test...
        vospaceLocation = 'vos://test/foo'
        osLocation = '/tmp/foo'
        if os.path.isfile(osLocation):
            os.remove(osLocation)
        # copy from vospace
        test_client.copy(vospaceLocation, osLocation)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None, view='data')
        computed_md5_mock.assert_called_once_with(osLocation)
        assert get_node_mock.called

        # repeat - local file and vospace file are now the same -> only
        # get_node is called to get the md5 of remote file
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        get_node_mock.reset_mock()
        test_client.copy(vospaceLocation, osLocation)
        assert not get_node_url_mock.called
        computed_md5_mock.assert_called_once_with(osLocation)
        get_node_mock.assert_called_once_with(vospaceLocation)

        # change the content of local files to trigger a new copy
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        computed_md5_mock.side_effect = ['d002233', md5sum]
        get_node_mock.reset_mock()
        test_client.copy(vospaceLocation, osLocation)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None, view='data')
        computed_md5_mock.assert_called_with(osLocation)
        get_node_mock.assert_called_once_with(vospaceLocation)

        # copy to vospace when md5 sums are the same -> only update occurs
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        computed_md5_mock.side_effect = None
        computed_md5_mock.return_value = md5sum
        test_client.copy(osLocation, vospaceLocation)
        mock_update.assert_called_once()
        assert not get_node_url_mock.called

        # make md5 different
        get_node_url_mock.reset_mock()
        get_node_url_mock.return_value =\
            ['http://cadc.ca/test', 'http://cadc.ca/test']
        computed_md5_mock.reset_mock()
        mock_update.reset_mock()
        props.get.side_effect = ['d00223344', md5sum]
        test_client.copy(osLocation, vospaceLocation)
        assert not mock_update.called
        get_node_url_mock.assert_called_once_with(vospaceLocation, 'PUT')
        computed_md5_mock.assert_called_once_with(osLocation)

        # copy 0 size file -> delete and create on client but no bytes
        # transferred
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        computed_md5_mock.return_value = vos.ZERO_MD5
        props.get.side_effect = [md5sum]
        mock_delete = Mock()
        mock_create = Mock()
        test_client.delete = mock_delete
        test_client.create = mock_create
        test_client.copy(osLocation, vospaceLocation)
        mock_create.assert_called_once_with(vospaceLocation)
        mock_delete.assert_called_once_with(vospaceLocation)
        assert not get_node_url_mock.called

        # copy new 0 size file -> reate on client but no bytes transferred
        get_node_url_mock.reset_mock()
        computed_md5_mock.reset_mock()
        mock_delete.reset_mock()
        mock_create.reset_mock()
        computed_md5_mock.return_value = vos.ZERO_MD5
        props.get.side_effect = [None]
        mock_delete = Mock()
        mock_create = Mock()
        test_client.delete = mock_delete
        test_client.create = mock_create
        test_client.copy(osLocation, vospaceLocation)
        mock_create.assert_called_once_with(vospaceLocation)
        assert not mock_delete.called
        assert not get_node_url_mock.called

        # error tests - md5sum mismatch
        props.get.side_effect = [md5sum]
        computed_md5_mock.return_value = '000bad000'
        with self.assertRaises(OSError):
            test_client.copy(vospaceLocation, osLocation)

        with self.assertRaises(OSError):
            test_client.copy(osLocation, vospaceLocation)

        # requests just the headers
        props.get.side_effect = [None]
        get_node_url_mock = Mock(
            return_value=['http://cadc.ca/test', 'http://cadc.ca/test'])
        test_client.get_node_url = get_node_url_mock
        computed_md5_mock.reset_mock()
        computed_md5_mock.side_effect = ['d002233', md5sum]
        get_node_mock.reset_mock()
        test_client.copy(vospaceLocation, osLocation, head=True)
        get_node_url_mock.assert_called_once_with(vospaceLocation,
                                                  method='GET',
                                                  cutout=None, view='header')