Skip to content

Commit 3f2692c

Browse files
rzfk2vclaude
andcommitted
ci: backend tests + fresh-install smoke test on every push
Two GitHub Actions jobs: the backend pytest suite, and a smoke test that builds the frontend and serves it from the backend the way a new user runs it (single process, direct :8000) — asserting the page loads with relative asset paths (no baked-in /sdr/ prefix), every referenced hashed asset resolves, and the WebSocket delivers the initial status. Catches fresh-install breakage (like the /sdr base-path 404s) before it ships. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e48bf0b commit 3f2692c

2 files changed

Lines changed: 92 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# CI: backend test suite + a fresh-install smoke test.
2+
#
3+
# The smoke test builds the frontend and serves it from the backend exactly the
4+
# way a new user runs it (single process, http://host:8000) — asserting the
5+
# page, its hashed assets, the API and the WebSocket all resolve. This is the
6+
# class of bug that only shows up on someone else's machine (e.g. the /sdr/
7+
# base-path 404s), caught before it ships.
8+
9+
name: CI
10+
11+
on:
12+
push:
13+
branches: [main]
14+
pull_request:
15+
16+
jobs:
17+
backend-tests:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
- uses: actions/setup-python@v5
22+
with:
23+
python-version: "3.12"
24+
cache: pip
25+
cache-dependency-path: backend/requirements.txt
26+
- name: Install backend dependencies
27+
run: pip install -r backend/requirements.txt pytest
28+
- name: Run the test suite
29+
working-directory: backend
30+
run: python -m pytest tests/ -q
31+
32+
fresh-install-smoke:
33+
name: build frontend, serve from backend
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v4
37+
- uses: actions/setup-node@v4
38+
with:
39+
node-version: 22
40+
cache: npm
41+
cache-dependency-path: frontend/package-lock.json
42+
- uses: actions/setup-python@v5
43+
with:
44+
python-version: "3.12"
45+
cache: pip
46+
cache-dependency-path: backend/requirements.txt
47+
- name: Build the frontend
48+
working-directory: frontend
49+
run: |
50+
npm ci
51+
npm run build
52+
- name: Install backend dependencies
53+
run: pip install -r backend/requirements.txt
54+
- name: Serve the built frontend from the backend and smoke-test it
55+
working-directory: backend
56+
run: |
57+
uvicorn app.main:app --port 8000 &
58+
for i in $(seq 1 20); do
59+
curl -sf -o /dev/null http://127.0.0.1:8000/api/status && break
60+
sleep 0.5
61+
done
62+
63+
# The page must load with *relative* asset paths (no baked-in prefix
64+
# like /sdr/ — that only works behind a matching reverse proxy).
65+
curl -sf http://127.0.0.1:8000/ -o /tmp/index.html
66+
grep -q '"\./assets/' /tmp/index.html
67+
if grep -q '"/sdr/' /tmp/index.html; then
68+
echo "index.html contains an absolute /sdr/ path — breaks direct :8000 access" >&2
69+
exit 1
70+
fi
71+
72+
# Every referenced hashed asset must actually be served.
73+
for asset in $(grep -oE 'assets/[A-Za-z0-9._-]+' /tmp/index.html | sort -u); do
74+
echo "checking $asset"
75+
curl -sf -o /dev/null "http://127.0.0.1:8000/$asset"
76+
done
77+
78+
# The WebSocket must connect and deliver the initial status message.
79+
python - <<'EOF'
80+
import asyncio, json
81+
import websockets
82+
83+
async def t():
84+
async with websockets.connect("ws://127.0.0.1:8000/ws") as ws:
85+
msg = json.loads(await asyncio.wait_for(ws.recv(), 10))
86+
assert msg.get("type") == "status", msg
87+
print("ws OK:", msg["mode"], "device_present:", msg["device_present"])
88+
89+
asyncio.run(t())
90+
EOF

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Cascade SDR
22

3+
[![CI](https://github.com/rzfk2v/Cascade-SDR/actions/workflows/ci.yml/badge.svg)](https://github.com/rzfk2v/Cascade-SDR/actions/workflows/ci.yml)
4+
35
A cross-platform receiver app **for RTL-SDR dongles** (RTL2832U + R820T/R820T2,
46
~24–1766 MHz). It is **RTL-SDR specific** — other SDRs (Airspy, HackRF, SDRplay,
57
…) are not supported. A small **Python backend** owns the dongle and does the

0 commit comments

Comments
 (0)