"""Tests for core startup.py functions.

Covers the highest-priority untested areas:
  1. load_token  — missing / malformed credentials file
  2. write_or_update_twin_json_file — deep-merge preserves existing keys
  3. write_or_update_twin_json_file — directory at path replaced by file
  4. load_environment_uuid — retry logic
  5. _remove_cached_twin_json_files — protected files never deleted
"""
from __future__ import annotations

import io
import itertools
import json
import logging
import os
import subprocess
import uuid as _uuid_module

import cyberwave_edge_core.startup as startup

# ===========================================================================
# 0. config dir resolution
# ===========================================================================


class TestResolveConfigDir:
    def test_env_override_takes_precedence(self, monkeypatch):
        monkeypatch.setenv("CYBERWAVE_EDGE_CONFIG_DIR", "/tmp/cw-custom")
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")

        assert startup._resolve_config_dir().as_posix() == "/tmp/cw-custom"

    def test_macos_uses_invoking_user_home_when_running_via_sudo(self, monkeypatch):
        monkeypatch.delenv("CYBERWAVE_EDGE_CONFIG_DIR", raising=False)
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        monkeypatch.setattr(
            startup, "_resolve_sudo_user_home", lambda: startup.Path("/Users/alice")
        )
        monkeypatch.setattr(startup.Path, "home", lambda: startup.Path("/var/root"))

        assert startup._resolve_config_dir() == startup.Path("/Users/alice/.cyberwave")

    def test_linux_default_remains_etc_cyberwave(self, monkeypatch):
        monkeypatch.delenv("CYBERWAVE_EDGE_CONFIG_DIR", raising=False)
        monkeypatch.setattr(startup.platform, "system", lambda: "Linux")

        assert startup._resolve_config_dir() == startup.Path("/etc/cyberwave")

    def test_migrate_legacy_macos_config_copies_json_files(self, tmp_path, monkeypatch):
        monkeypatch.delenv("CYBERWAVE_EDGE_CONFIG_DIR", raising=False)
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        legacy_dir = tmp_path / "legacy"
        target_dir = tmp_path / "new"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text('{"token":"abc"}')
        (legacy_dir / "environment.json").write_text('{"uuid":"123"}')
        monkeypatch.setattr(startup, "_LEGACY_MACOS_CONFIG_DIR", legacy_dir)

        startup._migrate_legacy_macos_config(target_dir)

        assert (target_dir / "credentials.json").exists()
        assert (target_dir / "environment.json").exists()

    def test_migrate_legacy_macos_config_skips_when_env_override_set(self, tmp_path, monkeypatch):
        monkeypatch.setenv("CYBERWAVE_EDGE_CONFIG_DIR", str(tmp_path / "custom"))
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        legacy_dir = tmp_path / "legacy"
        target_dir = tmp_path / "new"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text('{"token":"abc"}')
        monkeypatch.setattr(startup, "_LEGACY_MACOS_CONFIG_DIR", legacy_dir)

        startup._migrate_legacy_macos_config(target_dir)

        assert not (target_dir / "credentials.json").exists()

    def test_migrate_legacy_macos_config_skips_when_new_credentials_already_exist(
        self, tmp_path, monkeypatch
    ):
        monkeypatch.delenv("CYBERWAVE_EDGE_CONFIG_DIR", raising=False)
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        legacy_dir = tmp_path / "legacy"
        target_dir = tmp_path / "new"
        legacy_dir.mkdir()
        target_dir.mkdir()
        (legacy_dir / "credentials.json").write_text('{"token":"legacy"}')
        (legacy_dir / "environment.json").write_text('{"uuid":"legacy-env"}')
        (target_dir / "credentials.json").write_text('{"token":"new"}')
        monkeypatch.setattr(startup, "_LEGACY_MACOS_CONFIG_DIR", legacy_dir)

        startup._migrate_legacy_macos_config(target_dir)

        assert (target_dir / "credentials.json").read_text() == '{"token":"new"}'
        assert not (target_dir / "environment.json").exists()

    def test_migrate_legacy_macos_config_does_not_overwrite_existing_target_json(
        self, tmp_path, monkeypatch
    ):
        monkeypatch.delenv("CYBERWAVE_EDGE_CONFIG_DIR", raising=False)
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        legacy_dir = tmp_path / "legacy"
        target_dir = tmp_path / "new"
        legacy_dir.mkdir()
        target_dir.mkdir()
        (legacy_dir / "credentials.json").write_text('{"token":"legacy"}')
        (legacy_dir / "environment.json").write_text('{"uuid":"legacy-env"}')
        (target_dir / "environment.json").write_text('{"uuid":"new-env"}')
        monkeypatch.setattr(startup, "_LEGACY_MACOS_CONFIG_DIR", legacy_dir)

        startup._migrate_legacy_macos_config(target_dir)

        assert (target_dir / "credentials.json").exists()
        assert (target_dir / "environment.json").read_text() == '{"uuid":"new-env"}'


# ===========================================================================
# 0b. startup env bootstrap
# ===========================================================================


class TestBootstrapRuntimeEnvVars:
    def test_bootstrap_loads_envs_from_migrated_macos_credentials(self, tmp_path, monkeypatch):
        monkeypatch.delenv("CYBERWAVE_EDGE_CONFIG_DIR", raising=False)
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        monkeypatch.delenv("CYBERWAVE_BASE_URL", raising=False)
        monkeypatch.delenv("CYBERWAVE_MQTT_HOST", raising=False)

        legacy_dir = tmp_path / "legacy"
        target_dir = tmp_path / "new"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text(
            json.dumps(
                {
                    "envs": {
                        "CYBERWAVE_BASE_URL": " https://api.example.com ",
                        "CYBERWAVE_MQTT_HOST": " mqtt.example.com ",
                    }
                }
            )
        )
        monkeypatch.setattr(startup, "_LEGACY_MACOS_CONFIG_DIR", legacy_dir)
        monkeypatch.setattr(startup, "_resolve_config_dir", lambda: target_dir)

        startup._bootstrap_runtime_env_vars()

        assert os.getenv("CYBERWAVE_BASE_URL") == "https://api.example.com"
        assert os.getenv("CYBERWAVE_MQTT_HOST") == "mqtt.example.com"
        assert (target_dir / "credentials.json").exists()

    def test_bootstrap_does_not_overwrite_existing_process_env(self, tmp_path, monkeypatch):
        monkeypatch.delenv("CYBERWAVE_EDGE_CONFIG_DIR", raising=False)
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        monkeypatch.setenv("CYBERWAVE_BASE_URL", "https://already-set.example.com")

        legacy_dir = tmp_path / "legacy"
        target_dir = tmp_path / "new"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text(
            json.dumps({"envs": {"CYBERWAVE_BASE_URL": "https://from-file.example.com"}})
        )
        monkeypatch.setattr(startup, "_LEGACY_MACOS_CONFIG_DIR", legacy_dir)
        monkeypatch.setattr(startup, "_resolve_config_dir", lambda: target_dir)

        startup._bootstrap_runtime_env_vars()

        assert os.getenv("CYBERWAVE_BASE_URL") == "https://already-set.example.com"

    def test_bootstrap_ignores_blank_and_non_string_env_values(self, tmp_path, monkeypatch):
        monkeypatch.delenv("CYBERWAVE_EDGE_CONFIG_DIR", raising=False)
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        monkeypatch.delenv("CW_BOOTSTRAP_VALID", raising=False)
        monkeypatch.delenv("CW_BOOTSTRAP_VALID_2", raising=False)
        monkeypatch.delenv("CW_BOOTSTRAP_BLANK", raising=False)
        monkeypatch.delenv("CW_BOOTSTRAP_NON_STRING", raising=False)
        monkeypatch.delenv("CW_BOOTSTRAP_LIST", raising=False)

        legacy_dir = tmp_path / "legacy"
        target_dir = tmp_path / "new"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text(
            json.dumps(
                {
                    "envs": {
                        "CW_BOOTSTRAP_VALID": "  value-one  ",
                        "CW_BOOTSTRAP_VALID_2": "\tvalue-two\n",
                        "CW_BOOTSTRAP_BLANK": "   ",
                        "CW_BOOTSTRAP_NON_STRING": 123,
                        "CW_BOOTSTRAP_LIST": ["x"],
                    }
                }
            )
        )
        monkeypatch.setattr(startup, "_LEGACY_MACOS_CONFIG_DIR", legacy_dir)
        monkeypatch.setattr(startup, "_resolve_config_dir", lambda: target_dir)

        startup._bootstrap_runtime_env_vars()

        assert os.getenv("CW_BOOTSTRAP_VALID") == "value-one"
        assert os.getenv("CW_BOOTSTRAP_VALID_2") == "value-two"
        assert os.getenv("CW_BOOTSTRAP_BLANK") is None
        assert os.getenv("CW_BOOTSTRAP_NON_STRING") is None
        assert os.getenv("CW_BOOTSTRAP_LIST") is None


# ===========================================================================
# 1. load_token
# ===========================================================================


class TestLoadToken:
    def test_returns_none_when_credentials_file_absent(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CREDENTIALS_FILE", tmp_path / "credentials.json")
        assert startup.load_token() is None

    def test_returns_none_when_token_key_missing(self, tmp_path, monkeypatch):
        creds = tmp_path / "credentials.json"
        creds.write_text(json.dumps({"envs": {}}))
        monkeypatch.setattr(startup, "CREDENTIALS_FILE", creds)
        assert startup.load_token() is None

    def test_returns_none_when_token_is_empty_string(self, tmp_path, monkeypatch):
        creds = tmp_path / "credentials.json"
        creds.write_text(json.dumps({"token": ""}))
        monkeypatch.setattr(startup, "CREDENTIALS_FILE", creds)
        assert startup.load_token() is None

    def test_returns_none_when_json_is_malformed(self, tmp_path, monkeypatch):
        creds = tmp_path / "credentials.json"
        creds.write_text("{ not valid json }")
        monkeypatch.setattr(startup, "CREDENTIALS_FILE", creds)
        assert startup.load_token() is None

    def test_returns_token_when_file_is_valid(self, tmp_path, monkeypatch):
        creds = tmp_path / "credentials.json"
        creds.write_text(json.dumps({"token": "my-secret-token"}))
        monkeypatch.setattr(startup, "CREDENTIALS_FILE", creds)
        assert startup.load_token() == "my-secret-token"

    def test_logs_loaded_token_only_once_for_unchanged_credentials(
        self, tmp_path, monkeypatch, caplog
    ):
        creds = tmp_path / "credentials.json"
        creds.write_text(json.dumps({"token": "my-secret-token"}))
        monkeypatch.setattr(startup, "CREDENTIALS_FILE", creds)
        startup._reset_logged_token_signature()

        with caplog.at_level(logging.INFO, logger=startup.logger.name):
            assert startup.load_token() == "my-secret-token"
            assert startup.load_token() == "my-secret-token"

        loaded_messages = [
            record.message for record in caplog.records if "Loaded token from" in record.message
        ]
        assert len(loaded_messages) == 1


# ===========================================================================
# 2. write_or_update_twin_json_file — deep-merge preserves existing keys
# ===========================================================================


class TestWriteOrUpdateTwinJsonFileDeepMerge:
    _TWIN_UUID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"

    def test_creates_new_file_with_asset_embedded(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        result = startup.write_or_update_twin_json_file(
            self._TWIN_UUID, {"name": "robot"}, {"model": "x1"}
        )
        assert result is True
        written = json.loads((tmp_path / f"{self._TWIN_UUID}.json").read_text())
        assert written["name"] == "robot"
        assert written["asset"] == {"model": "x1"}

    def test_merge_preserves_locally_set_keys(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        existing = {
            "name": "old-name",
            "metadata": {"sensors_devices": {"camera": "/dev/video0"}, "extra": "keep-me"},
            "local_only_key": "do-not-lose",
        }
        (tmp_path / f"{self._TWIN_UUID}.json").write_text(json.dumps(existing))

        startup.write_or_update_twin_json_file(
            self._TWIN_UUID,
            {"name": "new-name", "metadata": {"sensors_devices": {"camera": "/dev/video1"}}},
            {},
        )
        written = json.loads((tmp_path / f"{self._TWIN_UUID}.json").read_text())

        # New value wins for updated keys
        assert written["name"] == "new-name"
        assert written["metadata"]["sensors_devices"]["camera"] == "/dev/video1"
        # Locally-set sibling key inside the nested dict is preserved
        assert written["metadata"]["extra"] == "keep-me"
        # Top-level local-only key is preserved
        assert written["local_only_key"] == "do-not-lose"

    def test_merge_overwrites_scalars_not_dicts(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        existing = {"x": 1, "nested": {"a": 1, "b": 2}}
        (tmp_path / f"{self._TWIN_UUID}.json").write_text(json.dumps(existing))

        startup.write_or_update_twin_json_file(
            self._TWIN_UUID, {"x": 99, "nested": {"a": 42}}, {}
        )
        written = json.loads((tmp_path / f"{self._TWIN_UUID}.json").read_text())
        assert written["x"] == 99
        assert written["nested"]["a"] == 42
        # "b" existed in existing nested dict and was not in override → preserved
        assert written["nested"]["b"] == 2

    def test_existing_file_is_updated_in_place(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        twin_json_path = tmp_path / f"{self._TWIN_UUID}.json"
        twin_json_path.write_text(json.dumps({"name": "stable", "asset": {"model": "x1"}}))
        original_inode = twin_json_path.stat().st_ino

        startup.write_or_update_twin_json_file(
            self._TWIN_UUID,
            {"name": "updated"},
            {"model": "x2"},
        )

        written = json.loads(twin_json_path.read_text())
        assert twin_json_path.stat().st_ino == original_inode
        assert written == {"name": "updated", "asset": {"model": "x2"}}

    def test_existing_file_remains_valid_when_atomic_write_fails(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        twin_json_path = tmp_path / f"{self._TWIN_UUID}.json"
        twin_json_path.write_text(json.dumps({"name": "stable", "asset": {"model": "x1"}}))

        original_json_dumps = startup.json.dumps

        def _failing_dumps(data, **kwargs):  # type: ignore[no-untyped-def]
            raise TypeError("boom")

        monkeypatch.setattr(startup.json, "dumps", _failing_dumps)

        try:
            startup.write_or_update_twin_json_file(
                self._TWIN_UUID,
                {"name": "new-name"},
                {"model": "x2"},
            )
        except TypeError as exc:
            assert str(exc) == "boom"
        else:
            raise AssertionError("Expected write_or_update_twin_json_file to propagate TypeError")
        finally:
            monkeypatch.setattr(startup.json, "dumps", original_json_dumps)

        written = json.loads(twin_json_path.read_text())
        assert written == {"name": "stable", "asset": {"model": "x1"}}
        assert not list(tmp_path.glob(f"{self._TWIN_UUID}.*.tmp"))


# ===========================================================================
# 3. write_or_update_twin_json_file — directory at path replaced by file
# ===========================================================================


class TestWriteOrUpdateTwinJsonFileDirectoryReplacement:
    _TWIN_UUID = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"

    def test_directory_at_path_is_removed_and_file_written(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        # Simulate the Docker bind-mount artifact: a directory where the file should be
        dir_path = tmp_path / f"{self._TWIN_UUID}.json"
        dir_path.mkdir()
        (dir_path / "dummy").write_text("content")

        assert dir_path.is_dir()
        result = startup.write_or_update_twin_json_file(
            self._TWIN_UUID, {"name": "robot"}, {"model": "x1"}
        )
        assert result is True
        assert dir_path.is_file(), "Directory should have been replaced by a regular file"
        written = json.loads(dir_path.read_text())
        assert written["name"] == "robot"


# ===========================================================================
# 4. load_environment_uuid — retry logic
# ===========================================================================


class TestLoadEnvironmentUuid:
    _VALID_UUID = "12345678-1234-5678-1234-567812345678"

    def test_returns_none_when_file_absent(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "ENVIRONMENT_FILE", tmp_path / "environment.json")
        assert startup.load_environment_uuid() is None

    def test_returns_none_for_invalid_uuid_format(self, tmp_path, monkeypatch):
        env_file = tmp_path / "environment.json"
        env_file.write_text(json.dumps({"uuid": "not-a-uuid"}))
        monkeypatch.setattr(startup, "ENVIRONMENT_FILE", env_file)
        assert startup.load_environment_uuid() is None

    def test_normalises_uuid_to_lowercase_canonical_form(self, tmp_path, monkeypatch):
        env_file = tmp_path / "environment.json"
        env_file.write_text(json.dumps({"uuid": self._VALID_UUID.upper()}))
        monkeypatch.setattr(startup, "ENVIRONMENT_FILE", env_file)
        result = startup.load_environment_uuid()
        assert result == self._VALID_UUID.lower()

    def test_returns_none_when_uuid_field_missing_no_retries(self, tmp_path, monkeypatch):
        env_file = tmp_path / "environment.json"
        env_file.write_text(json.dumps({}))
        monkeypatch.setattr(startup, "ENVIRONMENT_FILE", env_file)
        monkeypatch.setattr(startup.time, "sleep", lambda _: None)
        assert startup.load_environment_uuid(retries=0) is None

    def test_retries_until_file_becomes_valid(self, tmp_path, monkeypatch):
        """Simulate a race: file is written mid-boot and becomes valid on attempt 2."""
        env_file = tmp_path / "environment.json"
        # Start with an empty uuid field
        env_file.write_text(json.dumps({"uuid": ""}))
        monkeypatch.setattr(startup, "ENVIRONMENT_FILE", env_file)

        call_count = 0

        def _side_effect_sleep(seconds: float) -> None:
            nonlocal call_count
            call_count += 1
            # On first sleep (between attempt 1 and 2), write the valid UUID
            if call_count == 1:
                env_file.write_text(json.dumps({"uuid": self._VALID_UUID}))

        monkeypatch.setattr(startup.time, "sleep", _side_effect_sleep)

        result = startup.load_environment_uuid(retries=2, retry_delay_seconds=0.0)
        assert result == self._VALID_UUID
        assert call_count == 1, "sleep should have been called exactly once (one retry)"

    def test_no_sleep_when_valid_on_first_attempt(self, tmp_path, monkeypatch):
        env_file = tmp_path / "environment.json"
        env_file.write_text(json.dumps({"uuid": self._VALID_UUID}))
        monkeypatch.setattr(startup, "ENVIRONMENT_FILE", env_file)

        sleep_calls: list[float] = []
        monkeypatch.setattr(startup.time, "sleep", lambda s: sleep_calls.append(s))

        result = startup.load_environment_uuid(retries=3, retry_delay_seconds=0.1)
        assert result == self._VALID_UUID
        assert sleep_calls == [], "sleep must not be called when the file is valid immediately"


# ===========================================================================
# 5. _remove_cached_twin_json_files — protected files never deleted
# ===========================================================================


class TestRemoveCachedTwinJsonFiles:
    def test_protected_files_are_never_deleted(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        for protected_name in startup._PROTECTED_CONFIG_JSON_FILES:
            (tmp_path / protected_name).write_text("{}")

        removed = startup._remove_cached_twin_json_files()

        assert removed == [], "No files should be removed when only protected files exist"
        for protected_name in startup._PROTECTED_CONFIG_JSON_FILES:
            assert (tmp_path / protected_name).exists(), f"{protected_name} must not be deleted"

    def test_uuid_named_files_are_removed(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        twin_uuid = str(_uuid_module.uuid4())
        twin_file = tmp_path / f"{twin_uuid}.json"
        twin_file.write_text("{}")

        removed = startup._remove_cached_twin_json_files()

        assert f"{twin_uuid}.json" in removed
        assert not twin_file.exists()

    def test_non_uuid_named_files_are_not_deleted(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        custom_file = tmp_path / "my-custom-config.json"
        custom_file.write_text("{}")

        removed = startup._remove_cached_twin_json_files()

        assert removed == []
        assert custom_file.exists(), "Non-UUID named files must not be deleted"

    def test_returns_only_removed_filenames(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        uuid1 = str(_uuid_module.uuid4())
        uuid2 = str(_uuid_module.uuid4())
        (tmp_path / f"{uuid1}.json").write_text("{}")
        (tmp_path / f"{uuid2}.json").write_text("{}")
        (tmp_path / "credentials.json").write_text("{}")  # protected — must survive

        removed = startup._remove_cached_twin_json_files()

        assert set(removed) == {f"{uuid1}.json", f"{uuid2}.json"}
        assert (tmp_path / "credentials.json").exists()


# ===========================================================================
# 6. reconcile_driver_restart_failures — flapping driver detection
# ===========================================================================


class TestReconcileDriverRestartFailures:
    def test_stops_and_alerts_when_restart_threshold_is_exceeded(self, monkeypatch):
        container_name = "cyberwave-driver-1234abcd"
        twin_uuid = "11111111-1111-1111-1111-111111111111"
        restart_counts = iter([0, 1, 2, 3, 4, 5])
        timestamps = itertools.count(start=0, step=10)

        startup._CONTAINER_LAST_RESTART_COUNT.clear()
        startup._CONTAINER_RESTART_HISTORY.clear()
        startup._CONTAINER_TWIN_MAP.clear()
        monkeypatch.setattr(startup, "DRIVER_RESTART_LOOP_THRESHOLD", 4)
        monkeypatch.setattr(startup, "DRIVER_RESTART_LOOP_WINDOW_SECONDS", 60.0)
        monkeypatch.setattr(
            startup,
            "_list_driver_containers",
            lambda include_stopped: [container_name],
        )
        monkeypatch.setattr(startup.time, "time", lambda: float(next(timestamps)))
        monkeypatch.setattr(
            startup,
            "_inspect_driver_container",
            lambda _name: {
                "RestartCount": next(restart_counts),
                "State": {"Status": "restarting", "Error": "camera unavailable"},
                "Config": {"Env": [f"CYBERWAVE_TWIN_UUID={twin_uuid}"]},
            },
        )

        stopped: list[str] = []
        alerts: list[tuple] = []
        monkeypatch.setattr(
            startup,
            "_stop_driver_container",
            lambda name: stopped.append(name) or True,
        )
        monkeypatch.setattr(
            startup,
            "_send_alert_for_twin",
            lambda *args, **kwargs: alerts.append((args, kwargs)),
        )

        for _ in range(6):
            startup.reconcile_driver_restart_failures()

        assert stopped == [container_name]
        assert len(alerts) == 1
        assert alerts[0][0][0] == twin_uuid
        assert alerts[0][0][3] == "driver_restart_loop"

    def test_does_not_alert_for_sparse_restarts_outside_window(self, monkeypatch):
        container_name = "cyberwave-driver-1234abcd"
        twin_uuid = "22222222-2222-2222-2222-222222222222"
        restart_counts = iter([0, 1, 2, 3, 4, 5])
        timestamps = itertools.count(start=0, step=70)

        startup._CONTAINER_LAST_RESTART_COUNT.clear()
        startup._CONTAINER_RESTART_HISTORY.clear()
        startup._CONTAINER_TWIN_MAP.clear()
        monkeypatch.setattr(startup, "DRIVER_RESTART_LOOP_THRESHOLD", 4)
        monkeypatch.setattr(startup, "DRIVER_RESTART_LOOP_WINDOW_SECONDS", 60.0)
        monkeypatch.setattr(
            startup,
            "_list_driver_containers",
            lambda include_stopped: [container_name],
        )
        monkeypatch.setattr(startup.time, "time", lambda: float(next(timestamps)))
        monkeypatch.setattr(
            startup,
            "_inspect_driver_container",
            lambda _name: {
                "RestartCount": next(restart_counts),
                "State": {"Status": "restarting", "Error": ""},
                "Config": {"Env": [f"CYBERWAVE_TWIN_UUID={twin_uuid}"]},
            },
        )

        stopped: list[str] = []
        alerts: list[tuple] = []
        monkeypatch.setattr(
            startup,
            "_stop_driver_container",
            lambda name: stopped.append(name) or True,
        )
        monkeypatch.setattr(
            startup,
            "_send_alert_for_twin",
            lambda *args, **kwargs: alerts.append((args, kwargs)),
        )

        for _ in range(6):
            startup.reconcile_driver_restart_failures()

        assert stopped == []
        assert alerts == []


# ===========================================================================
# 7. _run_docker_image pull behavior with local fallback
# ===========================================================================


class TestRunDockerImagePullFallback:
    _TWIN_UUID = "99999999-9999-9999-9999-999999999999"

    def _patch_common(self, tmp_path, monkeypatch):
        monkeypatch.setattr(startup, "CONFIG_DIR", tmp_path)
        monkeypatch.setattr(startup.shutil, "which", lambda name: "/usr/bin/docker")
        monkeypatch.setattr(startup, "load_credentials_envs", lambda: {})
        monkeypatch.setattr(startup, "get_runtime_env_var", lambda *args, **kwargs: None)
        monkeypatch.setattr(startup.time, "sleep", lambda _: None)
        monkeypatch.setattr(
            startup,
            "_inspect_driver_container",
            lambda _name: {"State": {"Status": "running", "Error": ""}},
        )
        monkeypatch.setattr(startup, "_stream_container_logs", lambda *args, **kwargs: None)

    @staticmethod
    def _extract_env_map(docker_run_cmd: list[str]) -> dict[str, str]:
        env_map: dict[str, str] = {}
        for idx, arg in enumerate(docker_run_cmd):
            if arg != "-e" or idx + 1 >= len(docker_run_cmd):
                continue
            key, sep, value = docker_run_cmd[idx + 1].partition("=")
            if sep:
                env_map[key] = value
        return env_map

    def test_uses_local_image_when_pull_fails(self, tmp_path, monkeypatch):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        pull_calls: list[str] = []

        def _fake_pull(*args, **kwargs):  # type: ignore[no-untyped-def]
            pull_calls.append("pull")
            raise subprocess.CalledProcessError(
                returncode=1,
                cmd=["docker", "pull", "cyberwave-step14-driver:latest"],
                stderr="pull access denied",
            )

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            if cmd[:3] == ["docker", "image", "inspect"]:
                return subprocess.CompletedProcess(cmd, 0, stdout="[]", stderr="")
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(startup, "_pull_docker_image_with_progress", _fake_pull)
        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            [],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
        )

        assert success is True
        assert pull_calls == ["pull"]
        assert any(cmd[:3] == ["docker", "image", "inspect"] for cmd in commands)
        assert any(cmd[:2] == ["docker", "run"] for cmd in commands)

    def test_fails_when_pull_fails_and_image_missing_locally(self, tmp_path, monkeypatch):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        pull_calls: list[str] = []

        def _fake_pull(*args, **kwargs):  # type: ignore[no-untyped-def]
            pull_calls.append("pull")
            raise subprocess.CalledProcessError(
                returncode=1,
                cmd=["docker", "pull", "cyberwave-step14-driver:latest"],
                stderr="pull access denied",
            )

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            if cmd[:3] == ["docker", "image", "inspect"]:
                raise subprocess.CalledProcessError(
                    returncode=1,
                    cmd=cmd,
                    stderr="No such image",
                )
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(startup, "_pull_docker_image_with_progress", _fake_pull)
        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            [],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
        )

        assert success is False
        assert pull_calls == ["pull"]
        assert not any(cmd[:2] == ["docker", "run"] for cmd in commands)

    def test_forwards_process_cyberwave_env_vars_to_driver_container(self, tmp_path, monkeypatch):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        monkeypatch.setenv("CYBERWAVE_GO2_IP_ADDR", " 192.168.0.10 ")
        monkeypatch.setenv("CYBERWAVE_EMPTY", "   ")
        monkeypatch.setenv("GO2_IP_ADDR", "192.168.0.10")

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(
            startup, "_pull_docker_image_with_progress", lambda *args, **kwargs: None
        )
        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            [],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
        )

        assert success is True
        docker_run_cmd = next(cmd for cmd in commands if cmd[:2] == ["docker", "run"])
        env_map = self._extract_env_map(docker_run_cmd)
        assert env_map["CYBERWAVE_GO2_IP_ADDR"] == "192.168.0.10"
        assert "CYBERWAVE_EMPTY" not in env_map
        assert "GO2_IP_ADDR" not in env_map

    def test_process_env_does_not_override_credentials_env_values(self, tmp_path, monkeypatch):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        monkeypatch.setattr(
            startup,
            "load_credentials_envs",
            lambda: {
                "CYBERWAVE_GO2_IP_ADDR": "10.0.0.2",
                "CYBERWAVE_REGION": "eu-west-1",
            },
        )
        monkeypatch.setenv("CYBERWAVE_GO2_IP_ADDR", "192.168.0.10")
        monkeypatch.setenv("CYBERWAVE_REGION", "us-east-1")
        monkeypatch.setenv("CYBERWAVE_EXTRA", "enabled")

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(
            startup, "_pull_docker_image_with_progress", lambda *args, **kwargs: None
        )
        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            [],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
        )

        assert success is True
        docker_run_cmd = next(cmd for cmd in commands if cmd[:2] == ["docker", "run"])
        env_map = self._extract_env_map(docker_run_cmd)
        assert env_map["CYBERWAVE_GO2_IP_ADDR"] == "10.0.0.2"
        assert env_map["CYBERWAVE_REGION"] == "eu-west-1"
        assert env_map["CYBERWAVE_EXTRA"] == "enabled"

    def test_runs_macos_bridge_command_before_docker_run(self, tmp_path, monkeypatch):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")

        def _runtime_env(name, default=None):  # type: ignore[no-untyped-def]
            if name == "CYBERWAVE_MACOS_DEVICE_BRIDGE_COMMAND":
                return (
                    "/bin/echo bridge "
                    "{host_device} {container_device} {twin_uuid} {container_name}"
                )
            return default

        monkeypatch.setattr(startup, "get_runtime_env_var", _runtime_env)

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            ["--device", "/dev/video0:/dev/video0"],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
        )

        assert success is True
        bridge_cmd = next(cmd for cmd in commands if cmd and cmd[0] == "/bin/echo")
        assert "bridge" in bridge_cmd
        assert "/dev/video0" in bridge_cmd
        docker_run_cmd = next(cmd for cmd in commands if cmd[:2] == ["docker", "run"])
        assert "--network" not in docker_run_cmd
        assert "host" not in docker_run_cmd
        assert "--add-host" in docker_run_cmd
        assert "host.docker.internal:host-gateway" in docker_run_cmd
        env_map = self._extract_env_map(docker_run_cmd)
        assert env_map["CYBERWAVE_EDGE_HOST_PLATFORM"] == "darwin"

    def test_macos_bridge_command_failure_aborts_driver_start(self, tmp_path, monkeypatch):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")

        def _runtime_env(name, default=None):  # type: ignore[no-untyped-def]
            if name == "CYBERWAVE_MACOS_DEVICE_BRIDGE_COMMAND":
                return "/bin/false {host_device}"
            return default

        monkeypatch.setattr(startup, "get_runtime_env_var", _runtime_env)

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            if cmd and cmd[0] == "/bin/false":
                raise subprocess.CalledProcessError(returncode=1, cmd=cmd, stderr="failed")
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            ["--device", "/dev/ttyACM0:/dev/ttyACM0"],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
        )

        assert success is False
        assert any(cmd and cmd[0] == "/bin/false" for cmd in commands)
        assert not any(cmd[:2] == ["docker", "run"] for cmd in commands)

    def test_macos_bridge_resolved_source_sets_video_env_and_strips_video_device(
        self, tmp_path, monkeypatch
    ):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")

        def _runtime_env(name, default=None):  # type: ignore[no-untyped-def]
            if name == "CYBERWAVE_MACOS_DEVICE_BRIDGE_COMMAND":
                return "/bin/echo resolved_device=rtsp://host.docker.internal:8554/cam0"
            if name == "CYBERWAVE_MACOS_STRIP_VIDEO_DEVICE_PARAMS":
                return "true"
            return default

        monkeypatch.setattr(startup, "get_runtime_env_var", _runtime_env)

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            if cmd and cmd[0] == "/bin/echo":
                return subprocess.CompletedProcess(
                    cmd,
                    0,
                    stdout="resolved_device=rtsp://host.docker.internal:8554/cam0\n",
                    stderr="",
                )
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            ["--device", "/dev/video0:/dev/video0"],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
        )

        assert success is True
        docker_run_cmd = next(cmd for cmd in commands if cmd[:2] == ["docker", "run"])
        assert "--device" not in docker_run_cmd
        assert "--device=/dev/video0:/dev/video0" not in docker_run_cmd
        env_map = self._extract_env_map(docker_run_cmd)
        assert env_map["CYBERWAVE_METADATA_VIDEO_DEVICE"] == "rtsp://host.docker.internal:8554/cam0"
        assert json.loads(env_map["CYBERWAVE_EDGE_VIDEO_DEVICE_MAP"]) == {
            "/dev/video0": "rtsp://host.docker.internal:8554/cam0"
        }

    def test_macos_does_not_override_explicit_video_device_env_in_params(
        self, tmp_path, monkeypatch
    ):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")

        def _runtime_env(name, default=None):  # type: ignore[no-untyped-def]
            if name == "CYBERWAVE_MACOS_DEVICE_BRIDGE_COMMAND":
                return "/bin/echo resolved_device=rtsp://host.docker.internal:8554/cam0"
            return default

        monkeypatch.setattr(startup, "get_runtime_env_var", _runtime_env)

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            if cmd and cmd[0] == "/bin/echo":
                return subprocess.CompletedProcess(
                    cmd,
                    0,
                    stdout="resolved_device=rtsp://host.docker.internal:8554/cam0\n",
                    stderr="",
                )
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            [
                "--device",
                "/dev/video0:/dev/video0",
                "-e",
                "CYBERWAVE_METADATA_VIDEO_DEVICE=/dev/video7",
            ],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
        )

        assert success is True
        docker_run_cmd = next(cmd for cmd in commands if cmd[:2] == ["docker", "run"])
        env_map = self._extract_env_map(docker_run_cmd)
        assert env_map["CYBERWAVE_METADATA_VIDEO_DEVICE"] == "/dev/video7"

    def test_macos_candidate_mapping_sets_video_env_without_device_params(
        self, tmp_path, monkeypatch
    ):
        self._patch_common(tmp_path, monkeypatch)
        commands: list[list[str]] = []
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")

        def _runtime_env(name, default=None):  # type: ignore[no-untyped-def]
            if name == "CYBERWAVE_MACOS_DEVICE_BRIDGE_COMMAND":
                return "/bin/echo resolved_device=rtsp://host.docker.internal:8554/cam-main"
            return default

        monkeypatch.setattr(startup, "get_runtime_env_var", _runtime_env)

        def _fake_run(cmd, **kwargs):  # type: ignore[no-untyped-def]
            commands.append(list(cmd))
            if cmd and cmd[0] == "/bin/echo":
                return subprocess.CompletedProcess(
                    cmd,
                    0,
                    stdout="resolved_device=rtsp://host.docker.internal:8554/cam-main\n",
                    stderr="",
                )
            return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="")

        monkeypatch.setattr(startup.subprocess, "run", _fake_run)

        success = startup._run_docker_image(
            "cyberwave-step14-driver:latest",
            [],
            twin_uuid=self._TWIN_UUID,
            token="test-token",
            macos_bridge_device_candidates=["/dev/video0"],
        )

        assert success is True
        docker_run_cmd = next(cmd for cmd in commands if cmd[:2] == ["docker", "run"])
        env_map = self._extract_env_map(docker_run_cmd)
        assert env_map["CYBERWAVE_METADATA_VIDEO_DEVICE"] == "rtsp://host.docker.internal:8554/cam-main"
        assert json.loads(env_map["CYBERWAVE_EDGE_VIDEO_DEVICE_MAP"]) == {
            "/dev/video0": "rtsp://host.docker.internal:8554/cam-main"
        }

    def test_normalize_macos_bridge_candidates_supports_custom_host_to_container_mapping(self):
        normalized = startup._normalize_macos_bridge_candidates(
            ["/dev/video2:/dev/video0", " /dev/video5 ", "", "   "]
        )

        assert normalized == [
            ("/dev/video2", "/dev/video0"),
            ("/dev/video5", "/dev/video5"),
        ]


class TestDriverSelection:
    def test_prefers_platform_specific_driver_by_machine(self, monkeypatch):
        monkeypatch.setattr(startup.platform, "system", lambda: "Darwin")
        monkeypatch.setattr(startup.platform, "machine", lambda: "arm64")

        image, params = startup._get_best_driver_image_and_params(
            {
                "default": {"docker_image": "cyberwave/default-driver:latest"},
                "macos": {"docker_image": "cyberwave/macos-driver:latest"},
                "darwin-arm64": {
                    "docker_image": "cyberwave/macos-arm-driver:latest",
                    "params": ["--log-level", "debug"],
                },
            }
        )

        assert image == "cyberwave/macos-arm-driver:latest"
        assert params == ["--log-level", "debug"]

    def test_falls_back_to_default_when_no_platform_driver_matches(self, monkeypatch):
        monkeypatch.setattr(startup.platform, "system", lambda: "Linux")
        monkeypatch.setattr(startup.platform, "machine", lambda: "x86_64")

        image, params = startup._get_best_driver_image_and_params(
            {
                "default": {"docker_image": "cyberwave/default-driver:latest"},
                "macos": {"docker_image": "cyberwave/macos-driver:latest"},
            }
        )

        assert image == "cyberwave/default-driver:latest"
        assert params == []


class TestPullDockerImageWithProgress:
    class _FakeProcess:
        def __init__(self, output: str, *, returncode: int = 0):
            self.stdout = io.StringIO(output)
            self._returncode = returncode
            self.killed = False

        def wait(self, timeout=None):  # type: ignore[no-untyped-def]
            return self._returncode

        def kill(self):
            self.killed = True

    class _FakeMQTT:
        def __init__(self):
            self.topic_prefix = "cw/"
            self.published: list[tuple[str, dict[str, object]]] = []

        def publish(self, topic, payload):  # type: ignore[no-untyped-def]
            self.published.append((topic, payload))

    class _FakeClient:
        def __init__(self):
            self.mqtt = TestPullDockerImageWithProgress._FakeMQTT()

    def test_streams_pull_progress_lines_to_mqtt(self, monkeypatch):
        fake_client = self._FakeClient()
        monkeypatch.setattr(startup, "_get_shared_mqtt_client", lambda token: fake_client)
        monkeypatch.setattr(
            startup.subprocess,
            "Popen",
            lambda *args, **kwargs: self._FakeProcess(
                "layer-1: Pulling fs layer\rlayer-1: Downloading 10%\r"
                "layer-1: Downloading 10%\nDigest: sha256:abc123\n"
            ),
        )

        startup._pull_docker_image_with_progress(
            "ghcr.io/cyberwave/driver:1.2.3",
            container_name="cyberwave-driver-abcd1234",
            twin_uuid="99999999-9999-9999-9999-999999999999",
            token="test-token",
            timeout=30,
        )

        published_messages = [payload["message"] for _, payload in fake_client.mqtt.published]
        assert published_messages == [
            "docker pull started for image ghcr.io/cyberwave/driver:1.2.3",
            "docker pull: layer-1: Pulling fs layer",
            "docker pull: layer-1: Downloading 10%",
            "docker pull: Digest: sha256:abc123",
            "docker pull completed for image ghcr.io/cyberwave/driver:1.2.3",
        ]
        assert all(
            topic == "cw/cyberwave/twin/99999999-9999-9999-9999-999999999999/driverlog"
            for topic, _ in fake_client.mqtt.published
        )
        assert all(
            payload["driver_image"] == "ghcr.io/cyberwave/driver:1.2.3"
            for _, payload in fake_client.mqtt.published
        )


class TestReconcileDriverLogStreams:
    class _AliveThread:
        @staticmethod
        def is_alive() -> bool:
            return True

    def test_skips_token_reload_when_log_thread_is_already_attached(self, monkeypatch):
        monkeypatch.setattr(
            startup,
            "_list_running_driver_containers",
            lambda: ["cyberwave-driver-abcd1234"],
        )
        monkeypatch.setattr(
            startup,
            "_CONTAINER_LOG_THREADS",
            {"cyberwave-driver-abcd1234": self._AliveThread()},
        )
        monkeypatch.setattr(
            startup,
            "_CONTAINER_TWIN_MAP",
            {"cyberwave-driver-abcd1234": "99999999-9999-9999-9999-999999999999"},
        )

        load_calls: list[str] = []
        stream_calls: list[str] = []
        monkeypatch.setattr(startup, "load_token", lambda: load_calls.append("load") or "token")
        monkeypatch.setattr(
            startup,
            "_stream_container_logs",
            lambda *args, **kwargs: stream_calls.append("stream"),
        )

        attached = startup.reconcile_driver_log_streams()

        assert attached == 1
        assert load_calls == []
        assert stream_calls == []


class TestBuildDriverLogPayload:
    def test_resolve_container_driver_image_prefers_config_image(self):
        assert (
            startup._resolve_container_driver_image(
                {
                    "Config": {"Image": "ghcr.io/cyberwave/driver:1.2.3"},
                    "Image": "sha256:abc123",
                }
            )
            == "ghcr.io/cyberwave/driver:1.2.3"
        )

    def test_includes_edge_core_and_sdk_versions(self, monkeypatch):
        monkeypatch.setattr(startup, "EDGE_CORE_VERSION", "0.0.18-test")
        monkeypatch.setattr(startup, "CYBERWAVE_SDK_VERSION", "0.3.20-test")
        monkeypatch.setattr(startup.time, "time", lambda: 1234.5)

        payload = startup._build_driver_log_payload(
            "2026-03-09 12:00:00 ERROR driver failed",
            "cyberwave-driver-test",
            driver_image="ghcr.io/cyberwave/driver:1.2.3",
        )

        assert payload == {
            "type": "driver_log",
            "message": "2026-03-09 12:00:00 ERROR driver failed",
            "level": "ERROR",
            "container_name": "cyberwave-driver-test",
            "source": "edge",
            "timestamp": 1234.5,
            "edge_core_version": "0.0.18-test",
            "sdk_version": "0.3.20-test",
            "driver_image": "ghcr.io/cyberwave/driver:1.2.3",
        }

    def test_omits_optional_fields_when_unavailable(self, monkeypatch):
        monkeypatch.setattr(startup, "EDGE_CORE_VERSION", "0.0.18-test")
        monkeypatch.setattr(startup, "CYBERWAVE_SDK_VERSION", None)
        monkeypatch.setattr(startup.time, "time", lambda: 99.0)

        payload = startup._build_driver_log_payload(
            "informational startup message",
            "cyberwave-driver-test",
        )

        assert payload["edge_core_version"] == "0.0.18-test"
        assert payload["level"] == "INFO"
        assert "sdk_version" not in payload
        assert "driver_image" not in payload


class TestStartupHeartbeatOrdering:
    def test_starts_edge_heartbeat_before_fetching_drivers(self, monkeypatch):
        call_order: list[str] = []

        monkeypatch.setattr(startup, "load_token", lambda: "token-123")
        monkeypatch.setattr(startup, "validate_token", lambda token: True)
        monkeypatch.setattr(startup, "check_mqtt_connection", lambda token: True)
        monkeypatch.setattr(startup, "register_edge", lambda token: True)
        monkeypatch.setattr(
            startup,
            "load_environment_uuid",
            lambda retries=0, retry_delay_seconds=0.2: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
        )
        monkeypatch.setattr(startup, "get_or_create_fingerprint", lambda: "edge-fingerprint")
        monkeypatch.setattr(
            startup,
            "_list_linked_twin_uuids_for_fingerprint",
            lambda token, env_uuid, fingerprint: call_order.append("list_linked_twins")
            or ["bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"],
        )
        monkeypatch.setattr(
            startup,
            "_start_bootstrap_edge_health_publisher",
            lambda token, twin_uuids, edge_id: call_order.append("start_edge_heartbeat") or True,
        )
        monkeypatch.setattr(
            startup,
            "fetch_and_run_twin_drivers",
            lambda token, env_uuid, fingerprint: call_order.append("fetch_drivers") or [],
        )

        result = startup.run_startup_checks()

        assert result is True
        assert call_order == [
            "list_linked_twins",
            "start_edge_heartbeat",
            "fetch_drivers",
        ]
