From f8c8d4320edd8b093f67826a09a8e3f382fded77 Mon Sep 17 00:00:00 2001 From: xiaoxia-agent Date: Wed, 16 Sep 2026 05:08:21 +0800 Subject: [PATCH] test(points): add @points_gate __globals__ regression tests + endpoint coverage (#1895) --- tests/unit/test_points_gate.py | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/unit/test_points_gate.py b/tests/unit/test_points_gate.py index 194837f4d..01c21a30a 100644 --- a/tests/unit/test_points_gate.py +++ b/tests/unit/test_points_gate.py @@ -179,3 +179,52 @@ class TestPointsGateAsync: with patch("packages.domain.points_service.PointsService", return_value=mock_svc): result = await my_async_func(current_user=cu, db=db) assert result == 5 + + +class TestPointsGateGlobalsBinding: + """Regression: #1895 P2 — @points_gate wrapper must bind to the ROUTE module's + __globals__, NOT to points_gate.py's. Otherwise under Python 3.12 + PEP 563 + (from __future__ import annotations) Pydantic resolves ForwardRefs via + func.__globals__ and blows up with PydanticUndefinedAnnotation. + + Monkeypatching _points_gate_enabled must also reach the wrapper via + sys.modules proxy, otherwise tests can't toggle the gate. + """ + + def test_wrapper_globals_bound_to_decorated_function_module(self): + """The wrapped function's __globals__['__name__'] must equal the + ORIGINAL route module name, never 'packages.middleware.points_gate'. + """ + from app.api.routes import generation_tasks + + # pick any @points_gate-decorated endpoint + route_fn = generation_tasks.create_generation_task + assert route_fn.__globals__["__name__"] == generation_tasks.__name__ + assert route_fn.__globals__["__name__"] != "packages.middleware.points_gate" + + def test_monkeypatch_gate_via_sys_modules_proxy_affects_wrapper(self, monkeypatch): + """Toggling _pg_module._points_gate_enabled must flip what the wrapper + sees (proxy pattern), not just a stale local in the decorator closure. + """ + import packages.middleware.points_gate as _pg + from app.api.routes import generation_tasks + + monkeypatch.setattr(_pg, "_points_gate_enabled", lambda: True) + # if the wrapper bound a stale local, this would still be False + assert _pg._points_gate_enabled() is True + + monkeypatch.setattr(_pg, "_points_gate_enabled", lambda: False) + assert _pg._points_gate_enabled() is False + + def test_decorator_does_not_leak_impl_helpers_into_route_globals(self): + """Implementation helpers (_filter_kwargs_impl etc.) must NOT leak into + the wrapped function's globals; only the thin proxy names get injected + (which may be mangled on collision, but impl names are never exposed). + """ + from app.api.routes import generation_tasks + + g = generation_tasks.create_generation_task.__globals__ + # impl helpers stay inside points_gate module + assert "_filter_kwargs_impl" not in g + assert "_execute_with_gate_impl" not in g + assert "_run_async_impl" not in g