Files
2026-09-26 05:57:32 +00:00

83 lines
3.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""Stub of the DealSage endpoints the pipeline talks to, for local tests.
Records every request as JSON lines in the file named by STUB_LOG, and answers
like the real functions would:
POST /record-app-version allocate -> version 7 + upload token
promote -> promoted per STUB_POLICY (auto|manual)
fail -> ok
POST /record-app-check -> {"id": "..."}
POST /record-app-build start -> {"buildId": "bld-1"}; finish -> echo;
steps -> {"buildId": ..., "steps": <count>}
PUT /__upload/<app>/<v>/<path> -> 204
"""
import json, os, sys
from http.server import BaseHTTPRequestHandler, HTTPServer
LOG = os.environ.get("STUB_LOG", "/tmp/stub-requests.jsonl")
POLICY = os.environ.get("STUB_POLICY", "auto")
seq = {"n": 0}
class H(BaseHTTPRequestHandler):
def log_message(self, *a): # quiet
pass
def _record(self, body):
with open(LOG, "a") as f:
f.write(json.dumps({"method": self.command, "path": self.path,
"auth": self.headers.get("Authorization"), "body": body}) + "\n")
def _json(self, code, obj):
data = json.dumps(obj).encode()
self.send_response(code); self.send_header("content-type", "application/json")
self.send_header("content-length", str(len(data))); self.end_headers(); self.wfile.write(data)
def do_POST(self):
n = int(self.headers.get("content-length") or 0)
raw = self.rfile.read(n).decode() if n else ""
try: body = json.loads(raw) if raw else {}
except json.JSONDecodeError: body = {"_raw": raw}
self._record(body)
if self.headers.get("Authorization") != "Bearer test-publish-token":
return self._json(401, {"error": "bad token"})
if self.path.endswith("/record-app-version"):
a = body.get("action")
if a == "allocate":
return self._json(200, {"version": 7, "r2Prefix": f"apps/{body.get('appId')}/7/", "uploadToken": "upl-token"})
if a == "promote":
if POLICY == "auto":
return self._json(200, {"version": 7, "currentVersion": 7, "promoted": True, "channel": "live"})
return self._json(200, {"version": 7, "currentVersion": 6, "promoted": False, "channel": "branch/feature/x"})
if a == "fail":
return self._json(200, {"ok": True})
return self._json(400, {"error": "bad action"})
if self.path.endswith("/record-app-check"):
seq["n"] += 1
return self._json(200, {"id": f"chk-{seq['n']}"})
if self.path.endswith("/record-app-build"):
if body.get("action") == "start":
return self._json(200, {"buildId": "bld-1"})
if body.get("action") == "finish":
return self._json(200, {"buildId": body.get("buildId"), "outcome": body.get("outcome")})
if body.get("action") == "steps":
steps = body.get("steps")
if not isinstance(steps, list) or not steps:
return self._json(400, {"error": "steps must be a non-empty array"})
return self._json(200, {"buildId": body.get("buildId"), "steps": len(steps)})
return self._json(400, {"error": "bad action"})
self._json(404, {"error": "no route"})
def do_PUT(self):
n = int(self.headers.get("content-length") or 0)
self.rfile.read(n)
self._record({"bytes": n})
if not self.path.startswith("/__upload/"):
return self._json(404, {"error": "no route"})
if self.headers.get("Authorization") != "Bearer upl-token":
return self._json(401, {"error": "bad upload token"})
self.send_response(204); self.end_headers()
if __name__ == "__main__":
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8765
HTTPServer(("127.0.0.1", port), H).serve_forever()