Files
KF-OnlineJudge/backend/tests/test_delivery_validation_work.py
T

192 lines
5.8 KiB
Python

"""Ordinary string/worker models, not Root, durability or recovery acceptance."""
import importlib
import random
import re
import sys
import threading
import time
from collections.abc import Sequence
from pathlib import Path
from types import SimpleNamespace
import pytest
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
policy = importlib.import_module("scripts.release.policy")
file_copy = importlib.import_module("scripts.release.file_copy")
def old_path_result(value):
"""Frozen pre-optimization lexical policy, independent of the new cache."""
if not isinstance(value, str) or len(value) > 512:
return "invalid payload path"
if not all(re.fullmatch(r"[A-Za-z0-9_.+@-]{1,255}", p) for p in value.split("/")):
return "noncanonical payload path"
if any(
p in {".", "..", ".git", ".localplan", "docs", "__pycache__", ".env", ".ssh"}
for p in value.split("/")
):
return "private or unsafe payload path"
if not value.startswith(policy.ROOTS) or value.endswith((".pyc", ".pyo")):
return "payload path outside delivery allowlist"
return None
def test_lexical_policy_preserves_boundaries_and_refusal_categories():
values = [None, True, 1, [], {}, b"backend/a", ""]
components = [
".",
"..",
".git",
".localplan",
"docs",
"__pycache__",
".env",
".ssh",
"a//b",
"a/",
"a.pyc",
"a.pyo",
"a\x00b",
"a\nb",
"é",
"a" * 255,
"a" * 256,
"a" * 250 + "/" + "b" * 250,
"a" * 250 + "/" + "b" * 254,
]
values.extend("backend/" + component for component in components)
rng = random.Random(20261028)
for _ in range(3000):
value = "".join(rng.choices("abcXYZ019_.+@-/ \\:\n\x00é", k=rng.randrange(90)))
values.append(rng.choice(["backend/", "images/gnu/", "site/x/", ""]) + value)
for value in values:
refused = old_path_result(value)
if refused is None:
assert policy.relative_name(value) == value
else:
with pytest.raises(policy.DeliveryError) as observed:
policy.relative_name(value)
assert str(observed.value) == refused
def test_lexical_cache_is_bounded_and_custom_hash_cannot_bypass_policy():
class Collision(str):
def __hash__(self):
return hash("backend/valid.py")
def __eq__(self, other):
return True
policy._cached_relative_name.cache_clear()
try:
policy.relative_name("backend/valid.py")
before = policy._cached_relative_name.cache_info()
with pytest.raises(policy.DeliveryError, match="private or unsafe"):
policy.relative_name(Collision("backend/../escape"))
assert policy._cached_relative_name.cache_info() == before
for index in range(20000):
policy.relative_name("backend/member-" + str(index))
assert policy._cached_relative_name.cache_info().currsize == 16384
finally:
policy._cached_relative_name.cache_clear()
def test_deadline_stops_dispatch_but_waits_for_running_operations():
started = threading.Barrier(5)
release = threading.Event()
finished = threading.Event()
active = set()
errors = []
lock = threading.Lock()
def operation(index):
with lock:
active.add(index)
try:
started.wait(timeout=5)
assert release.wait(5)
return index
finally:
with lock:
active.remove(index)
def collect():
try:
file_copy.bounded_copies(list(range(32)), operation, deadline=time.monotonic() + 0.2)
except BaseException as error:
errors.append(error)
finally:
finished.set()
collector = threading.Thread(target=collect)
collector.start()
try:
started.wait(timeout=5)
assert not finished.wait(0.3)
with lock:
assert active == {0, 1, 2, 3}
finally:
release.set()
collector.join(5)
assert not collector.is_alive() and not active
assert len(errors) == 1 and isinstance(errors[0], policy.DeliveryError)
assert "copy deadline exceeded" in str(errors[0])
def test_single_operation_cannot_publish_after_its_deadline(monkeypatch):
current = [10.0]
monkeypatch.setattr(file_copy, "time", SimpleNamespace(monotonic=lambda: current[0]))
calls = []
def operation(index):
calls.append(index)
current[0] = 12.0
return "completed after deadline"
with pytest.raises(policy.DeliveryError, match="copy deadline exceeded"):
file_copy.bounded_copies([0], operation, deadline=11.0)
assert calls == [0]
def test_iterator_failure_joins_in_flight_work_without_dispatching_rest():
started, release, finished = (threading.Event() for _ in range(3))
calls, errors = [], []
class Broken(Sequence):
def __len__(self):
return 32
def __getitem__(self, index):
if index == 0:
return index
assert started.wait(5)
raise ValueError("finite inventory refusal")
def operation(index):
calls.append(index)
started.set()
assert release.wait(5)
return index
def collect():
try:
file_copy.bounded_copies(Broken(), operation)
except BaseException as error:
errors.append(error)
finally:
finished.set()
collector = threading.Thread(target=collect)
collector.start()
try:
assert started.wait(5)
assert not finished.wait(0.05)
finally:
release.set()
collector.join(5)
assert not collector.is_alive() and calls == [0]
assert len(errors) == 1 and str(errors[0]) == "finite inventory refusal"