"""
Unit tests for decnet.privdrop — no actual root required.

We stub os.geteuid / os.chown to simulate root and capture the calls,
so these tests are portable (CI doesn't run as root).
"""
import os

import pytest

from decnet import privdrop


def test_chown_noop_when_not_root(tmp_path, monkeypatch):
    target = tmp_path / "x.log"
    target.write_text("")
    monkeypatch.setattr(os, "geteuid", lambda: 1000)
    monkeypatch.setenv("SUDO_UID", "1000")
    monkeypatch.setenv("SUDO_GID", "1000")

    called = []
    monkeypatch.setattr(os, "chown", lambda *a, **kw: called.append(a))
    privdrop.chown_to_invoking_user(target)
    assert called == []


def test_chown_noop_when_no_sudo_env(tmp_path, monkeypatch):
    target = tmp_path / "x.log"
    target.write_text("")
    monkeypatch.setattr(os, "geteuid", lambda: 0)
    monkeypatch.delenv("SUDO_UID", raising=False)
    monkeypatch.delenv("SUDO_GID", raising=False)

    called = []
    monkeypatch.setattr(os, "chown", lambda *a, **kw: called.append(a))
    privdrop.chown_to_invoking_user(target)
    assert called == []


def test_chown_noop_when_path_missing(tmp_path, monkeypatch):
    monkeypatch.setattr(os, "geteuid", lambda: 0)
    monkeypatch.setenv("SUDO_UID", "1000")
    monkeypatch.setenv("SUDO_GID", "1000")

    called = []
    monkeypatch.setattr(os, "chown", lambda *a, **kw: called.append(a))
    privdrop.chown_to_invoking_user(tmp_path / "does-not-exist")
    assert called == []


def test_chown_applies_sudo_ids(tmp_path, monkeypatch):
    target = tmp_path / "x.log"
    target.write_text("")
    monkeypatch.setattr(os, "geteuid", lambda: 0)
    monkeypatch.setenv("SUDO_UID", "4242")
    monkeypatch.setenv("SUDO_GID", "4243")

    seen = {}
    def fake_chown(path, uid, gid):
        seen["path"] = str(path)
        seen["uid"] = uid
        seen["gid"] = gid
    monkeypatch.setattr(os, "chown", fake_chown)

    privdrop.chown_to_invoking_user(target)
    assert seen == {"path": str(target), "uid": 4242, "gid": 4243}


def test_chown_tree_recurses(tmp_path, monkeypatch):
    (tmp_path / "a").mkdir()
    (tmp_path / "a" / "b.log").write_text("")
    (tmp_path / "c.log").write_text("")

    monkeypatch.setattr(os, "geteuid", lambda: 0)
    monkeypatch.setenv("SUDO_UID", "1000")
    monkeypatch.setenv("SUDO_GID", "1000")

    chowned = []
    monkeypatch.setattr(os, "chown", lambda p, *a: chowned.append(str(p)))

    privdrop.chown_tree_to_invoking_user(tmp_path)
    assert str(tmp_path) in chowned
    assert str(tmp_path / "a") in chowned
    assert str(tmp_path / "a" / "b.log") in chowned
    assert str(tmp_path / "c.log") in chowned


def test_chown_swallows_oserror(tmp_path, monkeypatch):
    """A failed chown (e.g. cross-fs sudo edge case) must not raise."""
    target = tmp_path / "x.log"
    target.write_text("")
    monkeypatch.setattr(os, "geteuid", lambda: 0)
    monkeypatch.setenv("SUDO_UID", "1000")
    monkeypatch.setenv("SUDO_GID", "1000")

    def boom(*_a, **_kw):
        raise OSError("EPERM")
    monkeypatch.setattr(os, "chown", boom)

    privdrop.chown_to_invoking_user(target)  # must not raise


def test_chown_rejects_malformed_sudo_ids(tmp_path, monkeypatch):
    target = tmp_path / "x.log"
    target.write_text("")
    monkeypatch.setattr(os, "geteuid", lambda: 0)
    monkeypatch.setenv("SUDO_UID", "not-an-int")
    monkeypatch.setenv("SUDO_GID", "1000")

    called = []
    monkeypatch.setattr(os, "chown", lambda *a, **kw: called.append(a))
    privdrop.chown_to_invoking_user(target)
    assert called == []
