from collections import OrderedDict

from src.converter.convert_strategy import (BooleanConvertStrategy,
                                            ConvertStrategyFactory,
                                            DatetimeConvertStrategy,
                                            DictConvertStrategy,
                                            IntConvertStrategy,
                                            NoneValueConvertStrategy,
                                            StringConvertStrategy)


class TestConvertStrategyFactory:

    def test_create_none(self):
        """
        Cases:
            引数にnull(None)を指定した場合、NoneValueConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create(None)

        # Expects
        assert type(actual) == NoneValueConvertStrategy

    def test_create_float(self):
        """
        Cases:
            引数に指数表記を指定した場合、StringConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create(1.2345678E7)

        # Expects
        # 変換しない
        assert type(actual) == StringConvertStrategy

    def test_create_float_scale(self):
        """
        Cases:
            引数に少数を指定した場合、StringConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create(1.2345678)

        # Expects
        # 変換しない
        assert type(actual) == StringConvertStrategy

    def test_create_bool_true(self):
        """
        Cases:
            引数に論理値(True)を指定した場合、BooleanConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create(True)

        # Expects
        assert type(actual) == BooleanConvertStrategy

    def test_create_bool_false(self):
        """
        Cases:
            引数に論理値(False)を指定した場合、BooleanConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create(False)

        # Expects
        assert type(actual) == BooleanConvertStrategy

    def test_create_datetime(self):
        """
        Cases:
            引数に文字列かつ日付文字列を指定した場合、DatetimeConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create('2022-06-13T10:15:32.000+0000')

        # Expects
        assert type(actual) == DatetimeConvertStrategy

    def test_create_str(self):
        """
        Cases:
            引数にSalesforce日付型以外の文字列を指定した場合、StringConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create('test_string')

        # Expects
        assert type(actual) == StringConvertStrategy

    def test_create_int(self):
        """
        Cases:
            引数に整数を指定した場合、IntConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create(100)

        # Expects
        assert type(actual) == IntConvertStrategy

    def test_create_dict(self):
        """
        Cases:
            引数に辞書型の値を指定した場合、IntConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create({'key': 'value'})

        # Expects
        assert type(actual) == DictConvertStrategy

    def test_create_ordered_dict_dict(self):
        """
        Cases:
            引数に辞書型の値を指定した場合、IntConvertStrategyインスタンスが返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = ConvertStrategyFactory()
        actual = sut.create(OrderedDict([('key', 'value')]))

        # Expects
        assert type(actual) == DictConvertStrategy


class TestNoneValueConvertStrategy:

    def test_convert_value(self) -> str:
        """
        Cases:
            引数にnull(None)を指定した場合、''(空文字)が返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = NoneValueConvertStrategy()
        actual = sut.convert_value(None)

        # Expects
        assert actual == ""


class TestBooleanConvertStrategy:

    def test_convert_value_true(self) -> bool:
        """
        Cases:
            引数に論理値(True)を指定した場合、数値(1)が返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = BooleanConvertStrategy()
        actual = sut.convert_value(True)

        # Expects
        assert actual == 1

    def test_convert_value_false(self) -> bool:
        """
        Cases:
            引数に論理値(False)を指定した場合、数値(0)が返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = BooleanConvertStrategy()
        actual = sut.convert_value(False)

        # Expects
        assert actual == 0


class TestDatetimeConvertStrategy:

    def test_convert_value(self) -> str:
        """
        Cases:
            引数に日付文字列を指定した場合、YYYY-MM-DD HH:MM:SSがJSTで返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = DatetimeConvertStrategy()
        actual = sut.convert_value('2022-06-13T20:15:32.000+0000')

        # Expects
        assert actual == "2022-06-14 05:15:32"


class TestIntConvertStrategy:

    def test_convert_value(self):
        """
        Cases:
            引数に整数を指定した場合、加工されず整数が返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = IntConvertStrategy()
        actual = sut.convert_value(100)

        # Expects
        assert actual == 100


class TestStringConvertStrategy:

    def test_convert_value(self):
        """
        Cases:
            引数に文字列を指定した場合、加工されず文字列が返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = StringConvertStrategy()
        actual = sut.convert_value('テストデータ')

        # Expects
        assert actual == 'テストデータ'


class TestDictConvertStrategy:

    def test_convert_value_dict(self):
        """
        Cases:
            引数に辞書型のデータを指定した場合、JSONの文字列が返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = DictConvertStrategy()
        actual = sut.convert_value({'テストデータキー': 'テストデータバリュー'})

        # Expects
        assert actual == '{"テストデータキー": "テストデータバリュー"}'

    def test_convert_value_dict_in_line_break(self):
        """
        Cases:
            引数に辞書型のデータを指定した場合、JSONの文字列が返ってくること(バリューに改行を含む)
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = DictConvertStrategy()
        actual = sut.convert_value({'テストデータキー': 'テスト\nデータ\nバリュー'})

        # Expects
        assert actual == '{"テストデータキー": "テスト\\nデータ\\nバリュー"}'

    def test_convert_value_ordered_dict(self):
        """
        Cases:
            引数に整列された辞書型のデータを指定した場合、JSONの文字列が返ってくること
        Arranges:
            - なし
        Expects:
            - 戻り値が、期待値と一致する
        """

        # Act
        sut = DictConvertStrategy()
        actual = sut.convert_value(OrderedDict(
            [('テストデータキー', 'テストデータバリュー')]
        ))

        # Expects
        assert actual == '{"テストデータキー": "テストデータバリュー"}'
