furtka-gateway/tests/test_reconciler.py
Robert Syrnicki 3a9d18fcd5 Scaffold gateway stack and single-tenant control plane
wg-easy + Traefik docker-compose stack (Phase 1) plus a stdlib-only
control-plane API for box registration, WireGuard peer provisioning via
wg-easy, and per-box route publish/unpublish backed by Traefik's file
provider (Phase 2, single-tenant mode). SQLite holds accounts/boxes/routes
so a later multi-tenant shared instance is the same schema with more rows,
not a reshape.

wg-easy's actual REST API was verified against its source rather than
assumed: it has no bearer-token auth (session-cookie login via
POST /api/auth/password) and no way to accept an externally-generated
public key (it always mints the keypair itself, private key included) —
both corrected from the original plan during implementation.
2026-08-24 11:50:25 +02:00

54 lines
1.9 KiB
Python

from control_plane import paths, reconciler, routes, traefikconf
from control_plane.db import Database
def _seeded_db(tmp_path):
db = Database(db_path=tmp_path / "gateway.db")
db.execute(
"INSERT INTO accounts (id, email, created_at, registration_token_hash, "
"account_token_hash, box_limit, route_limit_per_box) "
"VALUES ('acc1', NULL, '2026-01-01T00:00:00', '', '', 5, 5)"
)
db.execute(
"INSERT INTO boxes (id, account_id, name, wg_public_key, wg_peer_id, "
"wg_allowed_ip, box_token_hash, registered_at) "
"VALUES ('box1', 'acc1', 'my-box', 'pk==', 'peer-1', '10.8.0.5/32', 'h', "
"'2026-01-01T00:00:00')"
)
return db
def test_reconcile_recreates_missing_file(tmp_path, gateway_paths):
db = _seeded_db(tmp_path)
result = routes.publish_route(
db, "box1", "acc1", "vaultwarden", "vault.example.com", 8081, 5, "le"
)
route_file = paths.dynamic_dir() / f"route-{result['route_id']}.yml"
route_file.unlink()
assert not route_file.exists()
reconciler.reconcile(db, cert_resolver="le")
assert route_file.exists()
assert "vault.example.com" in route_file.read_text()
def test_reconcile_removes_orphan_file(tmp_path, gateway_paths):
db = _seeded_db(tmp_path)
traefikconf.write_route("orphan", "orphan.example.com", "10.8.0.9", 80, cert_resolver="le")
reconciler.reconcile(db, cert_resolver="le")
assert "orphan" not in traefikconf.existing_route_ids()
def test_reconcile_ignores_disabled_routes(tmp_path, gateway_paths):
db = _seeded_db(tmp_path)
result = routes.publish_route(
db, "box1", "acc1", "vaultwarden", "vault.example.com", 8081, 5, "le"
)
db.execute("UPDATE routes SET enabled = 0 WHERE id = ?", (result["route_id"],))
reconciler.reconcile(db, cert_resolver="le")
assert traefikconf.existing_route_ids() == set()