This commit is contained in:
Mateusz Gruszczyński committed 2026-10-04 11:15:58 +02:00
1 parent 87e571c170
commit fa7abc9e53
308 files changed
+49746 -50237

No files matched your search

+148
View File
@@ -0,0 +1,148 @@
mod renewal_regressions {
use super::*;
use crate::{
auth::Auth,
config::{self, Config, ValidationPolicy},
db::Store,
};
async fn configured(automatic: bool) -> (Arc<App>, tempfile::TempDir, Certificate, Value) {
let directory = tempfile::tempdir().unwrap();
let cert_dir = directory.path().join("certs");
crate::tls::initialize_default(&cert_dir).unwrap();
for name in ["acme-old", "acme-next"] {
crate::tls::generate(&cert_dir, name, vec!["example.org".into()]).unwrap();
}
let cert = Certificate {
id: "site".into(),
covered_hosts: Vec::new(),
hosts: vec!["example.org".into()],
cert_file: "acme-old.pem".into(),
key_file: "acme-old.key".into(),
issuer: Some(crate::tls::Issuer {
provider: "cloudflare".into(),
environment: "staging".into(),
auto_renew: automatic,
}),
};
let mut config = Config::default();
config.tls.certificates.push(cert.clone());
config.tls.default_certificate = Some(cert.id.clone());
let policy = ValidationPolicy {
proxy_addr: "127.0.0.1:8080".parse().unwrap(),
admin_addr: "127.0.0.1:9090".parse().unwrap(),
tls_addr: None,
cert_dir: Some(cert_dir),
data_dir: None,
};
let store = Store::open(&directory.path().join("db"), &config)
.await
.unwrap();
let auth = Auth::new(store.clone(), directory.path().to_owned())
.await
.unwrap();
let snapshot = config::compile(config, 0, &policy).unwrap();
let app = App::new(
store,
snapshot,
policy,
auth,
8,
Duration::from_secs(2),
Duration::from_secs(2),
Duration::from_secs(30),
);
let output = serde_json::json!({"provider":"cloudflare", "environment":"staging", "certificate_id":"site",
"domains":["example.org"], "cert_file":"acme-next.pem", "key_file":"acme-next.key", "changed":true});
(app, directory, cert, output)
}
async fn stop(app: Arc<App>) {
app.shutdown.cancel();
app.tunnels.close();
app.tunnels.wait().await;
app.store.close().await;
}
#[tokio::test]
async fn renewed_pair_publishes_one_revision_and_preserves_bindings_and_old_files() {
let (app, _directory, original, output) = configured(true).await;
deploy_renewal(&app, &original, &output, true)
.await
.unwrap();
let snapshot = app.config.load_full();
assert_eq!(snapshot.envelope.revision, 1);
let applied = &snapshot.envelope.config.tls.certificates[0];
assert_eq!(applied.cert_file, "acme-next.pem");
assert_eq!(applied.hosts, original.hosts);
assert!(applied.issuer.as_ref().unwrap().auto_renew);
assert_eq!(
app.store.load().await.unwrap().config.tls.certificates[0].cert_file,
"acme-next.pem"
);
assert!(app
.policy
.cert_dir
.as_ref()
.unwrap()
.join("acme-old.key")
.exists());
stop(app).await;
}
#[tokio::test]
async fn invalid_renewed_domain_never_changes_active_configuration() {
let (app, _directory, original, mut output) = configured(true).await;
output["domains"] = serde_json::json!(["unrelated.example.org"]);
assert!(deploy_renewal(&app, &original, &output, true)
.await
.is_err());
assert_eq!(app.config.load().envelope.revision, 0);
assert_eq!(
app.store.load().await.unwrap().config.tls.certificates[0].cert_file,
original.cert_file
);
stop(app).await;
}
#[tokio::test]
async fn operator_opt_out_wins_over_an_older_automatic_job() {
let (app, _directory, mut original, output) = configured(false).await;
original.issuer.as_mut().unwrap().auto_renew = true;
assert_eq!(
deploy_renewal(&app, &original, &output, true)
.await
.unwrap_err(),
"acme.configuration_changed"
);
assert_eq!(app.config.load().envelope.revision, 0);
// Explicit Check now remains allowed when scheduled checks are disabled.
deploy_renewal(&app, &original, &output, false)
.await
.unwrap();
assert!(
!app.config.load().envelope.config.tls.certificates[0]
.issuer
.as_ref()
.unwrap()
.auto_renew
);
stop(app).await;
}
#[tokio::test]
async fn saved_renewal_observations_are_persisted_in_sqlite_without_provider_credentials() {
let (app, _directory, _original, _output) = configured(true).await;
app.acme.renewals.lock().unwrap().insert(
"site".into(),
RenewalState {
last_checked: 100,
next_check: 43200,
status: "complete".into(),
message: "acme.not_due".into(),
..Default::default()
},
);
app.acme.persist(&app.store).await;
let saved = app.store.load_certificate_renewals().await.unwrap();
let state = saved.get("site").unwrap();
assert_eq!(state.next_check, 43200);
assert_eq!(state.status, "complete");
assert_eq!(state.message, "acme.not_due");
stop(app).await;
}
}