Server: no built-in public host, no trusted proxy by default; OpenAPI server variable host (D-123)
KVITT_PUBLIC_URL is required: every command refuses to start without it (compose always sets it). KVITT_TRUSTED_PROXY defaults to none, which the rate limiter never resolves. contracts/openapi.yaml and the utoipa document: servers[].url https://{host}:8443 with variable host (default kvitt.example.invalid). E-7 and E-12 examples on kvitt.example.invalid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
@@ -22,6 +22,8 @@ use sqlx::SqliteConnection;
|
||||
use tower::ServiceExt;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// `KVITT_PUBLIC_URL` of the harness, on the reserved `.invalid` name of the examples (E-7, E-12).
|
||||
pub const PUBLIC_URL: &str = "https://kvitt.example.invalid:8443";
|
||||
pub const ADMIN_ID: &str = "0192a000-0000-7000-8000-000000000001";
|
||||
pub const MEMBER_ID: &str = "0192a000-0000-7000-8000-000000000002";
|
||||
pub const ADMIN_DEVICE_ID: &str = "0192a001-0000-7000-8000-000000000001";
|
||||
@@ -151,6 +153,7 @@ impl TestApp {
|
||||
let path = dir.0.to_string_lossy().into_owned();
|
||||
let config = Config::from_lookup(|k| match k {
|
||||
"KVITT_DATA_DIR" => Some(path.clone()),
|
||||
"KVITT_PUBLIC_URL" => Some(PUBLIC_URL.to_owned()),
|
||||
// No proxy is trusted unless a test says so.
|
||||
"KVITT_TRUSTED_PROXY" => Some("127.0.0.9".to_owned()),
|
||||
_ => None,
|
||||
|
||||
@@ -39,6 +39,30 @@ fn printed_code(output: &Output) -> String {
|
||||
code.to_owned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_command_refuses_to_start_without_a_public_url() {
|
||||
// SPEC §10.4: KVITT_PUBLIC_URL has no built-in host; compose always sets it.
|
||||
let dir = TempDir::new();
|
||||
for args in [&["serve"][..], &["bootstrap"], &["migrate"]] {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_kvitt-server"))
|
||||
.args(args)
|
||||
.env("KVITT_DATA_DIR", &dir.0)
|
||||
.env_remove("KVITT_PUBLIC_URL")
|
||||
.env("RUST_LOG", "warn")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert_eq!(output.status.code(), Some(1), "{args:?}: {output:?}");
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(
|
||||
stderr.starts_with("configuration: KVITT_PUBLIC_URL is not set"),
|
||||
"{args:?}: {stderr}"
|
||||
);
|
||||
assert!(output.stdout.is_empty(), "{args:?}");
|
||||
}
|
||||
// Nothing was created: the server stopped before opening the database.
|
||||
assert!(!dir.0.join("kvitt.sqlite").exists());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn bootstrap_prints_an_admin_setup_invite_cancels_the_previous_one_then_refuses() {
|
||||
let dir = TempDir::new();
|
||||
|
||||
@@ -219,9 +219,7 @@ async fn e12_partner_invite_matches_the_example() {
|
||||
assert_eq!(code.len(), 12);
|
||||
assert_eq!(
|
||||
reply.json["link"],
|
||||
json!(format!(
|
||||
"https://icehack.asuscomm.com:8443/i/{code}?from=Jacob"
|
||||
))
|
||||
json!(format!("{PUBLIC_URL}/i/{code}?from=Jacob"))
|
||||
);
|
||||
assert_eq!(reply.json["created_at"], NOW);
|
||||
assert_eq!(reply.json["expires_at"], "2026-10-20T12:00:00.000Z");
|
||||
|
||||
@@ -34,9 +34,9 @@ async fn e4_english_by_default_with_both_links_and_the_grouped_code() {
|
||||
assert!(html.contains(">Open in Kvitt</a>"));
|
||||
assert!(html.contains("7K3M-QX9D-2HPA"));
|
||||
assert!(html.contains(
|
||||
"href=\"kvitt://invite?server=https%3A%2F%2Ficehack%2Easuscomm%2Ecom%3A8443&code=7K3MQX9D2HPA\""
|
||||
"href=\"kvitt://invite?server=https%3A%2F%2Fkvitt%2Eexample%2Einvalid%3A8443&code=7K3MQX9D2HPA\""
|
||||
));
|
||||
assert!(html.contains("href=\"https://icehack.asuscomm.com:8443/apk/files/kvitt-412.apk\""));
|
||||
assert!(html.contains("href=\"https://kvitt.example.invalid:8443/apk/files/kvitt-412.apk\""));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -90,7 +90,7 @@ async fn e4_passes_the_admin_name_of_an_e12_link_into_the_app_link_only() {
|
||||
assert_eq!(reply.status, 200);
|
||||
let html = reply.text();
|
||||
assert!(html.contains(
|
||||
"href=\"kvitt://invite?server=https%3A%2F%2Ficehack%2Easuscomm%2Ecom%3A8443&code=7K3MQX9D2HPA&from=%C3%85sa%20Berg\""
|
||||
"href=\"kvitt://invite?server=https%3A%2F%2Fkvitt%2Eexample%2Einvalid%3A8443&code=7K3MQX9D2HPA&from=%C3%85sa%20Berg\""
|
||||
), "{html}");
|
||||
// The name is not shown on the page itself.
|
||||
assert!(!html.contains("Åsa"));
|
||||
|
||||
@@ -47,8 +47,10 @@ impl TestApp {
|
||||
|
||||
async fn migrated_to(target: Option<i64>) -> Self {
|
||||
let dir = TempDir::new();
|
||||
let config = Config::from_lookup(|k| {
|
||||
(k == "KVITT_DATA_DIR").then(|| dir.0.to_string_lossy().into_owned())
|
||||
let config = Config::from_lookup(|k| match k {
|
||||
"KVITT_DATA_DIR" => Some(dir.0.to_string_lossy().into_owned()),
|
||||
"KVITT_PUBLIC_URL" => Some("https://kvitt.example.invalid:8443".to_owned()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
let pool = db::connect(&config.database_path()).await.unwrap();
|
||||
|
||||
@@ -79,6 +79,7 @@ async fn rotate_epoch_sets_a_new_random_epoch_and_exits_0() {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_kvitt-server"))
|
||||
.arg("rotate-epoch")
|
||||
.env("KVITT_DATA_DIR", app.data_dir())
|
||||
.env("KVITT_PUBLIC_URL", PUBLIC_URL)
|
||||
.env("RUST_LOG", "warn")
|
||||
.output()
|
||||
.unwrap();
|
||||
@@ -96,6 +97,7 @@ fn binary(dir: &std::path::Path, bind: SocketAddr) -> Command {
|
||||
command
|
||||
.env("KVITT_DATA_DIR", dir)
|
||||
.env("KVITT_BIND", bind.to_string())
|
||||
.env("KVITT_PUBLIC_URL", PUBLIC_URL)
|
||||
.env("KVITT_FCM_CREDENTIALS", dir.join("no-such-key.json"))
|
||||
.env("RUST_LOG", "warn");
|
||||
command
|
||||
|
||||
Reference in New Issue
Block a user