#!/usr/bin/env python3
"""Pure unit test for the empty-My-Packs store resolver guard in autopatch.py.

Covers the fail-closed guard decision (original -> PATCH, already-patched -> NOOP,
unknown -> SKIP) and pins the guarded patch table to the exact RVA/bytes proven on
the tested FIFA 17 build (JNZ 0x14869 -> JG 0x14869 at CardsDLL RVA 0x14858).

Run: python3 test_autopatch_guard.py
"""
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import autopatch  # importable: runtime loop is guarded by `if __name__ == "__main__"`

GUARD_VA = 0x180014858
ORIG = bytes.fromhex("750f")   # JNZ 0x14869
PATCH = bytes.fromhex("7f0f")  # JG  0x14869


def test_table_exact():
    assert autopatch.STORE_PATCHES_GUARDED == {GUARD_VA: (ORIG, PATCH)}, \
        autopatch.STORE_PATCHES_GUARDED
    # Byte-level pin so a bad hex literal cannot slip through.
    assert ORIG == b"\x75\x0f" and PATCH == b"\x7f\x0f"


def test_decision():
    assert autopatch.guarded_action(ORIG, ORIG, PATCH) == "patch"   # apply
    assert autopatch.guarded_action(PATCH, ORIG, PATCH) == "noop"   # already patched
    assert autopatch.guarded_action(b"\x00\x00", ORIG, PATCH) == "skip"  # build mismatch
    assert autopatch.guarded_action(b"\x90", ORIG, PATCH) == "skip"      # wrong length


def test_guard_state_after():
    # already patched (7f0f) -> VERIFIED (guarded_action "noop"); write args irrelevant.
    assert autopatch.guard_state_after(PATCH, ORIG, PATCH, True, PATCH) == autopatch.GUARD_VERIFIED
    # original (750f) + write ok + reread 7f0f -> VERIFIED (guarded_action "patch").
    assert autopatch.guard_state_after(ORIG, ORIG, PATCH, True, PATCH) == autopatch.GUARD_VERIFIED
    # original + write FAILS -> WRITE_FAILED.
    assert autopatch.guard_state_after(ORIG, ORIG, PATCH, False, ORIG) == autopatch.GUARD_WRITE_FAILED
    # original + write ok but reread != 7f0f -> VERIFY_FAILED.
    assert autopatch.guard_state_after(ORIG, ORIG, PATCH, True, ORIG) == autopatch.GUARD_VERIFY_FAILED
    assert autopatch.guard_state_after(ORIG, ORIG, PATCH, True, b"") == autopatch.GUARD_VERIFY_FAILED
    # unknown bytes -> UNSUPPORTED_BUILD (guarded_action "skip"); write args irrelevant.
    assert autopatch.guard_state_after(b"\x00\x00", ORIG, PATCH, True, PATCH) == autopatch.GUARD_UNSUPPORTED_BUILD


def test_capability_constants():
    assert autopatch.EMPTY_MYPACKS_RESOLVER_VERSION == 1
    assert autopatch.EMPTY_MYPACKS_RESOLVER_CAPABILITY == "fifa17.empty_mypacks_resolver"
    # State constant values are the exact tokens carried in the emitted status line.
    assert autopatch.GUARD_VERIFIED == "VERIFIED"
    assert autopatch.GUARD_UNSUPPORTED_BUILD == "UNSUPPORTED_BUILD"
    assert autopatch.GUARD_WRITE_FAILED == "WRITE_FAILED"
    assert autopatch.GUARD_VERIFY_FAILED == "VERIFY_FAILED"
    assert autopatch.GUARD_NOT_ATTEMPTED == "NOT_ATTEMPTED"


if __name__ == "__main__":
    test_table_exact()
    test_decision()
    test_guard_state_after()
    test_capability_constants()
    print("OK: autopatch guard table + fail-closed decision + guard-state function + capability constants")
