net-kingdom/sso-mfa/k8s/keycape/test_adapter_source_closure.py
tegwick d16db30282 Pin bind-env full local source closure (NK48-R2-F3)
bind-env's source_digest covered bind-env-adapter.py and keycape-client.py
only, but keycape-client.py executes portal-client-rollout.py, which
executes openbao-client-config.py and register-user-engine-portal.py.
Pin the same five-file closure the registration adapter pins, and add a
closure-completeness test that imports every *-adapter.py in a fresh
interpreter and fails on any loaded local file its SOURCE_FILES omits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 903097@bnt-lap001
Assistant-Session: aa320c8f-c3fa-46e7-8541-0b58e1e45d23
2026-10-17 11:07:33 +02:00

82 lines
2.7 KiB
Python

"""Every adapter's source_digest must cover every local file it executes.
Each *-adapter.py is imported in a fresh interpreter with
importlib.util.spec_from_file_location instrumented; the local .py files
actually loaded (sys.modules __file__ under the repo plus every
spec_from_file_location target) must be a subset of its SOURCE_FILES.
"""
import json
import os
from pathlib import Path
import subprocess
import sys
import unittest
ROOT = Path(__file__).resolve().parent
REPO = ROOT.parents[2]
PROBE = r'''
import importlib.util, json, sys
from pathlib import Path
root, repo, adapter = Path(sys.argv[1]), Path(sys.argv[2]), sys.argv[3]
seen = set()
_orig = importlib.util.spec_from_file_location
def _spy(name, location=None, *a, **k):
if location is not None:
seen.add(str(Path(location).resolve()))
return _orig(name, location, *a, **k)
importlib.util.spec_from_file_location = _spy
spec = _orig('adapter_under_probe', root / adapter)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
for m in list(sys.modules.values()):
f = getattr(m, '__file__', None)
if f:
seen.add(str(Path(f).resolve()))
local = sorted(p for p in seen
if p.endswith('.py') and Path(p).is_relative_to(repo)
and '.venv' not in Path(p).parts and 'site-packages' not in Path(p).parts)
local.append(str((root / adapter).resolve()))
pinned = []
for entry in mod.SOURCE_FILES:
pinned.append(str((repo / entry[0] / entry[1]).resolve()) if isinstance(entry, tuple)
else str((root / entry).resolve()))
print(json.dumps({'loaded': sorted(set(local)), 'pinned': sorted(pinned)}))
'''
# Adapters outside this fix's scope that fail the check (reported, not fixed).
KNOWN_FAILING = set()
def probe(adapter):
out = subprocess.run([sys.executable, '-c', PROBE, str(ROOT), str(REPO), adapter],
capture_output=True, text=True, env=dict(os.environ), cwd=ROOT)
if out.returncode:
raise AssertionError(f'{adapter} probe failed: {out.stderr[-2000:]}')
return json.loads(out.stdout.strip().splitlines()[-1])
def unpinned(adapter):
r = probe(adapter)
return sorted(set(r['loaded']) - set(r['pinned']))
class AdapterSourceClosure(unittest.TestCase):
pass
def _make(adapter):
def test(self):
missing = unpinned(adapter)
self.assertEqual(missing, [], f'{adapter} loads files its source_digest does not pin')
if adapter in KNOWN_FAILING:
test = unittest.expectedFailure(test)
return test
for _p in sorted(ROOT.glob('*-adapter.py')):
setattr(AdapterSourceClosure, 'test_' + _p.stem.replace('-', '_'), _make(_p.name))
if __name__ == '__main__':
unittest.main()