use serde_json::{Value, json};
use crate::region::MICROVM_REGIONS;
pub const MODEL_API_VERSION: &str = "2025-09-09";
pub const MAX_RUN_HOOK_PAYLOAD_BYTES: usize = 4096;
pub const DOCUMENTED_RUN_HOOK_PAYLOAD_BYTES: usize = 16_384;
pub const MAX_IMAGE_NAME_LEN: usize = 64;
pub const IMAGE_NAME_PATTERN: &str = "[a-zA-Z0-9-_]+";
pub const MAX_VERSION_LEN: usize = 2048;
pub const VERSION_PATTERN: &str = "[^\\s]+";
pub const MAX_NON_BLANK_LEN: usize = 2048;
pub const NON_BLANK_PATTERN: &str = "[^\\s]+";
pub const MAX_IDENTIFIER_LEN: usize = 256;
pub const MAX_IMAGE_ARN_LEN: usize = 2048;
pub const MAX_TAG_KEY_LEN: usize = 128;
pub const MAX_TAG_VALUE_LEN: usize = 256;
pub const TAG_COMPONENT_PATTERN: &str = "([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)";
pub const MAX_LOG_GROUP_LEN: usize = 512;
pub const LOG_GROUP_PATTERN: &str = "[a-zA-Z0-9_\\-/.#]+";
pub const MAX_LOG_STREAM_LEN: usize = 512;
pub const LOG_STREAM_PATTERN: &str = "[^:*]*";
pub const MAX_USER_LOG_STREAM_LEN: usize = 495;
pub const MIN_ROLE_ARN_LEN: usize = 20;
pub const MAX_ROLE_ARN_LEN: usize = 2048;
pub const ROLE_ARN_PATTERN: &str = "arn:aws[a-z\\-]*:iam::[0-9]{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+";
pub const MIN_PORT: u16 = 1;
pub const MAX_PORT: u16 = 65_535;
pub const MIN_IDLE_DURATION_SEC: u32 = 60;
pub const MAX_DURATION_SEC: u32 = 28_800;
pub const MAX_MICROVM_HOOK_TIMEOUT_SEC: u32 = 60;
pub const MAX_IMAGE_HOOK_TIMEOUT_SEC: u32 = 3_600;
pub const MAX_HOOK_PORT: u32 = 65_535;
pub const CAPABILITIES: [&str; 1] = ["ALL"];
pub const ARCHITECTURES: [&str; 1] = ["ARM_64"];
pub const MAX_NETWORK_CONNECTORS: usize = 10;
pub const MAX_NETWORK_CONNECTOR_LEN: usize = 2048;
pub const MAX_IMAGE_EGRESS_CONNECTORS: usize = 1;
pub const IMAGE_VERSION_STATUSES: [&str; 2] = ["ACTIVE", "INACTIVE"];
pub const HOOK_STATES: [&str; 2] = ["DISABLED", "ENABLED"];
pub const MICROVM_STATES: [&str; 6] = [
"PENDING",
"RUNNING",
"SUSPENDING",
"SUSPENDED",
"TERMINATING",
"TERMINATED",
];
pub const IMAGE_STATES: [&str; 9] = [
"CREATING",
"CREATED",
"CREATE_FAILED",
"UPDATING",
"UPDATED",
"UPDATE_FAILED",
"DELETING",
"DELETE_FAILED",
"DELETED",
];
pub const IMAGE_VERSION_STATES: [&str; 7] = [
"PENDING",
"IN_PROGRESS",
"SUCCESSFUL",
"FAILED",
"DELETING",
"DELETED",
"DELETE_FAILED",
];
pub const BUILD_STATES: [&str; 4] = ["PENDING", "IN_PROGRESS", "SUCCESSFUL", "FAILED"];
pub const CHIPSETS: [&str; 1] = ["GRAVITON"];
pub const MAX_RESOURCES: usize = 1;
pub const MAX_CLIENT_TOKEN_LEN: usize = 128;
pub const MODEL_IMAGE_READY_STATES: [&str; 2] = ["CREATED", "UPDATED"];
pub const TOLERATED_IMAGE_READY_STATES: [&str; 2] = ["ACTIVE", "AVAILABLE"];
pub const TERMINAL_STATES: [&str; 4] = ["TERMINATED", "TERMINATING", "SUSPENDED", "SUSPENDING"];
pub const DEAD_STATES: [&str; 2] = ["TERMINATED", "TERMINATING"];
pub fn is_valid_image_name(name: &str) -> bool {
!name.is_empty()
&& name.len() <= MAX_IMAGE_NAME_LEN
&& name
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
}
pub fn is_valid_log_group(group: &str) -> bool {
!group.is_empty()
&& group.len() <= MAX_LOG_GROUP_LEN
&& group
.bytes()
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-' | b'/' | b'.' | b'#'))
}
pub fn is_valid_tag_component(component: &str) -> bool {
component.chars().all(|c| {
c.is_alphabetic()
|| c.is_numeric()
|| matches!(c, ' ' | '\u{00a0}' | '\u{1680}' | '\u{2000}'..='\u{200a}'
| '\u{2028}' | '\u{2029}' | '\u{202f}' | '\u{205f}' | '\u{3000}')
|| matches!(c, '_' | '.' | ':' | '/' | '=' | '+' | '-' | '@')
})
}
pub fn is_valid_role_arn(arn: &str) -> bool {
let Some(rest) = arn.strip_prefix("arn:aws") else {
return false;
};
let Some(colon) = rest.find(':') else {
return false;
};
let (partition_suffix, rest) = rest.split_at(colon);
if !partition_suffix
.bytes()
.all(|b| b.is_ascii_lowercase() || b == b'-')
{
return false;
}
let Some(rest) = rest.strip_prefix(":iam::") else {
return false;
};
let Some(colon) = rest.find(':') else {
return false;
};
let (account, rest) = rest.split_at(colon);
if account.len() != 12 || !account.bytes().all(|b| b.is_ascii_digit()) {
return false;
}
let Some(name) = rest.strip_prefix(":role") else {
return false;
};
let name = name.strip_prefix('/').unwrap_or(name);
!name.is_empty()
&& name.bytes().all(|b| {
b.is_ascii_alphanumeric()
|| matches!(b, b'_' | b'+' | b'=' | b',' | b'.' | b'@' | b'-' | b'/')
})
}
pub fn as_json() -> Value {
let entries: Vec<(&str, Value)> = vec![
("MODEL_API_VERSION", json!(MODEL_API_VERSION)),
(
"MAX_RUN_HOOK_PAYLOAD_BYTES",
json!(MAX_RUN_HOOK_PAYLOAD_BYTES),
),
(
"DOCUMENTED_RUN_HOOK_PAYLOAD_BYTES",
json!(DOCUMENTED_RUN_HOOK_PAYLOAD_BYTES),
),
("MAX_IMAGE_NAME_LEN", json!(MAX_IMAGE_NAME_LEN)),
("IMAGE_NAME_PATTERN", json!(IMAGE_NAME_PATTERN)),
("MAX_VERSION_LEN", json!(MAX_VERSION_LEN)),
("VERSION_PATTERN", json!(VERSION_PATTERN)),
("MAX_NON_BLANK_LEN", json!(MAX_NON_BLANK_LEN)),
("NON_BLANK_PATTERN", json!(NON_BLANK_PATTERN)),
("MAX_IDENTIFIER_LEN", json!(MAX_IDENTIFIER_LEN)),
("MAX_IMAGE_ARN_LEN", json!(MAX_IMAGE_ARN_LEN)),
("MAX_TAG_KEY_LEN", json!(MAX_TAG_KEY_LEN)),
("MAX_TAG_VALUE_LEN", json!(MAX_TAG_VALUE_LEN)),
("TAG_COMPONENT_PATTERN", json!(TAG_COMPONENT_PATTERN)),
("MAX_LOG_GROUP_LEN", json!(MAX_LOG_GROUP_LEN)),
("LOG_GROUP_PATTERN", json!(LOG_GROUP_PATTERN)),
("MAX_LOG_STREAM_LEN", json!(MAX_LOG_STREAM_LEN)),
("LOG_STREAM_PATTERN", json!(LOG_STREAM_PATTERN)),
("MAX_USER_LOG_STREAM_LEN", json!(MAX_USER_LOG_STREAM_LEN)),
("MIN_ROLE_ARN_LEN", json!(MIN_ROLE_ARN_LEN)),
("MAX_ROLE_ARN_LEN", json!(MAX_ROLE_ARN_LEN)),
("ROLE_ARN_PATTERN", json!(ROLE_ARN_PATTERN)),
("MIN_PORT", json!(MIN_PORT)),
("MAX_PORT", json!(MAX_PORT)),
("MIN_IDLE_DURATION_SEC", json!(MIN_IDLE_DURATION_SEC)),
("MAX_DURATION_SEC", json!(MAX_DURATION_SEC)),
(
"MAX_MICROVM_HOOK_TIMEOUT_SEC",
json!(MAX_MICROVM_HOOK_TIMEOUT_SEC),
),
(
"MAX_IMAGE_HOOK_TIMEOUT_SEC",
json!(MAX_IMAGE_HOOK_TIMEOUT_SEC),
),
("MAX_HOOK_PORT", json!(MAX_HOOK_PORT)),
("CAPABILITIES", json!(CAPABILITIES)),
("ARCHITECTURES", json!(ARCHITECTURES)),
("MAX_NETWORK_CONNECTORS", json!(MAX_NETWORK_CONNECTORS)),
(
"MAX_NETWORK_CONNECTOR_LEN",
json!(MAX_NETWORK_CONNECTOR_LEN),
),
(
"MAX_IMAGE_EGRESS_CONNECTORS",
json!(MAX_IMAGE_EGRESS_CONNECTORS),
),
("IMAGE_VERSION_STATUSES", json!(IMAGE_VERSION_STATUSES)),
("HOOK_STATES", json!(HOOK_STATES)),
("MICROVM_STATES", json!(MICROVM_STATES)),
("IMAGE_STATES", json!(IMAGE_STATES)),
("IMAGE_VERSION_STATES", json!(IMAGE_VERSION_STATES)),
("BUILD_STATES", json!(BUILD_STATES)),
("CHIPSETS", json!(CHIPSETS)),
("MAX_RESOURCES", json!(MAX_RESOURCES)),
("MAX_CLIENT_TOKEN_LEN", json!(MAX_CLIENT_TOKEN_LEN)),
("MODEL_IMAGE_READY_STATES", json!(MODEL_IMAGE_READY_STATES)),
(
"TOLERATED_IMAGE_READY_STATES",
json!(TOLERATED_IMAGE_READY_STATES),
),
("TERMINAL_STATES", json!(TERMINAL_STATES)),
("DEAD_STATES", json!(DEAD_STATES)),
(
"MICROVM_REGIONS",
json!(
MICROVM_REGIONS
.iter()
.map(|region| region.as_str())
.collect::<Vec<_>>()
),
),
(
"SIZE_CLASSES",
json!(
crate::sizing::SIZE_CLASSES
.iter()
.map(|row| json!({
"baseline_mib": row.baseline_mib,
"baseline_vcpu": row.baseline_vcpu,
"peak_mib": row.peak_mib,
"peak_vcpu": row.peak_vcpu,
}))
.collect::<Vec<_>>()
),
),
];
Value::Object(
entries
.into_iter()
.map(|(key, value)| (key.to_string(), value))
.collect(),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn as_json_carries_every_key_the_drift_gate_reads() {
let emitted = as_json();
let object = emitted.as_object().expect("an object");
let mut keys: Vec<&str> = object.keys().map(String::as_str).collect();
keys.sort_unstable();
assert_eq!(
keys,
[
"ARCHITECTURES",
"BUILD_STATES",
"CAPABILITIES",
"CHIPSETS",
"DEAD_STATES",
"DOCUMENTED_RUN_HOOK_PAYLOAD_BYTES",
"HOOK_STATES",
"IMAGE_NAME_PATTERN",
"IMAGE_STATES",
"IMAGE_VERSION_STATES",
"IMAGE_VERSION_STATUSES",
"LOG_GROUP_PATTERN",
"LOG_STREAM_PATTERN",
"MAX_CLIENT_TOKEN_LEN",
"MAX_DURATION_SEC",
"MAX_HOOK_PORT",
"MAX_IDENTIFIER_LEN",
"MAX_IMAGE_ARN_LEN",
"MAX_IMAGE_EGRESS_CONNECTORS",
"MAX_IMAGE_HOOK_TIMEOUT_SEC",
"MAX_IMAGE_NAME_LEN",
"MAX_LOG_GROUP_LEN",
"MAX_LOG_STREAM_LEN",
"MAX_MICROVM_HOOK_TIMEOUT_SEC",
"MAX_NETWORK_CONNECTORS",
"MAX_NETWORK_CONNECTOR_LEN",
"MAX_NON_BLANK_LEN",
"MAX_PORT",
"MAX_RESOURCES",
"MAX_ROLE_ARN_LEN",
"MAX_RUN_HOOK_PAYLOAD_BYTES",
"MAX_TAG_KEY_LEN",
"MAX_TAG_VALUE_LEN",
"MAX_USER_LOG_STREAM_LEN",
"MAX_VERSION_LEN",
"MICROVM_REGIONS",
"MICROVM_STATES",
"MIN_IDLE_DURATION_SEC",
"MIN_PORT",
"MIN_ROLE_ARN_LEN",
"MODEL_API_VERSION",
"MODEL_IMAGE_READY_STATES",
"NON_BLANK_PATTERN",
"ROLE_ARN_PATTERN",
"SIZE_CLASSES",
"TAG_COMPONENT_PATTERN",
"TERMINAL_STATES",
"TOLERATED_IMAGE_READY_STATES",
"VERSION_PATTERN",
]
);
}
#[test]
fn the_emitted_values_are_the_measured_ones() {
let emitted = as_json();
assert_eq!(emitted["MODEL_API_VERSION"], "2025-09-09");
assert_eq!(emitted["MAX_RUN_HOOK_PAYLOAD_BYTES"], 4096);
assert_eq!(emitted["MAX_IMAGE_NAME_LEN"], 64);
assert_eq!(emitted["IMAGE_NAME_PATTERN"], "[a-zA-Z0-9-_]+");
assert_eq!(emitted["MAX_VERSION_LEN"], 2048);
assert_eq!(emitted["VERSION_PATTERN"], "[^\\s]+");
assert_eq!(emitted["MAX_NON_BLANK_LEN"], 2048);
assert_eq!(emitted["NON_BLANK_PATTERN"], "[^\\s]+");
assert_eq!(emitted["MAX_IDENTIFIER_LEN"], 256);
assert_eq!(emitted["MAX_IMAGE_ARN_LEN"], 2048);
assert_eq!(emitted["MAX_TAG_KEY_LEN"], 128);
assert_eq!(emitted["MAX_TAG_VALUE_LEN"], 256);
assert_eq!(
emitted["TAG_COMPONENT_PATTERN"],
"([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)"
);
assert_eq!(emitted["MAX_LOG_GROUP_LEN"], 512);
assert_eq!(emitted["LOG_GROUP_PATTERN"], "[a-zA-Z0-9_\\-/.#]+");
assert_eq!(emitted["MAX_LOG_STREAM_LEN"], 512);
assert_eq!(emitted["LOG_STREAM_PATTERN"], "[^:*]*");
assert_eq!(emitted["MAX_USER_LOG_STREAM_LEN"], 495);
assert_eq!(emitted["MIN_ROLE_ARN_LEN"], 20);
assert_eq!(emitted["MAX_ROLE_ARN_LEN"], 2048);
assert_eq!(
emitted["ROLE_ARN_PATTERN"],
"arn:aws[a-z\\-]*:iam::[0-9]{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+"
);
assert_eq!(emitted["MIN_PORT"], 1);
assert_eq!(emitted["MAX_PORT"], 65_535);
assert_eq!(emitted["MIN_IDLE_DURATION_SEC"], 60);
assert_eq!(emitted["MAX_DURATION_SEC"], 28_800);
assert_eq!(emitted["MAX_MICROVM_HOOK_TIMEOUT_SEC"], 60);
assert_eq!(emitted["MAX_IMAGE_HOOK_TIMEOUT_SEC"], 3_600);
assert_eq!(emitted["MAX_HOOK_PORT"], 65_535);
assert_eq!(emitted["CAPABILITIES"], json!(["ALL"]));
assert_eq!(emitted["ARCHITECTURES"], json!(["ARM_64"]));
assert_eq!(emitted["MAX_NETWORK_CONNECTORS"], 10);
assert_eq!(emitted["MAX_IMAGE_EGRESS_CONNECTORS"], 1);
assert_eq!(
emitted["IMAGE_VERSION_STATUSES"],
json!(["ACTIVE", "INACTIVE"])
);
assert_eq!(emitted["HOOK_STATES"], json!(["DISABLED", "ENABLED"]));
assert_eq!(
emitted["MICROVM_STATES"],
json!([
"PENDING",
"RUNNING",
"SUSPENDING",
"SUSPENDED",
"TERMINATING",
"TERMINATED"
])
);
assert_eq!(
emitted["IMAGE_STATES"],
json!([
"CREATING",
"CREATED",
"CREATE_FAILED",
"UPDATING",
"UPDATED",
"UPDATE_FAILED",
"DELETING",
"DELETE_FAILED",
"DELETED"
])
);
assert_eq!(
emitted["IMAGE_VERSION_STATES"],
json!([
"PENDING",
"IN_PROGRESS",
"SUCCESSFUL",
"FAILED",
"DELETING",
"DELETED",
"DELETE_FAILED"
])
);
assert_eq!(
emitted["BUILD_STATES"],
json!(["PENDING", "IN_PROGRESS", "SUCCESSFUL", "FAILED"])
);
assert_eq!(emitted["CHIPSETS"], json!(["GRAVITON"]));
assert_eq!(emitted["MAX_RESOURCES"], 1);
assert_eq!(emitted["MAX_CLIENT_TOKEN_LEN"], 128);
assert_eq!(
emitted["TERMINAL_STATES"],
json!(["TERMINATED", "TERMINATING", "SUSPENDED", "SUSPENDING"])
);
assert_eq!(emitted["DEAD_STATES"], json!(["TERMINATED", "TERMINATING"]));
assert_eq!(
emitted["MODEL_IMAGE_READY_STATES"],
json!(["CREATED", "UPDATED"])
);
assert_eq!(
emitted["TOLERATED_IMAGE_READY_STATES"],
json!(["ACTIVE", "AVAILABLE"])
);
assert_eq!(
emitted["MICROVM_REGIONS"],
json!([
"us-east-1",
"us-east-2",
"us-west-2",
"eu-west-1",
"ap-northeast-1"
])
);
}
#[test]
fn the_emitted_object_round_trips_through_a_string() {
let emitted = as_json();
let text = serde_json::to_string(&emitted).expect("serializes");
let parsed: Value = serde_json::from_str(&text).expect("parses");
assert_eq!(parsed, emitted);
}
#[test]
fn every_dead_state_is_also_a_terminal_state() {
for state in DEAD_STATES {
assert!(TERMINAL_STATES.contains(&state), "{state} is not terminal");
}
assert!(
!DEAD_STATES.contains(&"SUSPENDED"),
"SUSPENDED is terminal but not dead: it is the state a resume is called from"
);
}
#[test]
fn the_image_level_connector_ceiling_is_one_and_not_the_vm_levels_ten() {
assert_eq!(MAX_IMAGE_EGRESS_CONNECTORS, 1);
assert_eq!(MAX_NETWORK_CONNECTORS, 10);
let emitted = as_json();
let image = emitted["MAX_IMAGE_EGRESS_CONNECTORS"]
.as_u64()
.expect("a number");
let vm = emitted["MAX_NETWORK_CONNECTORS"]
.as_u64()
.expect("a number");
assert!(
image < vm,
"collapsing the two would be permissive by 10x on the image-level list: \
{image} against {vm}"
);
}
#[test]
fn the_two_version_statuses_are_active_and_inactive() {
assert_eq!(IMAGE_VERSION_STATUSES, ["ACTIVE", "INACTIVE"]);
for status in IMAGE_VERSION_STATUSES {
assert_eq!(status, status.to_uppercase(), "{status}");
}
assert!(
!IMAGE_VERSION_STATUSES.contains(&"DISABLED"),
"DISABLED is HookState's spelling, not this enum's — mixing them is a \
ValidationException on the only member the update request has"
);
}
#[test]
fn the_model_and_tolerated_ready_states_do_not_overlap() {
for state in TOLERATED_IMAGE_READY_STATES {
assert!(
!MODEL_IMAGE_READY_STATES.contains(&state),
"{state} is claimed as both model-backed and tolerated"
);
}
}
#[test]
fn an_image_name_takes_letters_digits_hyphen_and_underscore_only() {
assert!(is_valid_image_name("agentd-conformance_01"));
assert!(is_valid_image_name("A"));
assert!(is_valid_image_name(&"a".repeat(MAX_IMAGE_NAME_LEN)));
assert!(!is_valid_image_name(""), "min is 1");
assert!(
!is_valid_image_name(&"a".repeat(MAX_IMAGE_NAME_LEN + 1)),
"65 characters is one past the ceiling"
);
for rejected in [
"my.image",
"team/image",
"my image",
"img!",
"imagé",
"img\n",
] {
assert!(!is_valid_image_name(rejected), "{rejected}");
}
}
#[test]
fn a_multibyte_name_is_refused_by_the_character_class_not_by_luck() {
assert!(!is_valid_image_name("é"));
assert!(!is_valid_image_name("日本語"));
}
#[test]
fn a_log_group_takes_the_models_character_set_and_bounds() {
assert!(is_valid_log_group("/aws/lambda-microvms/img"));
assert!(is_valid_log_group("builds_2026.08#a"));
assert!(is_valid_log_group(&"a".repeat(MAX_LOG_GROUP_LEN)));
assert!(!is_valid_log_group(""), "min is 1");
assert!(
!is_valid_log_group(&"a".repeat(MAX_LOG_GROUP_LEN + 1)),
"513 characters is one past the ceiling"
);
for rejected in ["arn:aws:logs", "a group", "grp*", "grp\n", "imagé"] {
assert!(!is_valid_log_group(rejected), "{rejected}");
}
}
#[test]
fn the_user_stream_ceiling_leaves_room_for_the_slash_and_sixteen_hex() {
assert_eq!(MAX_USER_LOG_STREAM_LEN + 1 + 16, MAX_LOG_STREAM_LEN);
}
#[test]
fn the_models_own_prose_claims_four_times_the_ceiling_its_shape_states() {
let emitted = as_json();
let real = emitted["MAX_RUN_HOOK_PAYLOAD_BYTES"]
.as_u64()
.expect("a number");
let documented = emitted["DOCUMENTED_RUN_HOOK_PAYLOAD_BYTES"]
.as_u64()
.expect("a number");
assert_eq!(real, 4096, "the shape, which is what the service enforces");
assert_eq!(
documented, 16_384,
"the model's documentation string on RunMicrovmRequest.runHookPayload"
);
assert!(
documented > real,
"the model's prose is permissive against its own shape: {documented} against {real}"
);
assert_eq!(
documented / real,
4,
"wrong by 4x, in the direction that tells a caller four times as much secret \
material fits as actually does"
);
}
#[test]
fn a_legal_image_arn_can_be_longer_than_a_legal_identifier() {
let emitted = as_json();
let identifier = emitted["MAX_IDENTIFIER_LEN"].as_u64().expect("a number");
let arn = emitted["MAX_IMAGE_ARN_LEN"].as_u64().expect("a number");
assert!(
arn > identifier,
"the model permits a {arn}-character image ARN and a {identifier}-character \
identifier, so a legal response value can be an illegal request value"
);
let longest_region = MICROVM_REGIONS
.iter()
.map(|region| region.as_str().len())
.max()
.expect("five regions");
let worst = format!(
"arn:aws:lambda:{}:{}:microvm-image:{}",
"x".repeat(longest_region),
"9".repeat(12),
"a".repeat(MAX_IMAGE_NAME_LEN),
);
assert!(
worst.len() <= MAX_IDENTIFIER_LEN,
"the worst legal image ARN is {} characters, which must fit the {MAX_IDENTIFIER_LEN} \
identifier bound or the contradiction would be reachable rather than theoretical",
worst.len(),
);
}
#[test]
fn every_state_set_the_polling_loops_use_is_a_subset_of_the_models_enum() {
for state in TERMINAL_STATES {
assert!(
MICROVM_STATES.contains(&state),
"TERMINAL_STATES has {state}, which is not a MicrovmState — \
ControlPlane::wait_for_running fails fast on it and would now never fire"
);
}
for state in DEAD_STATES {
assert!(
MICROVM_STATES.contains(&state),
"DEAD_STATES has {state}, which is not a MicrovmState — the resume path passes \
this set as its fail-fast list"
);
}
for state in MODEL_IMAGE_READY_STATES {
assert!(
IMAGE_STATES.contains(&state),
"MODEL_IMAGE_READY_STATES has {state}, which is not a MicrovmImageState — \
Image::is_ready would never answer true for it and a built image would poll to \
the 45-minute deadline"
);
}
for state in TOLERATED_IMAGE_READY_STATES {
assert!(
!IMAGE_STATES.contains(&state),
"{state} is in the model's enum now, so it is no longer a tolerated legacy \
spelling — promote it to MODEL_IMAGE_READY_STATES"
);
}
assert!(
BUILD_STATES.contains(&"PENDING"),
"the stall probe's replay signature is 'every build still PENDING'"
);
let failed: Vec<&str> = IMAGE_STATES
.iter()
.copied()
.filter(|state| state.contains("FAILED"))
.collect();
assert_eq!(
failed,
["CREATE_FAILED", "UPDATE_FAILED", "DELETE_FAILED"],
"the three spellings the substring test covers"
);
}
#[test]
fn a_tag_component_takes_any_script_and_eight_punctuation_marks() {
for accepted in [
"owner",
"cost-centre",
"cost centre",
"team/agents",
"a.b:c=d+e-f@g_h",
"日本語",
"café",
"123",
"",
] {
assert!(is_valid_tag_component(accepted), "{accepted:?}");
}
for rejected in [
"a,b",
"a#b",
"50%",
"line\nbreak",
"tab\there",
"a!b",
"a(b",
] {
assert!(!is_valid_tag_component(rejected), "{rejected:?}");
}
}
#[test]
fn a_role_arn_needs_twelve_account_digits_and_an_iam_role_path() {
for accepted in [
"arn:aws:iam::123456789012:role/build",
"arn:aws:iam::123456789012:role/service-role/path/to/role",
"arn:aws-us-gov:iam::123456789012:role/build",
"arn:aws-cn:iam::123456789012:role/build",
"arn:aws:iam::123456789012:role/name+with=every,allowed.char@here_x-y",
"arn:aws:iam::123456789012:roleadmin",
] {
assert!(is_valid_role_arn(accepted), "{accepted}");
}
for rejected in [
"bonk-sandbox-microvm-build",
"arn:aws:lambda:us-east-1:123456789012:function:handler",
"arn:aws:iam::12345678901:role/build",
"arn:aws:iam::1234567890123:role/build",
"arn:aws:iam::abcdefghijkl:role/build",
"arn:aws:iam:us-east-1:123456789012:role/build",
"arn:aws:iam::123456789012:role",
"arn:aws:iam::123456789012:role/",
"arn:aws:iam::123456789012:user/laith",
"arn:AWS:iam::123456789012:role/build",
"",
"arn:",
] {
assert!(!is_valid_role_arn(rejected), "{rejected}");
}
}
#[test]
fn the_conformance_accounts_own_role_arns_are_accepted() {
for real in [
"arn:aws:iam::392583147479:role/bonk-sandbox-microvm-build",
"arn:aws:iam::392583147479:role/bonk-sandbox-microvm-execution",
] {
assert!(is_valid_role_arn(real), "{real}");
assert!(real.len() >= MIN_ROLE_ARN_LEN, "{real}");
assert!(real.len() <= MAX_ROLE_ARN_LEN, "{real}");
}
}
#[test]
fn the_port_floor_is_one_and_the_ceiling_is_what_a_u16_holds() {
assert_eq!(MIN_PORT, 1, "0 is 'let the kernel choose', not a port");
assert_eq!(MAX_PORT, u16::MAX);
assert_eq!(
u32::from(MAX_PORT),
MAX_HOOK_PORT,
"PortNumber and HooksPortInteger agree today; they are two shapes and this is where \
it is noticed if they stop"
);
}
#[test]
fn the_version_and_non_blank_shapes_agree_today_and_are_still_two_constants() {
assert_eq!(MAX_VERSION_LEN, MAX_NON_BLANK_LEN);
assert_eq!(VERSION_PATTERN, NON_BLANK_PATTERN);
let emitted = as_json();
assert_eq!(emitted["MAX_VERSION_LEN"], emitted["MAX_NON_BLANK_LEN"]);
assert_eq!(emitted["VERSION_PATTERN"], emitted["NON_BLANK_PATTERN"]);
}
}