Initial commit — StreamStack v1

Five-service streaming platform: auth, catalogue, streaming, ingest, thumbnailer.
Includes React frontend served by nginx, NATS JetStream event bus, aiobotocore
async S3, PyAV video metadata + thumbnail extraction, service-to-service JWT auth,
and a full unit + e2e test suite.
This commit is contained in:
2026-05-04 22:16:39 +10:00
commit 2309e9f43a
80 changed files with 6339 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
import os
import time
import boto3
import httpx
import pytest
GATEWAY_URL = os.environ.get("GATEWAY_URL", "http://localhost:8080")
S3_ENDPOINT = os.environ.get("S3_ENDPOINT_URL", "http://localhost:9000")
S3_ACCESS_KEY = os.environ.get("S3_ACCESS_KEY", "minioadmin")
S3_SECRET_KEY = os.environ.get("S3_SECRET_KEY", "minioadmin")
S3_BUCKET = os.environ.get("S3_BUCKET_MEDIA", "media")
TESTDATA = os.path.join(os.path.dirname(__file__), "..", "..", "testdata")
TEST_VIDEOS = [
{
"filename": "Big_Buck_Bunny_1080_10s_30MB.mp4",
"s3_key": "media/big_buck_bunny.mp4",
"media_type": "movie",
"title": "Big Buck Bunny",
"director": "Sacha Goedegebure",
"release_year": 2008,
"is_published": True,
},
{
"filename": "Jellyfish_1080_10s_30MB.mkv",
"s3_key": "media/jellyfish.mkv",
"media_type": "movie",
"title": "Jellyfish",
"director": "Blender Foundation",
"release_year": 2015,
"is_published": True,
},
]
ADMIN_EMAIL = "e2e-admin@streamstack.test"
ADMIN_PASSWORD = "e2etestpassword"
@pytest.fixture(scope="session")
def s3():
return boto3.client(
"s3",
endpoint_url=S3_ENDPOINT,
aws_access_key_id=S3_ACCESS_KEY,
aws_secret_access_key=S3_SECRET_KEY,
region_name="us-east-1",
)
@pytest.fixture(scope="session")
def admin_token():
with httpx.Client(base_url=GATEWAY_URL) as client:
# Register; ignore 409 if already exists from a previous run
client.post(
"/api/v1/auth/users/",
json={"email": ADMIN_EMAIL, "password": ADMIN_PASSWORD, "roles": ["admin", "viewer"]},
)
resp = client.post(
"/api/v1/auth/token",
data={"username": ADMIN_EMAIL, "password": ADMIN_PASSWORD},
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
resp.raise_for_status()
return resp.json()["access_token"]
@pytest.fixture(scope="session")
def seeded_media(s3, admin_token):
items = []
with httpx.Client(base_url=GATEWAY_URL) as client:
for video in TEST_VIDEOS:
local_path = os.path.join(TESTDATA, video["filename"])
s3.upload_file(local_path, S3_BUCKET, video["s3_key"])
payload = {k: v for k, v in video.items() if k != "filename"}
resp = client.post(
"/api/v1/catalogue/",
json=payload,
headers={"Authorization": f"Bearer {admin_token}"},
)
resp.raise_for_status()
items.append(resp.json())
return items
def wait_for_service(url: str, timeout: int = 60) -> None:
deadline = time.time() + timeout
while time.time() < deadline:
try:
r = httpx.get(url, timeout=5)
if r.status_code < 500:
return
except Exception:
pass
time.sleep(2)
raise RuntimeError(f"Service not ready: {url}")
def pytest_configure(config):
wait_for_service(f"{GATEWAY_URL}/api/v1/auth/health")
wait_for_service(f"{GATEWAY_URL}/api/v1/catalogue/health")
wait_for_service(f"{GATEWAY_URL}/api/v1/stream/health")