# Copyright (c) 2015 EMC Corporation.
# 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.

import ddt
from oslo_serialization import jsonutils as json
import requests
import requests_mock
import six

from manila import exception
from manila.share.drivers.dell_emc.plugins.isilon import isilon_api
from manila import test


@ddt.ddt
class IsilonApiTest(test.TestCase):

    def setUp(self):
        super(IsilonApiTest, self).setUp()

        self._mock_url = 'https://localhost:8080'
        _mock_auth = ('admin', 'admin')
        self.isilon_api = isilon_api.IsilonApi(
            self._mock_url, _mock_auth
        )

    @ddt.data(False, True)
    def test_create_directory(self, is_recursive):
        with requests_mock.Mocker() as m:
            path = '/ifs/test'
            self.assertEqual(0, len(m.request_history))
            self._add_create_directory_response(m, path, is_recursive)

            r = self.isilon_api.create_directory(path,
                                                 recursive=is_recursive)

            self.assertTrue(r)
            self.assertEqual(1, len(m.request_history))
            request = m.request_history[0]
            self._verify_dir_creation_request(request, path, is_recursive)

    @requests_mock.mock()
    def test_clone_snapshot(self, m):
        snapshot_name = 'snapshot01'
        fq_target_dir = '/ifs/admin/target'

        self.assertEqual(0, len(m.request_history))
        self._add_create_directory_response(m, fq_target_dir, False)
        snapshots_json = (
            '{"snapshots": '
            '[{"name": "snapshot01", "path": "/ifs/admin/source"}]'
            '}'
        )
        self._add_get_snapshot_response(m, snapshot_name, snapshots_json)

        # In order to test cloning a snapshot, we build out a mock
        # source directory tree. After the method under test is called we
        # will verify the necessary calls are made to clone a snapshot.
        source_dir_listing_json = (
            '{"children": ['
            '{"name": "dir1", "type": "container"},'
            '{"name": "dir2", "type": "container"},'
            '{"name": "file1", "type": "object"},'
            '{"name": "file2", "type": "object"}'
            ']}'
        )
        self._add_get_directory_listing_response(
            m, '/ifs/.snapshot/{0}/admin/source'.format(snapshot_name),
            source_dir_listing_json)

        # Add request responses for creating directories and cloning files
        # to the destination tree
        self._add_file_clone_response(m, '/ifs/admin/target/file1',
                                      snapshot_name)
        self._add_file_clone_response(m, '/ifs/admin/target/file2',
                                      snapshot_name)
        self._add_create_directory_response(m, fq_target_dir + '/dir1', False)
        self._add_get_directory_listing_response(
            m, '/ifs/.snapshot/{0}/admin/source/dir1'.format(snapshot_name),
            '{"children": ['
            '{"name": "file11", "type": "object"}, '
            '{"name": "file12", "type": "object"}'
            ']}')
        self._add_file_clone_response(m, '/ifs/admin/target/dir1/file11',
                                      snapshot_name)
        self._add_file_clone_response(m, '/ifs/admin/target/dir1/file12',
                                      snapshot_name)
        self._add_create_directory_response(m, fq_target_dir + '/dir2', False)
        self._add_get_directory_listing_response(
            m, '/ifs/.snapshot/{0}/admin/source/dir2'.format(snapshot_name),
            '{"children": ['
            '{"name": "file21", "type": "object"}, '
            '{"name": "file22", "type": "object"}'
            ']}')
        self._add_file_clone_response(m, '/ifs/admin/target/dir2/file21',
                                      snapshot_name)
        self._add_file_clone_response(m, '/ifs/admin/target/dir2/file22',
                                      snapshot_name)

        # Call method under test
        self.isilon_api.clone_snapshot(snapshot_name, fq_target_dir)

        # Verify calls needed to clone the source snapshot to the target dir
        expected_calls = []
        clone_path_list = [
            'file1', 'file2', 'dir1/file11', 'dir1/file12',
            'dir2/file21', 'dir2/file22']
        for path in clone_path_list:
            expected_call = IsilonApiTest.ExpectedCall(
                IsilonApiTest.ExpectedCall.FILE_CLONE,
                self._mock_url + '/namespace/ifs/admin/target/' + path,
                ['/ifs/admin/target/' + path, '/ifs/admin/source/' + path,
                 snapshot_name])
            expected_calls.append(expected_call)
        dir_path_list = [
            ('/dir1?recursive', '/dir1'),
            ('/dir2?recursive', '/dir2'),
            ('?recursive=', '')]
        for url, path in dir_path_list:
            expected_call = IsilonApiTest.ExpectedCall(
                IsilonApiTest.ExpectedCall.DIR_CREATION,
                self._mock_url + '/namespace/ifs/admin/target' + url,
                ['/ifs/admin/target' + path, False])
            expected_calls.append(expected_call)

        self._verify_clone_snapshot_calls(expected_calls, m.request_history)

    class ExpectedCall(object):
        DIR_CREATION = 'dir_creation'
        FILE_CLONE = 'file_clone'

        def __init__(self, request_type, match_url, verify_args):
            self.request_type = request_type
            self.match_url = match_url
            self.verify_args = verify_args

    def _verify_clone_snapshot_calls(self, expected_calls, response_calls):
        actual_calls = []
        for call in response_calls:
            actual_calls.append(call)
        for expected_call in expected_calls:
            # Match the expected call to the actual call, then verify
            match_found = False
            for call in actual_calls:
                if call.url.startswith(expected_call.match_url):
                    match_found = True
                    if expected_call.request_type == 'dir_creation':
                        self._verify_dir_creation_request(
                            call, *expected_call.verify_args)
                    elif expected_call.request_type == 'file_clone':
                        pass
                    else:
                        self.fail('Invalid request type')
                    actual_calls.remove(call)
            self.assertTrue(match_found)

    @requests_mock.mock()
    def test_get_directory_listing(self, m):
        self.assertEqual(0, len(m.request_history))
        fq_dir_path = 'ifs/admin/test'
        json_str = '{"my_json": "test123"}'
        self._add_get_directory_listing_response(m, fq_dir_path, json_str)

        actual_json = self.isilon_api.get_directory_listing(fq_dir_path)

        self.assertEqual(1, len(m.request_history))
        self.assertEqual(json.loads(json_str), actual_json)

    @ddt.data((200, True), (404, False))
    def test_is_path_existent(self, data):
        status_code, expected_return_value = data
        with requests_mock.mock() as m:
            self.assertEqual(0, len(m.request_history))
            path = '/ifs/home/admin'
            m.head('{0}/namespace{1}'.format(self._mock_url, path),
                   status_code=status_code)

            r = self.isilon_api.is_path_existent(path)

            self.assertEqual(expected_return_value, r)
            self.assertEqual(1, len(m.request_history))

    @requests_mock.mock()
    def test_is_path_existent_unexpected_error(self, m):
        path = '/ifs/home/admin'
        m.head('{0}/namespace{1}'.format(self._mock_url, path),
               status_code=400)

        self.assertRaises(
            requests.exceptions.HTTPError, self.isilon_api.is_path_existent,
            '/ifs/home/admin')

    @ddt.data(
        (200, '{"snapshots": [{"path": "/ifs/home/test"}]}',
         {'path': '/ifs/home/test'}),
        (404, '{"errors": []}', None)
    )
    def test_get_snapshot(self, data):
        status_code, json_body, expected_return_value = data
        with requests_mock.mock() as m:
            self.assertEqual(0, len(m.request_history))
            snapshot_name = 'foo1'
            self._add_get_snapshot_response(m, snapshot_name, json_body,
                                            status=status_code)

            r = self.isilon_api.get_snapshot(snapshot_name)

            self.assertEqual(1, len(m.request_history))
            self.assertEqual(expected_return_value, r)

    @requests_mock.mock()
    def test_get_snapshot_unexpected_error(self, m):
        snapshot_name = 'foo1'
        json_body = '{"snapshots": [{"path": "/ifs/home/test"}]}'
        self._add_get_snapshot_response(
            m, snapshot_name, json_body, status=400)

        self.assertRaises(
            requests.exceptions.HTTPError, self.isilon_api.get_snapshot,
            snapshot_name)

    @requests_mock.mock()
    def test_get_snapshots(self, m):
        self.assertEqual(0, len(m.request_history))
        snapshot_json = '{"snapshots": [{"path": "/ifs/home/test"}]}'
        m.get('{0}/platform/1/snapshot/snapshots'.format(self._mock_url),
              status_code=200, json=json.loads(snapshot_json))

        r = self.isilon_api.get_snapshots()

        self.assertEqual(1, len(m.request_history))
        self.assertEqual(json.loads(snapshot_json), r)

    @requests_mock.mock()
    def test_get_snapshots_error_occurred(self, m):
        self.assertEqual(0, len(m.request_history))
        m.get('{0}/platform/1/snapshot/snapshots'.format(self._mock_url),
              status_code=404)

        self.assertRaises(requests.exceptions.HTTPError,
                          self.isilon_api.get_snapshots)

        self.assertEqual(1, len(m.request_history))

    @ddt.data(
        ('/ifs/home/admin',
         '{"exports": [{"id": 42, "paths": ["/ifs/home/admin"]}]}', 42),
        ('/ifs/home/test',
         '{"exports": [{"id": 42, "paths": ["/ifs/home/admin"]}]}', None)
    )
    def test_lookup_nfs_export(self, data):
        share_path, response_json, expected_return = data
        with requests_mock.mock() as m:
            self.assertEqual(0, len(m.request_history))
            m.get('{0}/platform/1/protocols/nfs/exports'
                  .format(self._mock_url), json=json.loads(response_json))

            r = self.isilon_api.lookup_nfs_export(share_path)

            self.assertEqual(1, len(m.request_history))
            self.assertEqual(expected_return, r)

    @requests_mock.mock()
    def test_get_nfs_export(self, m):
        self.assertEqual(0, len(m.request_history))
        export_id = 42
        response_json = '{"exports": [{"id": 1}]}'
        status_code = 200
        m.get('{0}/platform/1/protocols/nfs/exports/{1}'
              .format(self._mock_url, export_id),
              json=json.loads(response_json), status_code=status_code)

        r = self.isilon_api.get_nfs_export(export_id)

        self.assertEqual(1, len(m.request_history))
        self.assertEqual(json.loads('{"id": 1}'), r)

    @requests_mock.mock()
    def test_get_nfs_export_error(self, m):
        self.assertEqual(0, len(m.request_history))
        export_id = 3
        response_json = '{}'
        status_code = 404
        m.get('{0}/platform/1/protocols/nfs/exports/{1}'
              .format(self._mock_url, export_id),
              json=json.loads(response_json), status_code=status_code)

        r = self.isilon_api.get_nfs_export(export_id)

        self.assertEqual(1, len(m.request_history))
        self.assertIsNone(r)

    @requests_mock.mock()
    def test_lookup_smb_share(self, m):
        self.assertEqual(0, len(m.request_history))
        share_name = 'my_smb_share'
        share_json = '{"id": "my_smb_share"}'
        response_json = '{{"shares": [{0}]}}'.format(share_json)
        m.get('{0}/platform/1/protocols/smb/shares/{1}'
              .format(self._mock_url, share_name), status_code=200,
              json=json.loads(response_json))

        r = self.isilon_api.lookup_smb_share(share_name)

        self.assertEqual(1, len(m.request_history))
        self.assertEqual(json.loads(share_json), r)

    @requests_mock.mock()
    def test_lookup_smb_share_error(self, m):
        self.assertEqual(0, len(m.request_history))
        share_name = 'my_smb_share'
        m.get('{0}/platform/1/protocols/smb/shares/{1}'.format(
            self._mock_url, share_name), status_code=404)

        r = self.isilon_api.lookup_smb_share(share_name)

        self.assertEqual(1, len(m.request_history))
        self.assertIsNone(r)

    @ddt.data((201, True), (404, False))
    def test_create_nfs_export(self, data):
        status_code, expected_return_value = data
        with requests_mock.mock() as m:
            self.assertEqual(0, len(m.request_history))
            export_path = '/ifs/home/test'
            m.post(self._mock_url + '/platform/1/protocols/nfs/exports',
                   status_code=status_code)

            r = self.isilon_api.create_nfs_export(export_path)

            self.assertEqual(1, len(m.request_history))
            call = m.request_history[0]
            expected_request_body = '{"paths": ["/ifs/home/test"]}'
            self.assertEqual(json.loads(expected_request_body),
                             json.loads(call.body))
            self.assertEqual(expected_return_value, r)

    @ddt.data((201, True), (404, False))
    def test_create_smb_share(self, data):
        status_code, expected_return_value = data
        with requests_mock.mock() as m:
            self.assertEqual(0, len(m.request_history))
            share_name = 'my_smb_share'
            share_path = '/ifs/home/admin/smb_share'
            m.post(self._mock_url + '/platform/1/protocols/smb/shares',
                   status_code=status_code)

            r = self.isilon_api.create_smb_share(share_name, share_path)

            self.assertEqual(expected_return_value, r)
            self.assertEqual(1, len(m.request_history))
            expected_request_data = {
                'name': share_name,
                'path': share_path,
                'permissions': []
            }
            self.assertEqual(expected_request_data,
                             json.loads(m.request_history[0].body))

    @requests_mock.mock()
    def test_create_snapshot(self, m):
        self.assertEqual(0, len(m.request_history))
        snapshot_name = 'my_snapshot_01'
        snapshot_path = '/ifs/home/admin'
        m.post(self._mock_url + '/platform/1/snapshot/snapshots',
               status_code=201)

        r = self.isilon_api.create_snapshot(snapshot_name, snapshot_path)

        self.assertEqual(1, len(m.request_history))
        self.assertTrue(r)
        expected_request_body = json.loads(
            '{{"name": "{0}", "path": "{1}"}}'
            .format(snapshot_name, snapshot_path)
        )
        self.assertEqual(expected_request_body,
                         json.loads(m.request_history[0].body))

    @requests_mock.mock()
    def test_create_snapshot_error_case(self, m):
        self.assertEqual(0, len(m.request_history))
        snapshot_name = 'my_snapshot_01'
        snapshot_path = '/ifs/home/admin'
        m.post(self._mock_url + '/platform/1/snapshot/snapshots',
               status_code=404)

        self.assertRaises(requests.exceptions.HTTPError,
                          self.isilon_api.create_snapshot,
                          snapshot_name, snapshot_path)
        self.assertEqual(1, len(m.request_history))

    @ddt.data(True, False)
    def test_delete(self, is_recursive_delete):
        with requests_mock.mock() as m:
            self.assertEqual(0, len(m.request_history))
            fq_path = '/ifs/home/admin/test'
            m.delete(self._mock_url + '/namespace' + fq_path + '?recursive='
                     + six.text_type(is_recursive_delete), status_code=204)

            self.isilon_api.delete(fq_path, recursive=is_recursive_delete)

            self.assertEqual(1, len(m.request_history))

    @requests_mock.mock()
    def test_delete_error_case(self, m):
        fq_path = '/ifs/home/admin/test'
        m.delete(self._mock_url + '/namespace' + fq_path + '?recursive=False',
                 status_code=403)

        self.assertRaises(requests.exceptions.HTTPError,
                          self.isilon_api.delete, fq_path, recursive=False)

    @ddt.data((204, True), (404, False))
    def test_delete_nfs_share(self, data):
        status_code, expected_return_value = data
        with requests_mock.mock() as m:
            self.assertEqual(0, len(m.request_history))
            share_number = 42
            m.delete('{0}/platform/1/protocols/nfs/exports/{1}'
                     .format(self._mock_url, share_number),
                     status_code=status_code)

            r = self.isilon_api.delete_nfs_share(share_number)

            self.assertEqual(1, len(m.request_history))
            self.assertEqual(expected_return_value, r)

    @ddt.data((204, True), (404, False))
    def test_delete_smb_shares(self, data):
        status_code, expected_return_value = data
        with requests_mock.mock() as m:
            self.assertEqual(0, len(m.request_history))

            share_name = 'smb_share_42'
            m.delete('{0}/platform/1/protocols/smb/shares/{1}'
                     .format(self._mock_url, share_name),
                     status_code=status_code)

            r = self.isilon_api.delete_smb_share(share_name)

            self.assertEqual(1, len(m.request_history))
            self.assertEqual(expected_return_value, r)

    @requests_mock.mock()
    def test_delete_snapshot(self, m):
        self.assertEqual(0, len(m.request_history))
        m.delete(self._mock_url + '/platform/1/snapshot/snapshots/my_snapshot',
                 status_code=204)

        self.isilon_api.delete_snapshot("my_snapshot")

        self.assertEqual(1, len(m.request_history))

    @requests_mock.mock()
    def test_delete_snapshot_error_case(self, m):
        m.delete(self._mock_url + '/platform/1/snapshot/snapshots/my_snapshot',
                 status_code=403)

        self.assertRaises(requests.exceptions.HTTPError,
                          self.isilon_api.delete_snapshot, "my_snapshot")

    @requests_mock.mock()
    def test_quota_create(self, m):
        quota_path = '/ifs/manila/test'
        quota_size = 256
        self.assertEqual(0, len(m.request_history))
        m.post(self._mock_url + '/platform/1/quota/quotas', status_code=201)

        self.isilon_api.quota_create(quota_path, 'directory', quota_size)

        self.assertEqual(1, len(m.request_history))
        expected_request_json = {
            'path': quota_path,
            'type': 'directory',
            'include_snapshots': False,
            'thresholds_include_overhead': False,
            'enforced': True,
            'thresholds': {'hard': quota_size},
        }
        call_body = m.request_history[0].body
        self.assertEqual(expected_request_json, json.loads(call_body))

    @requests_mock.mock()
    def test_quota_create__path_does_not_exist(self, m):
        quota_path = '/ifs/test2'
        self.assertEqual(0, len(m.request_history))
        m.post(self._mock_url + '/platform/1/quota/quotas', status_code=400)

        self.assertRaises(
            requests.exceptions.HTTPError,
            self.isilon_api.quota_create,
            quota_path, 'directory', 2
        )

    @requests_mock.mock()
    def test_quota_get(self, m):
        self.assertEqual(0, len(m.request_history))
        response_json = {'quotas': [{}]}
        m.get(self._mock_url + '/platform/1/quota/quotas', json=response_json,
              status_code=200)
        quota_path = "/ifs/manila/test"
        quota_type = "directory"

        self.isilon_api.quota_get(quota_path, quota_type)

        self.assertEqual(1, len(m.request_history))
        request_query_string = m.request_history[0].qs
        expected_query_string = {'path': [quota_path]}
        self.assertEqual(expected_query_string, request_query_string)

    @requests_mock.mock()
    def test_quota_get__path_does_not_exist(self, m):
        self.assertEqual(0, len(m.request_history))
        m.get(self._mock_url + '/platform/1/quota/quotas', status_code=404)

        response = self.isilon_api.quota_get(
            '/ifs/does_not_exist', 'directory')

        self.assertIsNone(response)

    @requests_mock.mock()
    def test_quota_modify(self, m):
        self.assertEqual(0, len(m.request_history))
        quota_id = "ADEF1G"
        new_size = 1024
        m.put('{0}/platform/1/quota/quotas/{1}'.format(
            self._mock_url, quota_id), status_code=204)

        self.isilon_api.quota_modify_size(quota_id, new_size)

        self.assertEqual(1, len(m.request_history))
        expected_request_body = {'thresholds': {'hard': new_size}}
        request_body = m.request_history[0].body
        self.assertEqual(expected_request_body, json.loads(request_body))

    @requests_mock.mock()
    def test_quota_modify__given_id_does_not_exist(self, m):
        quota_id = 'ADE2F'
        m.put('{0}/platform/1/quota/quotas/{1}'.format(
            self._mock_url, quota_id), status_code=404)

        self.assertRaises(
            requests.exceptions.HTTPError,
            self.isilon_api.quota_modify_size,
            quota_id, 1024
        )

    @requests_mock.mock()
    def test_quota_set__quota_already_exists(self, m):
        self.assertEqual(0, len(m.request_history))
        quota_path = '/ifs/manila/test'
        quota_type = 'directory'
        quota_size = 256
        quota_id = 'AFE2C'
        m.get('{0}/platform/1/quota/quotas'.format(
            self._mock_url), json={'quotas': [{'id': quota_id}]},
            status_code=200)
        m.put(
            '{0}/platform/1/quota/quotas/{1}'.format(self._mock_url, quota_id),
            status_code=204
        )

        self.isilon_api.quota_set(quota_path, quota_type, quota_size)

        expected_quota_modify_json = {'thresholds': {'hard': quota_size}}
        quota_put_json = json.loads(m.request_history[1].body)
        self.assertEqual(expected_quota_modify_json, quota_put_json)

    @requests_mock.mock()
    def test_quota_set__quota_does_not_already_exist(self, m):
        self.assertEqual(0, len(m.request_history))
        m.get('{0}/platform/1/quota/quotas'.format(
            self._mock_url), status_code=404)
        m.post('{0}/platform/1/quota/quotas'.format(self._mock_url),
               status_code=201)
        quota_path = '/ifs/manila/test'
        quota_type = 'directory'
        quota_size = 256

        self.isilon_api.quota_set(quota_path, quota_type, quota_size)

        # verify a call is made to create a quota
        expected_create_json = {
            six.text_type('path'): quota_path,
            six.text_type('type'): 'directory',
            six.text_type('include_snapshots'): False,
            six.text_type('thresholds_include_overhead'): False,
            six.text_type('enforced'): True,
            six.text_type('thresholds'): {six.text_type('hard'): quota_size},
        }
        create_request_json = json.loads(m.request_history[1].body)
        self.assertEqual(expected_create_json, create_request_json)

    @requests_mock.mock()
    def test_quota_set__path_does_not_already_exist(self, m):
        m.get(self._mock_url + '/platform/1/quota/quotas', status_code=400)

        e = self.assertRaises(
            requests.exceptions.HTTPError,
            self.isilon_api.quota_set,
            '/ifs/does_not_exist', 'directory', 2048
        )
        self.assertEqual(400, e.response.status_code)

    @ddt.data(
        ('foouser', isilon_api.SmbPermission.rw),
        ('testuser', isilon_api.SmbPermission.ro),
    )
    def test_smb_permission_add(self, data):
        user, smb_permission = data
        share_name = 'testshare'

        with requests_mock.mock() as m:
            papi_share_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
                self._mock_url, share_name)
            share_data = {
                'shares': [
                    {'permissions': []}
                ]
            }
            m.get(papi_share_url, status_code=200, json=share_data)

            auth_url = ('{0}/platform/1/auth/mapping/users/lookup?user={1}'
                        ''.format(self._mock_url, user))
            example_sid = 'SID:S-1-5-21'
            sid_json = {
                'id': example_sid,
                'name': user,
                'type': 'user'
            }
            auth_json = {'mapping': [
                {'user': {'sid': sid_json}}
            ]}
            m.get(auth_url, status_code=200, json=auth_json)
            m.put(papi_share_url)

            self.isilon_api.smb_permissions_add(share_name, user,
                                                smb_permission)

            perms_put_request = m.request_history[2]
            expected_perm_request_json = {
                'permissions': [
                    {'permission': smb_permission.value,
                     'permission_type': 'allow',
                     'trustee': sid_json
                     }
                ]
            }
            self.assertEqual(expected_perm_request_json,
                             json.loads(perms_put_request.body))

    @requests_mock.mock()
    def test_smb_permission_add_with_multiple_users_found(self, m):
        user = 'foouser'
        smb_permission = isilon_api.SmbPermission.rw
        share_name = 'testshare'
        papi_share_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
            self._mock_url, share_name)
        share_data = {
            'shares': [
                {'permissions': []}
            ]
        }
        m.get(papi_share_url, status_code=200, json=share_data)

        auth_url = ('{0}/platform/1/auth/mapping/users/lookup?user={1}'
                    ''.format(self._mock_url, user))
        example_sid = 'SID:S-1-5-21'
        sid_json = {
            'id': example_sid,
            'name': user,
            'type': 'user'
        }
        auth_json = {'mapping': [
            {'user': {'sid': sid_json}},
            {'user': {'sid': sid_json}},
        ]}
        m.get(auth_url, status_code=200, json=auth_json)
        m.put(papi_share_url)

        self.assertRaises(exception.ShareBackendException,
                          self.isilon_api.smb_permissions_add,
                          share_name, user, smb_permission)

    @requests_mock.mock()
    def test_smb_permission_remove(self, m):

        share_name = 'testshare'
        user = 'testuser'

        share_data = {
            'permissions': [{
                'permission': 'change',
                'permission_type': 'allow',
                'trustee': {
                    'id': 'SID:S-1-5-21',
                    'name': user,
                    'type': 'user',
                }
            }]
        }
        papi_share_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
            self._mock_url, share_name)
        m.get(papi_share_url, status_code=200, json={'shares': [share_data]})
        num_existing_perms = len(self.isilon_api.lookup_smb_share(share_name))
        self.assertEqual(1, num_existing_perms)

        m.put(papi_share_url)
        self.isilon_api.smb_permissions_remove(share_name, user)

        smb_put_request = m.request_history[2]
        expected_body = {'permissions': []}
        expected_body = json.dumps(expected_body)
        self.assertEqual(expected_body, smb_put_request.body)

    @requests_mock.mock()
    def test_smb_permission_remove_with_multiple_existing_perms(self, m):

        share_name = 'testshare'
        user = 'testuser'

        foouser_perms = {
            'permission': 'change',
            'permission_type': 'allow',
            'trustee': {
                'id': 'SID:S-1-5-21',
                'name': 'foouser',
                'type': 'user',
            }
        }
        user_perms = {
            'permission': 'change',
            'permission_type': 'allow',
            'trustee': {
                'id': 'SID:S-1-5-22',
                'name': user,
                'type': 'user',
            }
        }
        share_data = {
            'permissions': [
                foouser_perms,
                user_perms,
            ]
        }
        papi_share_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
            self._mock_url, share_name)
        m.get(papi_share_url, status_code=200, json={'shares': [share_data]})
        num_existing_perms = len(self.isilon_api.lookup_smb_share(
            share_name)['permissions'])
        self.assertEqual(2, num_existing_perms)
        m.put(papi_share_url)

        self.isilon_api.smb_permissions_remove(share_name, user)

        smb_put_request = m.request_history[2]
        expected_body = {'permissions': [foouser_perms]}
        expected_body = json.dumps(expected_body)
        self.assertEqual(json.loads(expected_body),
                         json.loads(smb_put_request.body))

    @requests_mock.mock()
    def test_smb_permission_remove_with_empty_perms_list(self, m):
        share_name = 'testshare'
        user = 'testuser'

        share_data = {'permissions': []}
        papi_share_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
            self._mock_url, share_name)
        m.get(papi_share_url, status_code=200, json={'shares': [share_data]})
        m.put(papi_share_url)

        self.assertRaises(exception.ShareBackendException,
                          self.isilon_api.smb_permissions_remove,
                          share_name, user)

    @requests_mock.mock()
    def test_auth_lookup_user(self, m):
        user = 'foo'
        auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format(
            self._mock_url, user)
        example_sid = 'SID:S-1-5-21'
        sid_json = {
            'id': example_sid,
            'name': user,
            'type': 'user'
        }
        auth_json = {
            'mapping': [
                {'user': {'sid': sid_json}}
            ]
        }
        m.get(auth_url, status_code=200, json=auth_json)

        returned_auth_json = self.isilon_api.auth_lookup_user(user)
        self.assertEqual(auth_json, returned_auth_json)

    @requests_mock.mock()
    def test_auth_lookup_user_with_nonexistent_user(self, m):
        user = 'nonexistent'
        auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format(
            self._mock_url, user)
        m.get(auth_url, status_code=404)
        self.assertRaises(exception.ShareBackendException,
                          self.isilon_api.auth_lookup_user, user)

    @requests_mock.mock()
    def test_auth_lookup_user_with_backend_error(self, m):
        user = 'foo'
        auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format(
            self._mock_url, user)
        m.get(auth_url, status_code=400)
        self.assertRaises(requests.exceptions.HTTPError,
                          self.isilon_api.auth_lookup_user, user)

    def _add_create_directory_response(self, m, path, is_recursive):
        url = '{0}/namespace{1}?recursive={2}'.format(
            self._mock_url, path, six.text_type(is_recursive))
        m.put(url, status_code=200)

    def _add_file_clone_response(self, m, fq_dest_path, snapshot_name):
        url = '{0}/namespace{1}?clone=true&snapshot={2}'.format(
            self._mock_url, fq_dest_path, snapshot_name)
        m.put(url)

    def _add_get_directory_listing_response(self, m, fq_dir_path, json_str):
        url = '{0}/namespace{1}?detail=default'.format(
            self._mock_url, fq_dir_path)
        m.get(url, json=json.loads(json_str), status_code=200)

    def _add_get_snapshot_response(
            self, m, snapshot_name, json_str, status=200):
        url = '{0}/platform/1/snapshot/snapshots/{1}'.format(
            self._mock_url, snapshot_name
        )
        m.get(url, status_code=status, json=json.loads(json_str))

    def _verify_dir_creation_request(self, request, path, is_recursive):
        self.assertEqual('PUT', request.method)
        expected_url = '{0}/namespace{1}?recursive={2}'.format(
            self._mock_url, path, six.text_type(is_recursive))
        self.assertEqual(expected_url, request.url)
        self.assertIn("x-isi-ifs-target-type", request.headers)
        self.assertEqual("container",
                         request.headers['x-isi-ifs-target-type'])

    def _verify_clone_file_from_snapshot(
            self, request, fq_file_path, fq_dest_path, snapshot_name):
        self.assertEqual('PUT', request.method)
        expected_url = '{0}/namespace{1}?clone=true&snapshot={2}'.format(
            self._mock_url, fq_dest_path, snapshot_name
        )
        self.assertEqual(expected_url, request.request.url)
        self.assertIn("x-isi-ifs-copy-source", request.headers)
        self.assertEqual('/namespace' + fq_file_path,
                         request.headers['x-isi-ifs-copy-source'])
