import unittest
from unittest.mock import Mock, patch
import json
from ultimaker import Printer, Credentials, Identity, PrintJob
from uuid import UUID, uuid4
import os
from datetime import timedelta
from typing import Dict
import requests

mock_identity: str = Identity('mock application', 'mock user')
mock_name: str = '2D Printer'
mock_guid: UUID = uuid4()
mock_address: str = '127.0.0.1'
mock_port: str = '8080'
mock_id: str = '1234'
mock_key: str = 'abcd'
mock_credentials: Credentials = Credentials(mock_id, mock_key)
mock_credentials_json: Dict[str, str] = mock_credentials._asdict()
mock_print_job_name: str = '3DBenchy'
mock_print_job_time_elapsed: timedelta = timedelta(seconds=30)
mock_print_job_time_total: timedelta = timedelta(seconds=60)
mock_print_job_progress: float = 30./60.
mock_print_job_state: str = 'printing'
mock_print_job: PrintJob = PrintJob(**{
    "datetime_cleaned": "",
    "datetime_finished": "",
    "datetime_started": "2019-09-17T18:01:32",
    "name": mock_print_job_name,
    "pause_source": "",
    "progress": mock_print_job_progress,
    "reprint_original_uuid": "",
    "result": "",
    "source": "WEB_API",
    "source_application": "Cura Connect",
    "source_user": "U2",
    "state": mock_print_job_state,
    "time_elapsed": mock_print_job_time_elapsed,
    "time_total": mock_print_job_time_total,
    "uuid": uuid4()
})
mock_camera_snapshot_uri: str = 'data:image/png,base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/<KEY>


def default_printer_mock() -> Printer:
    printer = Printer(mock_address, mock_port, mock_identity, mock_credentials)
    # TODO: understand unittest.mock patch method so the set_credentials method can be asserted on
    # printer.set_credentials = patch.object(printer, 'set_credentials', wraps=printer.set_credentials)
    printer.post_auth_request = Mock(
        return_value=mock_credentials_json)
    printer.get_auth_check = Mock(return_value='authorized')
    printer.get_auth_verify = Mock(return_value=True)
    printer.get_system_name = Mock(return_value=mock_name)
    printer.get_printer_status = Mock(return_value='idle')
    printer.get_system_guid = Mock(return_value=mock_guid)
    printer.get_print_job = Mock(return_value=mock_print_job)
    printer.name = mock_name
    printer.guid = mock_guid
    return printer


class AcquireCredentialsTest(unittest.TestCase):
    def setUp(self):
        printer = default_printer_mock()
        printer.credentials = None
        self.printer = printer

    def test_printer_acquires_credentials(self):
        self.printer.get_credentials()
        self.assertTrue(self.printer.credentials is not None)
        self.printer.post_auth_request.assert_called_once()
        self.printer.get_auth_check.assert_not_called()
        self.printer.get_auth_verify.assert_not_called()

    def test_printer_acquires_credentials_only_once(self):
        self.printer.get_credentials()
        self.assertTrue(self.printer.credentials is not None)
        self.printer.post_auth_request.assert_called_once()
        self.printer.get_auth_check.assert_not_called()
        self.printer.get_auth_verify.assert_not_called()


class AlreadyHasCredentialsTest(unittest.TestCase):
    def setUp(self):
        printer = default_printer_mock()
        printer.get_credentials = Mock(return_value=mock_credentials)
        self.printer = printer

    def test_printer_is_authorized(self):
        self.assertTrue(self.printer.is_authorized())
        self.printer.get_credentials.assert_called_once()
        self.printer.post_auth_request.assert_not_called()
        self.printer.get_auth_check.assert_called_once()


class UltimakerJsonTest(unittest.TestCase):
    def test_expected_json_is_produced_when_idle(self):
        printer = default_printer_mock()
        printer.get_credentials = Mock(return_value=mock_credentials)
        printer.get_camera_snapshot_uri = Mock(
            return_value=mock_camera_snapshot_uri
        )
        json = printer.into_ultimaker_json()
        self.assertDictEqual({
            'system': {
                'name': mock_name
            },
            'printer': {
                'status': 'idle'
            },
            'camera': {
                'snapshot': mock_camera_snapshot_uri
            }
        }, json)

    def test_expected_json_is_produced_when_timeout(self):
        printer = default_printer_mock()
        printer.get_credentials = Mock(return_value=mock_credentials)
        printer.get_printer_status = timeout_exception_raiser
        printer.get_print_job_state = Mock(return_value=mock_print_job_state)
        json = printer.into_ultimaker_json()
        self.assertDictEqual({'system': {
            'name': mock_name
        }}, json)

    def test_expected_json_is_produced_when_printing(self):
        printer = default_printer_mock()
        printer.get_credentials = Mock(return_value=mock_credentials)
        printer.get_printer_status = Mock(return_value='printing')
        printer.get_print_job_time_elapsed = Mock(
            return_value=mock_print_job_time_elapsed)
        printer.get_print_job_time_total = Mock(
            return_value=mock_print_job_time_total)
        printer.get_print_job_progress = Mock(
            return_value=mock_print_job_progress)
        printer.get_print_job_state = Mock(
            return_value=mock_print_job_state)
        printer.get_camera_snapshot_uri = Mock(
            return_value=mock_camera_snapshot_uri
        )
        printer.get_print_job = Mock(return_value=mock_print_job)

        json = printer.into_ultimaker_json()
        self.assertDictEqual({
            'system': {
                'name': mock_name
            },
            'printer': {
                'status': 'printing'
            },
            'print_job': {
                'time_elapsed': str(mock_print_job_time_elapsed),
                'time_total': str(mock_print_job_time_total),
                'progress': mock_print_job_progress,
                'state': mock_print_job_state
            },
            'camera': {
                'snapshot': mock_camera_snapshot_uri
            }
        }, json)


def generic_exception_raiser():
    raise requests.exceptions.RequestException('An exception has occurred')

def timeout_exception_raiser():
    raise requests.exceptions.Timeout('An exception has occurred')

class VerifiesLoadedCredentialsTest(unittest.TestCase):
    def setUp(self):
        printer = default_printer_mock()
        self.printer = printer
        self.printer.get_auth_verify = Mock(return_value=False)

    def test_printer_is_not_verified(self):
        self.assertFalse(self.printer.credentials is None)
        self.printer.get_credentials()
        self.assertTrue(self.printer.credentials is not None)
        self.printer.post_auth_request.assert_called_once()
        self.printer.get_auth_verify.assert_called_once()


if __name__ == '__main__':
    unittest.main()

import unittest
from unittest.mock import Mock, patch
import json
from ultimaker import Printer, Credentials, Identity, PrintJob
from uuid import UUID, uuid4
import os
from datetime import timedelta
from typing import Dict
import requests

mock_identity: str = Identity('mock application', 'mock user')
mock_name: str = '2D Printer'
mock_guid: UUID = uuid4()
mock_address: str = '127.0.0.1'
mock_port: str = '8080'
mock_id: str = '1234'
mock_key: str = 'abcd'
mock_credentials: Credentials = Credentials(mock_id, mock_key)
mock_credentials_json: Dict[str, str] = mock_credentials._asdict()
mock_print_job_name: str = '3DBenchy'
mock_print_job_time_elapsed: timedelta = timedelta(seconds=30)
mock_print_job_time_total: timedelta = timedelta(seconds=60)
mock_print_job_progress: float = 30./60.
mock_print_job_state: str = 'printing'
mock_print_job: PrintJob = PrintJob(**{
    "datetime_cleaned": "",
    "datetime_finished": "",
    "datetime_started": "2019-09-17T18:01:32",
    "name": mock_print_job_name,
    "pause_source": "",
    "progress": mock_print_job_progress,
    "reprint_original_uuid": "",
    "result": "",
    "source": "WEB_API",
    "source_application": "Cura Connect",
    "source_user": "U2",
    "state": mock_print_job_state,
    "time_elapsed": mock_print_job_time_elapsed,
    "time_total": mock_print_job_time_total,
    "uuid": uuid4()
})
mock_camera_snapshot_uri: str = 'data:image/png,base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/<KEY>


def default_printer_mock() -> Printer:
    printer = Printer(mock_address, mock_port, mock_identity, mock_credentials)
    # TODO: understand unittest.mock patch method so the set_credentials method can be asserted on
    # printer.set_credentials = patch.object(printer, 'set_credentials', wraps=printer.set_credentials)
    printer.post_auth_request = Mock(
        return_value=mock_credentials_json)
    printer.get_auth_check = Mock(return_value='authorized')
    printer.get_auth_verify = Mock(return_value=True)
    printer.get_system_name = Mock(return_value=mock_name)
    printer.get_printer_status = Mock(return_value='idle')
    printer.get_system_guid = Mock(return_value=mock_guid)
    printer.get_print_job = Mock(return_value=mock_print_job)
    printer.name = mock_name
    printer.guid = mock_guid
    return printer


class AcquireCredentialsTest(unittest.TestCase):
    def setUp(self):
        printer = default_printer_mock()
        printer.credentials = None
        self.printer = printer

    def test_printer_acquires_credentials(self):
        self.printer.get_credentials()
        self.assertTrue(self.printer.credentials is not None)
        self.printer.post_auth_request.assert_called_once()
        self.printer.get_auth_check.assert_not_called()
        self.printer.get_auth_verify.assert_not_called()

    def test_printer_acquires_credentials_only_once(self):
        self.printer.get_credentials()
        self.assertTrue(self.printer.credentials is not None)
        self.printer.post_auth_request.assert_called_once()
        self.printer.get_auth_check.assert_not_called()
        self.printer.get_auth_verify.assert_not_called()


class AlreadyHasCredentialsTest(unittest.TestCase):
    def setUp(self):
        printer = default_printer_mock()
        printer.get_credentials = Mock(return_value=mock_credentials)
        self.printer = printer

    def test_printer_is_authorized(self):
        self.assertTrue(self.printer.is_authorized())
        self.printer.get_credentials.assert_called_once()
        self.printer.post_auth_request.assert_not_called()
        self.printer.get_auth_check.assert_called_once()


class UltimakerJsonTest(unittest.TestCase):
    def test_expected_json_is_produced_when_idle(self):
        printer = default_printer_mock()
        printer.get_credentials = Mock(return_value=mock_credentials)
        printer.get_camera_snapshot_uri = Mock(
            return_value=mock_camera_snapshot_uri
        )
        json = printer.into_ultimaker_json()
        self.assertDictEqual({
            'system': {
                'name': mock_name
            },
            'printer': {
                'status': 'idle'
            },
            'camera': {
                'snapshot': mock_camera_snapshot_uri
            }
        }, json)

    def test_expected_json_is_produced_when_timeout(self):
        printer = default_printer_mock()
        printer.get_credentials = Mock(return_value=mock_credentials)
        printer.get_printer_status = timeout_exception_raiser
        printer.get_print_job_state = Mock(return_value=mock_print_job_state)
        json = printer.into_ultimaker_json()
        self.assertDictEqual({'system': {
            'name': mock_name
        }}, json)

    def test_expected_json_is_produced_when_printing(self):
        printer = default_printer_mock()
        printer.get_credentials = Mock(return_value=mock_credentials)
        printer.get_printer_status = Mock(return_value='printing')
        printer.get_print_job_time_elapsed = Mock(
            return_value=mock_print_job_time_elapsed)
        printer.get_print_job_time_total = Mock(
            return_value=mock_print_job_time_total)
        printer.get_print_job_progress = Mock(
            return_value=mock_print_job_progress)
        printer.get_print_job_state = Mock(
            return_value=mock_print_job_state)
        printer.get_camera_snapshot_uri = Mock(
            return_value=mock_camera_snapshot_uri
        )
        printer.get_print_job = Mock(return_value=mock_print_job)

        json = printer.into_ultimaker_json()
        self.assertDictEqual({
            'system': {
                'name': mock_name
            },
            'printer': {
                'status': 'printing'
            },
            'print_job': {
                'time_elapsed': str(mock_print_job_time_elapsed),
                'time_total': str(mock_print_job_time_total),
                'progress': mock_print_job_progress,
                'state': mock_print_job_state
            },
            'camera': {
                'snapshot': mock_camera_snapshot_uri
            }
        }, json)


def generic_exception_raiser():
    raise requests.exceptions.RequestException('An exception has occurred')

def timeout_exception_raiser():
    raise requests.exceptions.Timeout('An exception has occurred')

class VerifiesLoadedCredentialsTest(unittest.TestCase):
    def setUp(self):
        printer = default_printer_mock()
        self.printer = printer
        self.printer.get_auth_verify = Mock(return_value=False)

    def test_printer_is_not_verified(self):
        self.assertFalse(self.printer.credentials is None)
        self.printer.get_credentials()
        self.assertTrue(self.printer.credentials is not None)
        self.printer.post_auth_request.assert_called_once()
        self.printer.get_auth_verify.assert_called_once()


if __name__ == '__main__':
    unittest.main()

# TODO: understand unittest.mock patch method so the set_credentials method can be asserted on # printer.set_credentials = patch.object(printer, 'set_credentials', wraps=printer.set_credentials)