# -*- coding: utf-8 -*-
'''
Tests the localfs tokens interface.
'''
from __future__ import absolute_import, print_function, unicode_literals

import os

import salt.exceptions
import salt.tokens.localfs
import salt.utils.files

from tests.support.unit import TestCase, skipIf
from tests.support.helpers import with_tempdir
from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch


class CalledWith(object):

    def __init__(self, func, called_with=None):
        self.func = func
        if called_with is None:
            self.called_with = []
        else:
            self.called_with = called_with

    def __call__(self, *args, **kwargs):
        self.called_with.append((args, kwargs))
        return self.func(*args, **kwargs)


@skipIf(NO_MOCK, NO_MOCK_REASON)
class WriteTokenTest(TestCase):

    @with_tempdir()
    def test_write_token(self, tmpdir):
        '''
        Validate tokens put in place with an atomic move
        '''
        opts = {
            'token_dir': tmpdir
        }
        fopen = CalledWith(salt.utils.files.fopen)
        rename = CalledWith(os.rename)
        with patch('salt.utils.files.fopen', fopen), patch('os.rename', rename):
            tdata = salt.tokens.localfs.mk_token(opts, {})
        assert 'token' in tdata
        t_path = os.path.join(tmpdir, tdata['token'])
        temp_t_path = '{}.tmp'.format(t_path)
        assert len(fopen.called_with) == 1, len(fopen.called_with)
        assert fopen.called_with == [
            ((temp_t_path, 'w+b'), {})
        ], fopen.called_with
        assert len(rename.called_with) == 1, len(rename.called_with)
        assert rename.called_with == [
            ((temp_t_path, t_path), {})
        ], rename.called_with


class TestLocalFS(TestCase):
    def setUp(self):
        # Default expected data
        self.expected_data = {'this': 'is', 'some': 'token data'}

    @with_tempdir()
    def test_get_token_should_return_token_if_exists(self, tempdir):
        opts = {'token_dir': tempdir}
        tok = salt.tokens.localfs.mk_token(
            opts=opts,
            tdata=self.expected_data,
        )['token']
        actual_data = salt.tokens.localfs.get_token(opts=opts, tok=tok)
        self.assertDictEqual(self.expected_data, actual_data)

    @with_tempdir()
    def test_get_token_should_raise_SaltDeserializationError_if_token_file_is_empty(self, tempdir):
        opts = {'token_dir': tempdir}
        tok = salt.tokens.localfs.mk_token(
            opts=opts,
            tdata=self.expected_data,
        )['token']
        with salt.utils.files.fopen(os.path.join(tempdir, tok), 'w') as f:
            f.truncate()
        with self.assertRaises(salt.exceptions.SaltDeserializationError) as e:
            salt.tokens.localfs.get_token(opts=opts, tok=tok)

    @with_tempdir()
    def test_get_token_should_raise_SaltDeserializationError_if_token_file_is_malformed(self, tempdir):
        opts = {'token_dir': tempdir}
        tok = salt.tokens.localfs.mk_token(
            opts=opts,
            tdata=self.expected_data,
        )['token']
        with salt.utils.files.fopen(os.path.join(tempdir, tok), 'w') as f:
            f.truncate()
            f.write('this is not valid msgpack data')
        with self.assertRaises(salt.exceptions.SaltDeserializationError) as e:
            salt.tokens.localfs.get_token(opts=opts, tok=tok)
