use std::collections::HashMap;
use workload_spec::validate::{resolve_env_from_mesh, MeshError, MeshResolver};
use workload_spec::*;
struct FakeMeshResolver {
by_ident: HashMap<String, Vec<u16>>,
}
impl FakeMeshResolver {
fn new() -> Self {
Self {
by_ident: HashMap::new(),
}
}
fn with(mut self, ident: &str, ports: Vec<u16>) -> Self {
self.by_ident.insert(ident.into(), ports);
self
}
}
impl MeshResolver for FakeMeshResolver {
fn resolve(&self, ident: &MeshIdent, kind: MeshLookup) -> Result<String, MeshError> {
let ports = self
.by_ident
.get(&ident.0)
.ok_or_else(|| MeshError::NotDeployed {
ident: ident.0.clone(),
})?;
match kind {
MeshLookup::Host => Ok(ident.0.clone()),
MeshLookup::Port => {
let p = ports.first().ok_or(MeshError::NoPorts {
ident: ident.0.clone(),
lookup: kind,
})?;
Ok(p.to_string())
}
MeshLookup::Url => {
let p = ports.first().ok_or(MeshError::NoPorts {
ident: ident.0.clone(),
lookup: kind,
})?;
Ok(format!("http://{}:{}", ident.0, p))
}
}
}
}
#[test]
fn from_mesh_round_trips_through_serde() {
let original = EnvValue::FromMesh {
ident: MeshIdent("noisetable-db.pdx".into()),
kind: MeshLookup::Url,
};
let json = serde_json::to_string(&original).expect("serialize");
let back: EnvValue = serde_json::from_str(&json).expect("deserialize");
assert_eq!(original, back);
}
#[test]
fn url_renders_first_port_with_http_prefix() {
let resolver = FakeMeshResolver::new().with("noisetable-db.pdx", vec![5432, 9100]);
let value = resolver
.resolve(&MeshIdent("noisetable-db.pdx".into()), MeshLookup::Url)
.expect("resolve");
assert_eq!(value, "http://noisetable-db.pdx:5432");
}
#[test]
fn host_renders_bare_ident() {
let resolver = FakeMeshResolver::new().with("noisetable-db.pdx", vec![5432]);
let value = resolver
.resolve(&MeshIdent("noisetable-db.pdx".into()), MeshLookup::Host)
.expect("resolve");
assert_eq!(value, "noisetable-db.pdx");
}
#[test]
fn port_renders_first_port_as_string() {
let resolver = FakeMeshResolver::new().with("noisetable-db.pdx", vec![5432, 9100]);
let value = resolver
.resolve(&MeshIdent("noisetable-db.pdx".into()), MeshLookup::Port)
.expect("resolve");
assert_eq!(value, "5432");
}
#[test]
fn missing_ident_returns_not_deployed_error() {
let resolver = FakeMeshResolver::new(); let err = resolver
.resolve(&MeshIdent("noisetable-db.pdx".into()), MeshLookup::Url)
.unwrap_err();
assert_eq!(
err,
MeshError::NotDeployed {
ident: "noisetable-db.pdx".into(),
}
);
}
#[test]
fn deployed_but_no_ports_returns_no_ports_error() {
let resolver = FakeMeshResolver::new().with("portless.pdx", vec![]);
let err = resolver
.resolve(&MeshIdent("portless.pdx".into()), MeshLookup::Url)
.unwrap_err();
assert_eq!(
err,
MeshError::NoPorts {
ident: "portless.pdx".into(),
lookup: MeshLookup::Url,
}
);
}
#[test]
fn resolve_env_from_mesh_renders_only_from_mesh_entries() {
let env = vec![
EnvVar {
name: "APP_ENV".into(),
value: EnvValue::Literal {
value: "production".into(),
},
},
EnvVar {
name: "DB_PASSWORD".into(),
value: EnvValue::FromSecret {
secret: "creds".into(),
key: "password".into(),
},
},
EnvVar {
name: "DATABASE_URL".into(),
value: EnvValue::FromMesh {
ident: MeshIdent("noisetable-db.pdx".into()),
kind: MeshLookup::Url,
},
},
EnvVar {
name: "DATABASE_HOST".into(),
value: EnvValue::FromMesh {
ident: MeshIdent("noisetable-db.pdx".into()),
kind: MeshLookup::Host,
},
},
];
let resolver = FakeMeshResolver::new().with("noisetable-db.pdx", vec![5432]);
let resolved = resolve_env_from_mesh(&env, &resolver).expect("resolve env");
assert_eq!(resolved.len(), 4);
assert_eq!(
resolved[0].value,
EnvValue::Literal {
value: "production".into()
}
);
assert!(matches!(resolved[1].value, EnvValue::FromSecret { .. }));
assert_eq!(
resolved[2].value,
EnvValue::Literal {
value: "http://noisetable-db.pdx:5432".into()
}
);
assert_eq!(
resolved[3].value,
EnvValue::Literal {
value: "noisetable-db.pdx".into()
}
);
}
#[test]
fn resolve_env_from_mesh_propagates_missing_ident_error() {
let env = vec![EnvVar {
name: "DATABASE_URL".into(),
value: EnvValue::FromMesh {
ident: MeshIdent("missing.pdx".into()),
kind: MeshLookup::Url,
},
}];
let resolver = FakeMeshResolver::new();
let err = resolve_env_from_mesh(&env, &resolver).unwrap_err();
assert_eq!(
err,
MeshError::NotDeployed {
ident: "missing.pdx".into(),
}
);
}