use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio_postgres::Config;
use super::harness::{
PROVIDER_MATERIAL, ROTATED_MATERIAL, first, live_material, state_pinning, sweep,
};
use crate::backends::control_plane::postgres::{ControlPlaneSettings, PostgresControlPlane};
use crate::backends::control_plane::{ControlPlaneError, ControlPlaneStore};
use crate::desired_state::{
DesiredState, ExpectedRevision, ResourceVersionNumber, RevisionId, fixtures,
};
async fn journal() -> Option<(PostgresControlPlane, String)> {
let dsn = crate::test_services::postgres_dsn()?;
let schema = format!(
"secret_redaction_{}",
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("a monotonic wall clock")
.as_nanos()
);
let mut config: Config = dsn.parse().expect("a parseable test DSN");
config.connect_timeout(Duration::from_secs(5));
let (client, connection) = config
.connect(crate::usage::tls_connector())
.await
.expect("a connection to create the test schema");
tokio::spawn(async move {
let _ = connection.await;
});
client
.batch_execute(&format!("CREATE SCHEMA {schema}"))
.await
.expect("a fresh test schema");
let settings = ControlPlaneSettings {
schema: Some(schema.clone()),
operation_timeout: Duration::from_secs(10),
connect_timeout: Duration::from_secs(5),
..ControlPlaneSettings::default()
};
let store = PostgresControlPlane::connect(&dsn, settings)
.await
.expect("a migrated journal");
Some((store, schema))
}
async fn dump(schema: &str) -> String {
let dsn = crate::test_services::postgres_dsn().expect("a configured DSN");
let mut config: Config = dsn.parse().expect("a parseable test DSN");
config.connect_timeout(Duration::from_secs(5));
let (client, connection) = config
.connect(crate::usage::tls_connector())
.await
.expect("a connection to read the schema back");
tokio::spawn(async move {
let _ = connection.await;
});
let tables = client
.query(
"SELECT tablename FROM pg_tables WHERE schemaname = $1 ORDER BY tablename",
&[&schema.to_owned()],
)
.await
.expect("the schema's tables");
assert!(
!tables.is_empty(),
"the journal's schema has no tables, so the sweep would be vacuous"
);
let mut dumped = String::new();
for table in tables {
let name: String = table.get(0);
dumped.push_str(&format!("-- {schema}.{name}\n"));
let rows = client
.query(
&format!(r#"SELECT t::text FROM "{schema}"."{name}" t"#),
&[],
)
.await
.expect("a table's rows as text");
for row in rows {
let rendered: Option<String> = row.get(0);
dumped.push_str(rendered.as_deref().unwrap_or("(null)"));
dumped.push('\n');
}
}
dumped
}
async fn publish(
store: &PostgresControlPlane,
key: &str,
expected: ExpectedRevision,
state: DesiredState,
) -> Result<RevisionId, ControlPlaneError> {
store
.publish_revision(fixtures::candidate(expected, key, state))
.await
.map(|manifest| manifest.id)
}
#[tokio::test]
async fn no_durable_row_or_read_carries_secret_material() {
let Some((store, schema)) = journal().await else {
return;
};
let sweep = sweep();
let rotated = first().rotated();
let resolved =
live_material(&[(first(), PROVIDER_MATERIAL), (rotated, ROTATED_MATERIAL)]).await;
for (label, plaintext) in [("provider", &resolved[0]), ("rotated", &resolved[1])] {
sweep.assert_present("the material resolved out of the store", label, plaintext);
}
let first_revision = publish(
&store,
"publish-first",
ExpectedRevision::Empty,
state_pinning(first(), ResourceVersionNumber::FIRST),
)
.await
.expect("the first revision publishes");
let rotation = publish(
&store,
"publish-rotation",
ExpectedRevision::Exactly(first_revision),
state_pinning(rotated, ResourceVersionNumber::FIRST.next()),
)
.await
.expect("the rotation publishes");
let replayed = publish(
&store,
"publish-rotation",
ExpectedRevision::Exactly(first_revision),
state_pinning(rotated, ResourceVersionNumber::FIRST.next()),
)
.await
.expect("a replay returns the original outcome");
assert_eq!(replayed, rotation);
let conflict = publish(
&store,
"publish-rotation",
ExpectedRevision::Exactly(first_revision),
state_pinning(first(), ResourceVersionNumber::FIRST.next()),
)
.await
.expect_err("a reused key carrying different state is refused");
assert!(
matches!(conflict, ControlPlaneError::IdempotencyKeyReused { .. }),
"{conflict:?}"
);
sweep.assert_absent("a refused replay", &conflict.to_string());
sweep.assert_absent("a refused replay's Debug", &format!("{conflict:?}"));
for id in [first_revision, rotation] {
let manifest = store.load_manifest(id).await.expect("a retained manifest");
sweep.assert_absent("a revision manifest", &format!("{manifest:?}"));
let loaded = store.load_revision(id).await.expect("a hydrated revision");
sweep.assert_absent("a hydrated revision", &format!("{loaded:?}"));
sweep.assert_absent(
"a hydrated revision's state",
&format!("{:?}", loaded.state()),
);
for event in store.audit_trail(id).await.expect("an audit trail") {
sweep.assert_absent("an audit event", &format!("{event:?}"));
}
}
let desired = store
.load_desired_revision()
.await
.expect("the head hydrates")
.expect("a published head");
sweep.assert_absent("the desired revision", &format!("{desired:?}"));
sweep.assert_absent("the journal's durable rows", &dump(&schema).await);
let rows = dump(&schema).await;
let identifier = rotated.secret.to_string();
let hexed: String = identifier
.bytes()
.map(|byte| format!("{byte:02x}"))
.collect();
assert!(
rows.contains(&identifier) || rows.contains(&hexed),
"the credential's secret reference must be durable, or the sweep proves nothing"
);
drop(resolved);
}