use hws::types::{
ArtifactStoreInfo, AuthProviderView, CreatedPostgresDatabase, DeletedPostgresDatabase, DeploymentSpec, DeploymentStatus, DiskInventory, DiskState, JobRecord, MetricsResponse, NamespacePlugin, PluginInstalls, PluginView, PostgresDatabases,
UpstreamTrafficStatus, WorkflowList, WorkflowView,
};
use std::path::PathBuf;
fn fixture(name: &str) -> String {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("heyctl lives inside the app-lb workspace")
.join("testdata")
.join("wire")
.join(format!("{name}.json"));
std::fs::read_to_string(&path).unwrap_or_else(|e| {
panic!(
"{}: {e}\nrun `UPDATE_GOLDEN=1 cargo test -p app-lb wire_golden`",
path.display()
)
})
}
#[test]
fn workflow_view_understands_every_field() {
let w: WorkflowView = serde_json::from_str(&fixture("workflow")).expect("fixture parses");
assert!(
w.extra.is_empty(),
"heyctl does not understand these fields app-lb sends: {:?}\n\
Add them to WorkflowView in src/types.rs.",
w.extra.keys().collect::<Vec<_>>()
);
assert_eq!(w.id, "build");
assert_eq!(w.git_ref, "main", "`ref` on the wire, `git_ref` in Rust");
assert_eq!(w.path, ".ci/workflows/*.yml");
assert_eq!(w.network, "prod-runners");
assert!(w.auth.is_some(), "a SecretRef must survive as an opaque value");
assert_eq!(w.secrets_prefix.as_deref(), Some("ci/app"));
assert!(w.enabled);
}
#[test]
fn workflow_list_understands_every_field() {
let list: WorkflowList = serde_json::from_str(&fixture("workflow-list")).expect("parses");
assert!(
list.extra.is_empty(),
"unknown fields on the list envelope: {:?}",
list.extra.keys().collect::<Vec<_>>()
);
assert_eq!(list.workflows.len(), 1);
assert!(list.workflows[0].extra.is_empty());
assert_eq!(list.workflows[0].id, "build");
}
#[test]
fn a_minimal_workflow_arrives_with_its_defaults_filled() {
let w: WorkflowView = serde_json::from_str(&fixture("workflow-minimal")).expect("parses");
assert!(w.extra.is_empty(), "{:?}", w.extra.keys().collect::<Vec<_>>());
assert_eq!(w.git_ref, "main");
assert_eq!(w.path, ".ci/workflows/*.yml");
assert!(w.enabled);
assert!(w.auth.is_none());
}
#[test]
fn an_unknown_field_parses_and_is_reachable() {
let mut value: serde_json::Value =
serde_json::from_str(&fixture("workflow")).expect("fixture parses");
value["concurrency_group"] = serde_json::json!("prod");
let w: WorkflowView = serde_json::from_value(value).expect("a newer server still parses");
assert_eq!(w.id, "build", "the known fields still land");
assert_eq!(
w.extra.get("concurrency_group").and_then(|v| v.as_str()),
Some("prod"),
"an unknown field must be reachable, not dropped"
);
}
#[test]
fn upstream_traffic_status_understands_every_field() {
let status: UpstreamTrafficStatus =
serde_json::from_str(&fixture("upstream-traffic-status")).expect("fixture parses");
assert!(
status.extra.is_empty(),
"unknown upstream traffic fields: {:?}",
status.extra.keys().collect::<Vec<_>>()
);
assert_eq!(status.deployment_id, "stage");
assert_eq!(status.upstream, "us1.example.com:443");
assert_eq!(status.state, "draining");
assert!(status.healthy);
assert_eq!(status.in_flight, 3);
assert_eq!(status.reason.as_deref(), Some("regional maintenance"));
assert_eq!(status.started_at, Some(1_722_400_000));
}
#[test]
fn disk_inventory_understands_every_field() {
let inv: DiskInventory = serde_json::from_str(&fixture("disks")).expect("fixture parses");
assert!(
inv.extra.is_empty(),
"unknown inventory fields: {:?}",
inv.extra.keys().collect::<Vec<_>>()
);
assert!(inv.complete);
assert_eq!(inv.data_dir, "/var/lib/heyo");
assert_eq!(inv.ttl_secs, 604_800);
assert_eq!(inv.totals.disks, 3);
assert_eq!(inv.free_bytes, Some(12_884_901_888));
assert_eq!(inv.filesystem_bytes, Some(536_870_912_000));
assert!(inv.orphan_ttl_secs > 0, "the orphan TTL must survive the wire");
assert!(
inv.orphan_ttl_secs < inv.ttl_secs,
"an orphan must expire sooner than a recoverable disk, not later",
);
assert_eq!(inv.totals.reclaimable_bytes, 2_147_483_648);
assert!(
inv.totals.extra.is_empty(),
"unknown totals fields: {:?}",
inv.totals.extra.keys().collect::<Vec<_>>()
);
assert_eq!(inv.disks.len(), 3);
for d in &inv.disks {
assert!(
d.extra.is_empty(),
"unknown disk fields on {}: {:?}",
d.sandbox_id,
d.extra.keys().collect::<Vec<_>>()
);
for p in &d.parts {
assert!(
p.extra.is_empty(),
"unknown disk-part fields: {:?}",
p.extra.keys().collect::<Vec<_>>()
);
}
}
let running = &inv.disks[0];
assert_eq!(running.sandbox_id, "sb-1a2b3c4d");
assert_eq!(running.deployment.as_deref(), Some("web"));
assert_eq!(running.state, DiskState::Running);
assert!(running.claimed);
assert_eq!(running.bytes, 1_073_741_824);
assert_eq!(running.apparent_bytes, 8_589_934_592);
assert_eq!(running.held_by.as_deref(), Some("in use by a running sandbox"));
assert_eq!(running.parts.len(), 2);
assert_eq!(running.roots, vec!["run/sb-1a2b3c4d".to_string()]);
let orphan = &inv.disks[1];
assert_eq!(orphan.state, DiskState::Orphan);
assert_eq!(orphan.deployment, None);
assert_eq!(orphan.held_by, None);
assert_eq!(orphan.expires_at, Some(1_759_604_800));
let pinned = &inv.disks[2];
assert_eq!(pinned.state, DiskState::Stopped);
assert!(pinned.retain);
assert_eq!(pinned.note.as_deref(), Some("keeping for the incident postmortem"));
assert_eq!(pinned.expires_at, None);
}
#[test]
fn an_unknown_disk_state_degrades_to_unknown() {
let mut value: serde_json::Value =
serde_json::from_str(&fixture("disks")).expect("fixture parses");
value["disks"][0]["state"] = serde_json::json!("quiescing");
let inv: DiskInventory = serde_json::from_value(value).expect("a newer server still parses");
assert_eq!(inv.disks[0].state, DiskState::Unknown);
assert_eq!(inv.disks[1].state, DiskState::Orphan, "the other rows are unaffected");
}
#[test]
fn metrics_response_understands_every_field() {
let m: MetricsResponse =
serde_json::from_str(&fixture("metrics-response")).expect("fixture parses");
assert!(
m.extra.is_empty(),
"unknown metrics fields: {:?}",
m.extra.keys().collect::<Vec<_>>()
);
assert!(
m.global.extra.is_empty(),
"unknown global-metrics fields: {:?}",
m.global.extra.keys().collect::<Vec<_>>()
);
assert!(
m.daemon.extra.is_empty(),
"unknown daemon fields: {:?}",
m.daemon.extra.keys().collect::<Vec<_>>()
);
assert!(!m.daemon.reachable, "the fixture records an unreachable daemon");
assert!(
m.daemon.last_error.as_deref().is_some_and(|e| e.contains("deployed-sandboxes")),
"the reason must survive the wire, not just the boolean",
);
let a = &m.global.autoscale;
assert!(
a.extra.is_empty(),
"unknown autoscale fields: {:?}",
a.extra.keys().collect::<Vec<_>>()
);
assert_eq!(a.vms_created, 2);
assert_eq!(a.boot_timeouts, 1, "the field that had gone missing");
assert_eq!(a.create_failures, 1);
assert_eq!(
a.last_create_error.as_deref(),
Some("api error (500): No space left on device"),
"the reason a pool is stuck must survive the wire",
);
for d in &m.deployments {
assert!(
d.metrics.autoscale.extra.is_empty(),
"unknown per-deployment autoscale fields: {:?}",
d.metrics.autoscale.extra.keys().collect::<Vec<_>>()
);
}
}
#[test]
fn guest_mounts_survive_the_wire() {
let d: DeploymentStatus =
serde_json::from_str(&fixture("deployment-status-vm")).expect("fixture parses");
let vm = d.spec.vm.expect("the vm fixture has a template");
assert_eq!(vm.mounts.len(), 1, "the mount must not be dropped");
let m = &vm.mounts[0];
assert_eq!(m.path, "/data/corpus");
assert_eq!(m.store, "http://127.0.0.1:8080");
assert_eq!(m.artifact_ref, "corpus-2026-08", "`ref` on the wire");
assert_eq!(m.strip_components, Some(1));
assert!(m.read_only, "the fixture's mount is read-only");
assert!(m.auth.is_some(), "a SecretRef must survive as an opaque value");
assert!(
m.digest.as_deref().is_some_and(|d| d.len() == 64),
"the resolved digest is what says which bytes the guests hold",
);
assert!(m.summary().contains("/data/corpus"));
}
#[test]
fn a_mount_pull_reports_every_mount() {
let j: JobRecord = serde_json::from_str(&fixture("job-mount-pull")).expect("fixture parses");
assert!(j.is_mount_pull(), "kind is `mount-pull`, not `artifact-pull`");
assert_eq!(j.mounts.len(), 2);
let fetched = &j.mounts[0];
assert_eq!(fetched.path, "/data/corpus");
assert_eq!(fetched.artifact_ref, "corpus-2026-08", "`ref` on the wire");
assert_eq!(fetched.files, Some(1_204));
assert_eq!(fetched.bytes, Some(734_003_200));
assert_eq!(fetched.unpacked, Some(2_147_483_648));
assert!(fetched.changed, "this one moved the pool");
assert!(fetched.tree.is_some());
let reused = &j.mounts[1];
assert!(reused.reused);
assert!(!reused.changed);
assert_eq!(reused.bytes, Some(0));
assert_eq!(reused.files, None, "nothing was unpacked, which is not zero files");
assert_eq!(j.result_summary(), "1/2 updated");
assert_eq!(j.target_summary(), "/data/corpus,/opt/models");
}
#[test]
fn a_jwt_gate_survives_the_wire() {
let d: DeploymentStatus =
serde_json::from_str(&fixture("deployment-status-jwt")).expect("fixture parses");
let gate = d.spec.auth.expect("the fixture is gated");
assert!(gate.accepts_jwt(), "the provider list carries `jwt`");
assert!(
gate.providers().iter().any(|p| p == "google"),
"the fixture is the mixed gate: a person signs in, a program presents a token",
);
let jwt = gate.jwt.expect("the verification policy must not be dropped");
assert_eq!(jwt.issuer, "auth-service");
assert_eq!(jwt.audience.as_deref(), Some("heyo-app"));
assert_eq!(jwt.algorithms, vec!["HS256"]);
assert_eq!(jwt.subject_claim, "userId", "the Heyo auth API's subject is not `sub`");
assert_eq!(jwt.cookie.as_deref(), Some("heyo_access_token"));
assert_eq!(jwt.leeway_secs, Some(30));
assert!(jwt.secret.is_some(), "a SecretRef must survive as an opaque value");
assert_eq!(jwt.require.len(), 2);
let summary = jwt.require_summary();
assert!(summary.contains("role=user|admin"), "{summary}");
assert!(summary.contains("accountId=acct_7f3c"), "{summary}");
assert!(jwt.key_summary().contains("heyo-auth"), "{}", jwt.key_summary());
}
#[test]
fn auth_provider_view_understands_every_field() {
let heyo: AuthProviderView =
serde_json::from_str(&fixture("auth-provider-heyo")).expect("fixture parses");
assert!(
heyo.extra.is_empty(),
"heyctl does not understand these fields app-lb sends: {:?}\n\
Add them to AuthProviderView in src/types.rs.",
heyo.extra.keys().collect::<Vec<_>>()
);
assert_eq!(heyo.name, "heyo");
assert_eq!(heyo.namespace, "team-a");
assert_eq!(heyo.providers(), vec!["jwt".to_string()]);
let jwt = heyo.jwt.as_ref().expect("a jwt provider carries its policy");
assert_eq!(jwt.issuer, "auth-service");
assert_eq!(jwt.subject_claim, "userId", "the Heyo auth API's subject is not `sub`");
assert_eq!(heyo.admits(), "role=user|admin");
assert_eq!(heyo.trust_summary(), "auth-service");
let google: AuthProviderView =
serde_json::from_str(&fixture("auth-provider-google")).expect("fixture parses");
assert!(
google.extra.is_empty(),
"unknown fields on the google provider: {:?}",
google.extra.keys().collect::<Vec<_>>()
);
assert_eq!(google.providers(), vec!["google".to_string()]);
assert!(google.client_secret.is_some(), "a SecretRef must survive as a reference");
assert_eq!(google.admits(), "@example.com");
assert_eq!(google.cookie_domain.as_deref(), Some(".example.com"));
}
#[test]
fn an_inherited_gate_names_the_provider_it_came_from() {
let spec: DeploymentSpec =
serde_json::from_str(&fixture("deployment-inherited-gate")).expect("fixture parses");
let gate = spec.auth.as_ref().expect("the fixture has a gate");
assert_eq!(gate.provider_ref.as_deref(), Some("corp-google"));
assert_eq!(spec.namespace(), "team-a", "the provider is looked up in this namespace");
assert!(gate.client_id.is_none(), "an inheriting gate carries no identity of its own");
}
#[test]
fn plugin_view_understands_every_field() {
let plugins: Vec<PluginView> = serde_json::from_str(&fixture("plugins")).expect("fixture parses");
for p in &plugins {
assert!(
p.extra.is_empty(),
"heyctl does not understand these fields app-lb sends: {:?}\n\
Add them to PluginView in src/types.rs.",
p.extra.keys().collect::<Vec<_>>()
);
}
let on = &plugins[0];
assert_eq!(on.id, "example");
assert!(on.enabled);
assert_eq!(on.last_error.as_deref(), Some("connection refused"), "enabled and failing is a state");
assert_eq!(on.config["url"], "http://127.0.0.1:34199");
assert!(on.per_namespace);
assert_eq!(on.installed_in, ["team-a"]);
assert_eq!(on.dashboard.as_deref(), Some("ui"));
assert!(plugins[1].dashboard.is_none());
assert!(!plugins[1].enabled);
assert!(!plugins[1].per_namespace);
}
#[test]
fn namespace_plugin_understands_every_field() {
let plugins: Vec<NamespacePlugin> =
serde_json::from_str(&fixture("namespace-plugins")).expect("fixture parses");
for p in &plugins {
assert!(
p.extra.is_empty(),
"unknown namespace plugin fields: {:?}",
p.extra.keys().collect::<Vec<_>>()
);
}
let obs = &plugins[0];
assert_eq!(obs.id, "obs");
assert!(obs.enabled && obs.installed);
assert_eq!(obs.installed_at, Some(1_760_000_000));
assert_eq!(obs.installed_by.as_deref(), Some("token:0123456789ab"));
assert_eq!(obs.dashboard.as_deref(), Some("ui"));
let other = &plugins[1];
assert!(!other.enabled && !other.installed);
assert!(other.installed_at.is_none() && other.config.is_none());
}
#[test]
fn plugin_installs_understands_every_field() {
let i: PluginInstalls =
serde_json::from_str(&fixture("plugin-installs")).expect("fixture parses");
assert!(i.extra.is_empty(), "{:?}", i.extra.keys().collect::<Vec<_>>());
assert_eq!(i.plugin, "obs");
assert!(i.enabled);
assert_eq!(i.namespaces, ["team-a", "team-b"]);
for (ns, install) in &i.installs {
assert!(install.extra.is_empty(), "{ns}: {:?}", install.extra.keys().collect::<Vec<_>>());
}
assert_eq!(i.installs["team-a"].installed_by.as_deref(), Some("user:u_123"));
assert!(i.installs["team-b"].installed_by.is_none(), "the operator installs unattributed");
}
#[cfg(feature = "test-util")]
#[test]
fn the_image_inventory_reads_every_field() {
use hws::{ImageInventory, ImageSweep};
let inv: ImageInventory = serde_json::from_str(&fixture("images")).expect("fixture parses");
assert!(inv.extra.is_empty(), "unmodelled: {:?}", inv.extra.keys().collect::<Vec<_>>());
assert!(inv.complete);
assert_eq!(inv.delete_supported, Some(true));
assert_eq!((inv.cache_bytes, inv.cache_budget_bytes), (Some(0), Some(21_474_836_480)));
assert_eq!((inv.cache_hits, inv.cache_misses), (4, 1));
for i in &inv.images {
assert!(i.extra.is_empty(), "{}: unmodelled {:?}", i.name, i.extra.keys().collect::<Vec<_>>());
}
let pulled = inv.images.iter().find(|i| i.name == "img-c74abee2ce8409f1").unwrap();
assert_eq!(pulled.source, "pull");
assert_eq!(pulled.artifact_ref.as_deref(), Some("heyo/alpine:3.24"));
assert_eq!(
pulled.references.iter().map(ToString::to_string).collect::<Vec<_>>(),
vec!["deployment/web", "rollout of web", "job job-1 of api"]
);
let gone = inv.images.iter().find(|i| i.tier == "offloaded").unwrap();
assert!(!gone.present);
assert!(gone.offloaded_to.as_deref().unwrap().contains("app-lb-offload:"));
let sweep: ImageSweep = serde_json::from_str(&fixture("images-sweep")).expect("fixture parses");
assert_eq!(sweep.offloaded, vec!["img-0000000000000000"]);
assert_eq!(sweep.failed[0].0, "img-1111111111111111");
}
mod requests {
use hws::Client;
#[tokio::test]
async fn image_requests_have_the_routes_shapes() {
let (c, stub) = client(
Stub::new()
.json(200, json!({"images": []}))
.json(200, json!({"offloaded": [], "failed": []}))
.json(200, json!({"name": "img-a"}))
.json(200, json!({"name": "img-a", "pinned": true}))
.reply(204, ""),
);
c.images().await.unwrap();
c.sweep_images().await.unwrap();
c.offload_image("img-a").await.unwrap();
assert!(c.pin_image("img-a", true).await.unwrap().pinned);
c.delete_image("img-a").await.unwrap();
let calls = stub.calls();
let seen: Vec<_> = calls.iter().map(|c| (c.method, c.path.as_str())).collect();
assert_eq!(
seen,
vec![
(Method::Get, "/images"),
(Method::Post, "/images/sweep"),
(Method::Post, "/images/img-a/offload"),
(Method::Patch, "/images/img-a"),
(Method::Delete, "/images/img-a"),
]
);
assert_eq!(calls[3].body, Some(json!({"pinned": true})));
}
use hws::transport::Method;
use hws::transport::stub::Stub;
use serde_json::json;
use std::sync::Arc;
fn client(stub: Stub) -> (Client, Arc<Stub>) {
let stub = Arc::new(stub);
(Client::with_transport(stub.clone()), stub)
}
#[tokio::test]
async fn a_job_is_waited_on_through_its_deployment() {
let rec = |status: &str| {
json!([
{"id": "job-other", "deployment": "web", "kind": "artifact-pull", "status": "succeeded",
"started_at": 1, "finished_at": 2, "log": []},
{"id": "job-1", "deployment": "web", "kind": "artifact-pull", "status": status,
"started_at": 1, "finished_at": null, "log": ["pulling"]}
])
};
let (c, stub) = client(Stub::new().json(200, rec("running")).json(200, rec("succeeded")));
let done = c
.wait_for_job("job-1")
.in_deployment("web")
.poll_every(std::time::Duration::from_millis(1))
.await
.unwrap();
assert_eq!(done.status, "succeeded");
let paths: Vec<_> = stub.calls().into_iter().map(|c| (c.method, c.path)).collect();
assert_eq!(paths, vec![(Method::Get, "/deployments/web/jobs".to_string()); 2]);
let (c, _) = client(Stub::new().json(200, json!([])));
let gone = c.wait_for_job("job-9").in_deployment("web").await.unwrap_err();
assert!(matches!(gone, hws::Error::NotFound { .. }), "{gone:?}");
}
#[tokio::test]
async fn whoami_names_the_namespace_a_token_is_confined_to() {
let (c, stub) = client(
Stub::new()
.json(200, json!({
"caller": "app-token", "admin_scope": "admin", "fleet": false, "confined": true,
"may": {"read_view_routes": true, "use_admin_routes": true},
"token": {"id": "0123456789ab", "name": "ci"}, "namespace": "team-a",
"deployments": [], "expires_at": null, "expires_in_secs": null, "note": "…"
}))
.json(200, json!({
"caller": "federated", "admin_scope": "view", "fleet": false, "confined": true,
"may": {"read_view_routes": true, "use_admin_routes": false},
"subject": {"user_id": "u"}, "namespaces": {"a": "view", "b": "admin"}
}))
.json(200, json!({"caller": "operator", "admin_scope": "admin", "fleet": true, "confined": false})),
);
let me = c.whoami().await.unwrap();
assert!(me.extra.is_empty(), "{:?}", me.extra.keys().collect::<Vec<_>>());
assert_eq!(me.sole_namespace(), Some("team-a"));
assert_eq!(me.token.unwrap().name, "ci");
assert_eq!(c.whoami().await.unwrap().sole_namespace(), None, "two namespaces, none assumed");
assert_eq!(c.whoami().await.unwrap().sole_namespace(), None, "the operator is everywhere");
assert_eq!(stub.calls()[0].path, "/whoami");
}
#[tokio::test]
async fn a_rollout_carries_its_idempotency_key_and_revision() {
let op = json!({"operation_id": "op-1", "deployment": "web", "source_revision": "r1",
"target_spec_sha256": "abc", "status": "running", "phase": "preparing",
"readiness_verified": false, "previous_stopped": false, "error": null,
"preparation_stage": null});
let mut done = op.clone();
done["status"] = json!("succeeded");
let (c, stub) = client(Stub::new().json(202, op).json(200, done));
let spec = json!({"id": "web", "vm": {"image": "x"}});
let started = c.start_rollout("web", "op-1", "r1", &spec).await.unwrap();
assert!(started.extra.is_empty(), "{:?}", started.extra.keys().collect::<Vec<_>>());
assert!(!started.is_finished());
assert!(c.rollout("web", "op-1").await.unwrap().is_finished());
let calls = stub.calls();
assert_eq!(calls[0].method, Method::Post);
assert_eq!(calls[0].path, "/deployments/web/rollouts");
assert_eq!(
calls[0].body,
Some(json!({"operation_id": "op-1", "expected_revision": "r1", "spec": spec}))
);
assert_eq!(calls[1].path, "/deployments/web/rollouts/op-1");
}
#[tokio::test]
async fn discovery_status_is_camel_case_on_the_wire() {
let (c, stub) = client(Stub::new().json(200, json!({
"serviceId": "web", "version": 3,
"upstreams": [{"peer": "10.0.0.2:8080", "draining": false, "inFlight": 2}]
})));
let s = c.discovery_status("web", true).await.unwrap();
assert!(s.extra.is_empty(), "{:?}", s.extra.keys().collect::<Vec<_>>());
assert_eq!(s.service_id, "web");
assert_eq!(s.upstreams[0].in_flight, 2);
assert_eq!(stub.calls()[0].path, "/deployments/web/discovery-status?staged=true");
}
}
#[test]
fn mirrored_token_summary_understands_every_field() {
let t: hws::types::TokenSummary =
serde_json::from_str(&fixture("token-summary-mirrored")).expect("fixture parses");
assert!(
t.extra.is_empty(),
"heyctl does not understand these fields app-lb sends: {:?}\n\
Add them to TokenSummary in src/types.rs.",
t.extra.keys().collect::<Vec<_>>()
);
assert!(t.fleet);
assert_eq!(t.mirrored_from.as_deref(), Some("us2"));
}
#[test]
fn postgres_plugin_answers_are_understood() {
let list: PostgresDatabases =
serde_json::from_str(&fixture("postgres-databases")).expect("fixture parses");
assert!(
list.extra.is_empty(),
"unknown list fields: {:?}",
list.extra.keys().collect::<Vec<_>>()
);
let db = &list.databases[0];
assert!(
db.extra.is_empty(),
"unknown database fields: {:?}",
db.extra.keys().collect::<Vec<_>>()
);
assert_eq!(
(db.name.as_str(), db.database.as_str()),
("app", "ns_team_a_app")
);
assert_eq!(
(db.live, db.secret.as_deref()),
(Some(true), Some("pg-app"))
);
assert_eq!((list.port, list.limit), (6432, 2));
let created: CreatedPostgresDatabase =
serde_json::from_str(&fixture("postgres-database-created")).expect("fixture parses");
assert!(
created.extra.is_empty(),
"unknown create fields: {:?}",
created.extra.keys().collect::<Vec<_>>()
);
assert_eq!(created.password, "p@ss/w:rd");
assert!(
created
.url
.as_deref()
.is_some_and(|u| u.starts_with("postgres://"))
);
assert_eq!(created.status, "provisioning");
let deleted: DeletedPostgresDatabase =
serde_json::from_str(&fixture("postgres-database-deleted")).expect("fixture parses");
assert!(
deleted.extra.is_empty(),
"unknown delete fields: {:?}",
deleted.extra.keys().collect::<Vec<_>>()
);
assert_eq!(deleted.deleted, "app");
assert_eq!(deleted.secret_kept.as_deref(), Some("pg-app"));
}
#[test]
fn artifact_store_info_is_understood() {
let info: ArtifactStoreInfo =
serde_json::from_str(&fixture("artifact-store-info")).expect("fixture parses");
assert!(info.extra.is_empty(), "unknown fields: {:?}", info.extra.keys().collect::<Vec<_>>());
assert!(info.available && info.writable);
assert_eq!((info.namespace.as_str(), info.prefix.as_str()), ("team-a", "team-a/"));
}