#!/usr/bin/env python3
"""
Regression test for Debian bug #1038747, intended to run on Debian sid.

Background
----------
software-properties-gtk rewrites the download mirror by calling
aptsources.distro.Distribution.change_server(). On Debian the security
archive (debian-security) is a *separate* archive from the main one, whereas
on Ubuntu the security pocket shares the main archive. The unpatched
change_server() blindly rewrote every child source's URI onto the main
mirror, so a Debian security line such as

    deb http://deb.debian.org/debian-security sid-security main

was turned into the non-existent

    deb http://<mirror>/debian sid-security main

which broke `apt update` with "does not have a Release file".

This script drives change_server() directly with an in-memory Debian sid
source set (no /etc/apt access, no network needed) and asserts that the
security archive is NOT rewritten onto the main mirror. It also checks the
matching deb-src entry, because the fix adds a guard on the
source_code_sources loop too.

How to run on Debian sid
------------------------
    # On a real sid system (or a sid chroot):
    python3 test-1038747.py

    # Or inside a sid container (the environment used to validate the patch):
    docker run --rm -v "$PWD:/workspace" debian:sid bash -c '
      echo "deb [trusted=yes] https://mirrors.tencent.com/debian sid main" >/etc/apt/sources.list
      apt-get update -qq && apt-get install -y -qq python3 python3-apt
      python3 /workspace/test-1038747.py
    '

Exit code: 0 = fixed (PASS), 1 = bug still present (FAIL).
"""

import sys

from aptsources.distro import Distribution
from aptsources.sourceslist import SourceEntry


class FakeTemplate:
    """Minimal stand-in for an aptsources template.

    Values deliberately mirror real Debian data:
      * the security template's base_uri is security.debian.org, which is
        DIFFERENT from the security source URI (deb.debian.org/debian-security);
        that mismatch is exactly what made the unpatched code rewrite the
        security URI onto the main mirror.
      * the main / updates templates share the main archive base_uri.
    """

    def __init__(self, base_uri):
        self.base_uri = base_uri


class _StubSourcesList:
    """Avoid touching a real sources.list when change_server prunes a comp."""

    def remove(self, source):
        pass


def build_debian_sid():
    """Build an in-memory Debian sid Distribution with three source kinds."""
    d = Distribution(id="Debian", codename="sid",
                     description="Debian sid", release="sid")
    # get_sources() normally sets source_template; the fix relies on it to
    # compare archives, so replicate that here.
    d.source_template = FakeTemplate("http://deb.debian.org/debian")
    d.sourceslist = _StubSourcesList()

    main = SourceEntry("deb http://deb.debian.org/debian sid main")
    sec = SourceEntry("deb http://deb.debian.org/debian-security sid-security main")
    upd = SourceEntry("deb http://deb.debian.org/debian sid-updates main")
    sec_src = SourceEntry("deb-src http://deb.debian.org/debian-security sid-security main")

    main.template = FakeTemplate("http://deb.debian.org/debian")
    sec.template = FakeTemplate("https://security.debian.org/")  # != sec.uri
    upd.template = FakeTemplate("http://deb.debian.org/debian")
    sec_src.template = FakeTemplate("https://security.debian.org/")

    d.main_sources = [main]
    d.child_sources = [sec, upd]
    # Give the deb-src security entry a parent so the patch's source_code
    # guard can recognise it belongs to a separate archive.
    sec_src.parent = sec
    d.source_code_sources = [sec_src]
    return d, main, sec, upd, sec_src


def detect_environment():
    """Best-effort reporting of the host Debian release (informational only)."""
    try:
        with open("/etc/os-release") as fh:
            data = fh.read()
        info = {}
        for line in data.splitlines():
            if "=" in line:
                key, _, val = line.partition("=")
                info[key] = val.strip().strip('"')
        label = info.get("PRETTY_NAME", "unknown")
        if "sid" in (info.get("VERSION_CODENAME", "") + label).lower() or \
                "debian" in label.lower():
            return label
        return label
    except Exception:
        return "unknown"


def run():
    print("Environment:", detect_environment())
    print("Regression test: aptsources Distribution.change_server() vs #1038747")
    print("-" * 64)

    d, main, sec, upd, sec_src = build_debian_sid()
    mirror = "http://ftp.hu.debian.org/debian/"
    d.change_server(mirror)  # equivalent to switching the mirror in the GUI

    print("main         ->", main.uri, "(expected: follows the new mirror)")
    print("security     ->", sec.uri, "(expected: stays on debian-security)")
    print("updates      ->", upd.uri, "(informational: depends on child guard)")
    print("security-src ->", sec_src.uri, "(expected: stays on debian-security)")

    main_ok = (main.uri == mirror)
    sec_ok = sec.uri.rstrip("/").endswith("debian-security")
    src_ok = sec_src.uri.rstrip("/").endswith("debian-security")

    ok = main_ok and sec_ok and src_ok
    print("-" * 64)
    print("RESULT:", "PASS (bug #1038747 fixed)" if ok else "FAIL (bug #1038747 present)")
    if not main_ok:
        print("  - main source did not follow the mirror (unexpected)")
    if not sec_ok:
        print("  - security binary source was rewritten onto the main mirror (this is #1038747)")
    if not src_ok:
        print("  - security deb-src source was rewritten onto the main mirror (same bug, source side)")
    return ok


if __name__ == "__main__":
    sys.exit(0 if run() else 1)
