# Copyright 2022 The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Tests for DebusineBaseCommand class."""
import signal
from io import StringIO
from unittest import mock

from django.db.utils import DatabaseError
from django.test import TestCase

from debusine.server.management.debusine_base_command import DebusineBaseCommand
from debusine.test import TestHelpersMixin


class DebusineBaseCommandTests(TestHelpersMixin, TestCase):
    """Tests for the DebusineBaseCommand."""

    def setUp(self):
        """Initialize object."""
        self.default_sigint_handler = signal.getsignal(signal.SIGINT)
        self.default_sigterm_handler = signal.getsignal(signal.SIGTERM)

    def tearDown(self):
        """Restore signals."""
        signal.signal(signal.SIGINT, self.default_sigint_handler)
        signal.signal(signal.SIGTERM, self.default_sigterm_handler)

    def test_operational_error_is_caught(self):
        """Test that OperationalError raised in execute is dealt with."""
        patch = mock.patch(
            "django.core.management.BaseCommand.execute",
            side_effect=DatabaseError,
        )
        patch.start()
        self.addCleanup(patch.stop)

        err = StringIO()
        command = DebusineBaseCommand(stderr=err)

        with self.assertRaisesSystemExit(3):
            command.execute()

        self.assertIn("Error connecting to the database", err.getvalue())

    def test_signal_handlers_sigint_sigterm_setup(self):
        """Test DebusineBaseCommand set SIGINT/SIGTERM handlers."""

        class DebusineSignalSpy(DebusineBaseCommand):
            def handle(self, *args, **kwargs):
                self.sigint_handler = signal.getsignal(signal.SIGINT)
                self.sigterm_handler = signal.getsignal(signal.SIGTERM)

        debusine_command = DebusineSignalSpy()

        # DebusineBaseCommand.__init__ does not change the signal handlers
        self.assertEqual(
            signal.getsignal(signal.SIGINT), self.default_sigint_handler
        )
        self.assertEqual(
            signal.getsignal(signal.SIGTERM), self.default_sigterm_handler
        )

        debusine_command.run_from_argv(["debusine-admin", "test-command"])

        # DebusineBaseCommand.execute() set the handlers correctly (at the time
        # that DebusineSignalSpy.handle() was called the handlers were correctly
        # set)
        self.assertEqual(
            debusine_command.sigint_handler, DebusineBaseCommand._exit_handler
        )
        self.assertEqual(
            debusine_command.sigterm_handler, DebusineBaseCommand._exit_handler
        )

        # It is a global change
        self.assertEqual(
            signal.getsignal(signal.SIGINT), DebusineBaseCommand._exit_handler
        )
        self.assertEqual(
            signal.getsignal(signal.SIGTERM), DebusineBaseCommand._exit_handler
        )

    def test_exit_handler(self):
        """DebusineBaseCommand.exit_handler() raises SystemExit(3)."""
        with self.assertRaisesSystemExit(3):
            DebusineBaseCommand._exit_handler(None, None)
