|
|
|
@@ -80,15 +80,66 @@ print("WYNIK:" + json.dumps({
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
|
def wynik(drzewo):
|
|
|
|
|
# Generowania tekstu w tym obrazie nie ma, ale most modułów zostaje — więc jego
|
|
|
|
|
# transport musi nieść pulę z sesji tak samo jak wywołania ekranów. Osobna sonda,
|
|
|
|
|
# bo dokłada trasę, której w produkcie nie ma: w sondzie produktu zafałszowałaby
|
|
|
|
|
# każde pytanie o to, co produkt zawiera.
|
|
|
|
|
SONDA_MODULU = '''
|
|
|
|
|
import json, os, hashlib, secrets
|
|
|
|
|
sol = secrets.token_bytes(16)
|
|
|
|
|
dk = hashlib.scrypt(b"tajne", salt=sol, n=2**14, r=8, p=1, dklen=32)
|
|
|
|
|
os.environ.update(ASTROKLIENT_USERS="klient:scrypt$%s$%s" % (sol.hex(), dk.hex()),
|
|
|
|
|
SESSION_SECRET="t"*64, COOKIES_INSECURE="1", RATE_LIMIT_PER_MIN="0")
|
|
|
|
|
from fastapi.responses import StreamingResponse
|
|
|
|
|
from starlette.testclient import TestClient
|
|
|
|
|
from app.main import app, logic
|
|
|
|
|
from app import security, session, features
|
|
|
|
|
from app.clients import logic_client
|
|
|
|
|
wyslane = {}
|
|
|
|
|
def call_json(client, method, url, payload=None, **kw):
|
|
|
|
|
wyslane[url.split("/chart/", 1)[1]] = (payload or {}).get("tenant")
|
|
|
|
|
return {}
|
|
|
|
|
def stream_lines(client, url, payload=None, **kw):
|
|
|
|
|
wyslane[url.split("/chart/", 1)[1]] = (payload or {}).get("tenant")
|
|
|
|
|
yield '{"type": "result", "result": {}}'
|
|
|
|
|
logic_client.link_crypto.call_json = call_json
|
|
|
|
|
logic_client.link_crypto.stream_lines = stream_lines
|
|
|
|
|
# Trasa w miejscu modułu: woła ten sam transport co moduł generowania, a strumień
|
|
|
|
|
# konsumuje równie leniwie — dopiero przy wysyłaniu odpowiedzi, nie w obsłudze trasy.
|
|
|
|
|
features.ROUTES[("POST", "/sonda/modul")] = None
|
|
|
|
|
@app.post("/sonda/modul")
|
|
|
|
|
def sonda_modul():
|
|
|
|
|
podrobione = {"profile": "natal", "tenant": "ktos-inny"}
|
|
|
|
|
logic.wywolaj("/chart/prompt", podrobione, 5)
|
|
|
|
|
logic.wywolaj("/chart/horoscope", podrobione, 5)
|
|
|
|
|
def relay():
|
|
|
|
|
yield from logic.strumien("/chart/horoscope/stream", podrobione)
|
|
|
|
|
return StreamingResponse(relay(), media_type="application/x-ndjson")
|
|
|
|
|
c = TestClient(app, follow_redirects=False)
|
|
|
|
|
H = {"Cookie": f"{session.COOKIE}={security.issue_session('klient')}"}
|
|
|
|
|
r = c.post("/sonda/modul", headers=H)
|
|
|
|
|
print("WYNIK:" + json.dumps({"status": r.status_code, "pule": wyslane}))
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _uruchom(drzewo: pathlib.Path, sonda: str) -> dict:
|
|
|
|
|
"""Aplikacja uruchomiona w OSOBNYM PROCESIE.
|
|
|
|
|
|
|
|
|
|
Importy Pythona są zapamiętywane w procesie, więc sprawdzanie „produktu bez
|
|
|
|
|
modułu" tam, gdzie moduł już się zaimportował, dałoby wynik fałszywie
|
|
|
|
|
pozytywny."""
|
|
|
|
|
r = subprocess.run([sys.executable, "-c", SONDA], cwd=drzewo, capture_output=True,
|
|
|
|
|
r = subprocess.run([sys.executable, "-c", sonda], cwd=drzewo, capture_output=True,
|
|
|
|
|
text=True, env={"PYTHONPATH": str(drzewo), "PATH": "/usr/bin:/bin"})
|
|
|
|
|
linia = next((l for l in r.stdout.splitlines() if l.startswith("WYNIK:")), None)
|
|
|
|
|
assert linia, f"sonda nie wystartowała:\n{r.stdout[-2000:]}\n{r.stderr[-2000:]}"
|
|
|
|
|
return json.loads(linia[len("WYNIK:"):])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
|
def wynik(drzewo):
|
|
|
|
|
return _uruchom(drzewo, SONDA)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
|
def wynik_modulu(drzewo):
|
|
|
|
|
return _uruchom(drzewo, SONDA_MODULU)
|
|
|
|
|