# Copyright 2024 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.

"""Unit tests for task models."""
from datetime import datetime, timezone
from unittest import SkipTest, TestCase

try:
    import pydantic.v1 as pydantic
except ImportError:
    import pydantic as pydantic  # type: ignore

import debusine.tasks.models as task_models


class EnumTests(TestCase):
    """Tests for SystemImageBuild task data."""

    def test_enums_str(self):
        """Test enum stringification."""
        for enum_cls in (
            task_models.AutopkgtestNeedsInternet,
            task_models.BlhcFlags,
            task_models.DiskImageFormat,
            task_models.LintianFailOnSeverity,
            task_models.MmDebstrapVariant,
            task_models.SbuildBuildComponent,
            task_models.SystemBootstrapRepositoryCheckSignatureWith,
            task_models.SystemBootstrapRepositoryType,
            task_models.SystemBootstrapOptionsVariant,
        ):
            with self.subTest(enum_cls=enum_cls):
                for el in enum_cls:
                    with self.subTest(el=el):
                        self.assertEqual(str(el), el.value)


class LintianDataTests(TestCase):
    """Tests for Lintian task data."""

    def test_input_validation(self):
        """Test LintianInput validation."""
        task_models.LintianInput(source_artifact_id=1)
        task_models.LintianInput(binary_artifacts_ids=[1])
        task_models.LintianInput(binary_artifacts_ids=[1, 2])

        error_msg = (
            "One of source_artifact_id or binary_artifacts_ids must be set"
        )
        with self.assertRaisesRegex(ValueError, error_msg):
            task_models.LintianInput()
        with self.assertRaisesRegex(ValueError, error_msg):
            task_models.LintianInput(binary_artifacts_ids=[])


class SystemBootstrapDataTests(TestCase):
    """Tests for SystemBootstrap task data."""

    def test_repository_validation(self):
        """Test SystemBootstrapRepository validation."""
        common_kwargs = {
            "mirror": "https://deb.debian.org/deb",
            "suite": "bookworm",
        }

        task_models.SystemBootstrapRepository(**common_kwargs)

        error_msg = "ensure this value has at least 1 items"
        with self.assertRaisesRegex(ValueError, error_msg):
            task_models.SystemBootstrapRepository(types=[], **common_kwargs)

        task_models.SystemBootstrapRepository(
            check_signature_with="external",
            keyring={"url": "https://example.com/keyring_file.txt"},
            **common_kwargs,
        )

        error_msg = (
            "repository requires 'keyring':"
            " 'check_signature_with' is set to 'external'"
        )
        with self.assertRaisesRegex(ValueError, error_msg):
            task_models.SystemBootstrapRepository(
                check_signature_with="external", **common_kwargs
            )

    def test_repository_validation_duplicate_list_items(self):
        """Test SystemBootstrapRepository validation of duplicate list items."""
        # This is separate from test_repository_validation in order to be
        # skipped for bullseye. Enforcement of unique values in list was only
        # introduced from pydantic 1.9.0, while bullseye has 1.7.4-1.
        # This is a minor check that we have decided is not worth the effort to
        # support in bullseye.
        if pydantic.VERSION.startswith(("1.7.", "1.8.")):  # pragma: no cover
            raise SkipTest("feature not present in bullseye")

        common_kwargs = {
            "mirror": "https://deb.debian.org/deb",
            "suite": "bookworm",
        }

        task_models.SystemBootstrapRepository(**common_kwargs)

        error_msg = "the list has duplicated items"
        with self.assertRaisesRegex(ValueError, error_msg):
            task_models.SystemBootstrapRepository(
                types=["deb", "deb"], **common_kwargs
            )
        with self.assertRaisesRegex(ValueError, error_msg):
            task_models.SystemBootstrapRepository(
                components=["main", "main"], **common_kwargs
            )


class AutopkgtestDataTests(TestCase):
    """Tests for Autopkgtest task data."""

    def test_validation(self):
        """Test AutopkgtestData validation."""
        common_kwargs = {
            "input": {
                "source_artifact_id": 1,
                "binary_artifacts_ids": [1, 2],
            },
            "host_architecture": "amd64",
            "environment_id": 1,
        }

        task_models.AutopkgtestData(**common_kwargs)

        for field in ("global", "factor", "short", "install", "test", "copy"):
            with self.subTest(field=field):
                error_msg = (
                    f"timeout -> {field}\n"
                    f"  ensure this value is greater than or equal to 0"
                )
                with self.assertRaisesRegex(ValueError, error_msg):
                    task_models.AutopkgtestData(
                        timeout={field: -1}, **common_kwargs
                    )


class ImageCacheUsageLogEntryTests(TestCase):
    """Tests for ImageCacheUsageLogEntry."""

    def test_timestamp_validation_succeeds(self):
        """Test ImageCacheUsageLogEntry accepts aware timestamp."""
        task_models.ImageCacheUsageLogEntry(
            filename="foo", timestamp=datetime.now(timezone.utc)
        )

    def test_timestamp_validation_fails(self):
        """Test ImageCacheUsageLogEntry rejects naive timestamp."""
        with self.assertRaisesRegex(ValueError, "timestamp is TZ-naive"):
            task_models.ImageCacheUsageLogEntry(
                filename="foo", timestamp=datetime.now()
            )


class ImageCacheUsageLogTests(TestCase):
    """Tests for ImageCacheUsageLog."""

    def test_version_validation_succeeds(self):
        """Test ImageCacheUsageLog accepts version 1."""
        task_models.ImageCacheUsageLog(version=1)

    def test_version_validation_fails(self):
        """Test ImageCacheUsageLog rejects version 99."""
        with self.assertRaisesRegex(ValueError, "Unknown usage log version 99"):
            task_models.ImageCacheUsageLog(version=99)
