swactor/tests/mvp_system/resource_inventory_guarantees.rs
Zachery Aaron Shores-Chmielewski b5de4c9bd6 docs(mvp): lay out mvp-system spec and contracts
Add MVP_SYSTEM_SPEC plus per-component contract docs (arena manager, device bridge,
gpu worker ctl/ingress/egress/process-adapter, orchestrator run-fsm/token-endpoint,
run plan, stage controller, tx_rx edge, weights). Scaffold the guarantee tests against
the (empty) mvp-system crate.


Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
2026-06-23 11:12:45 +04:00

246 lines
9.4 KiB
Rust

//! Black-box contract tests for MVP resource inventory.
//!
//! These tests intentionally know only the public inventory/planner input
//! surface:
//!
//! - orchestrator-owned inventory entries
//! - candidate pool and placement input derived from that inventory
//! - planner acceptance or typed rejection
//!
//! They assert the guarantees in
//! `specs/mvp_system/resource_inventory_contract.md`.
use mvp_system::resource_inventory as inventory;
// The inventory fixture has more nodes than the placement needs. That proves
// the planner may choose among known inventory entries but may not invent hidden
// nodes or accept nodes that the orchestrator did not provide.
fn inventory_entries() -> Vec<inventory::InventoryEntry> {
vec![
inventory::InventoryEntry::ready(inventory::NodeId(10), inventory::GpuClass::TestSmall),
inventory::InventoryEntry::ready(inventory::NodeId(11), inventory::GpuClass::TestSmall),
inventory::InventoryEntry::ready(inventory::NodeId(12), inventory::GpuClass::TestSmall),
inventory::InventoryEntry::ready(inventory::NodeId(13), inventory::GpuClass::TestSmall),
]
}
// The fixed placement input names stage ownership without giving the planner
// permission to derive any other topology. The plan still owns the resulting
// stage records.
fn fixed_placement() -> inventory::PlacementInput {
inventory::PlacementInput::FixedLinear(vec![
inventory::StagePlacement {
stage_index: 0,
node_id: inventory::NodeId(10),
},
inventory::StagePlacement {
stage_index: 1,
node_id: inventory::NodeId(11),
},
inventory::StagePlacement {
stage_index: 2,
node_id: inventory::NodeId(12),
},
])
}
// This helper builds the public planning request from inventory facts. Tests
// mutate only the fact under examination so a rejection can be attributed to a
// specific inventory contract violation.
fn planning_request() -> inventory::PlanningRequest {
inventory::PlanningRequest {
run_id: inventory::RunId(7),
stage_count: 3,
entries: inventory_entries(),
placement: fixed_placement(),
}
}
// Node reports after boot are inventory inputs, not placement negotiations.
// This helper gives tests one post-boot report they can send and then prove did
// not rewrite the committed planning input.
fn post_boot_health_report(node_id: inventory::NodeId) -> inventory::NodeReport {
inventory::NodeReport::BootHealth {
node_id,
health: inventory::BootHealth::Ready,
}
}
// This proves inventory formation is orchestrator-owned and available before
// planning: every entry is tied to a known node id, and node reports do not
// negotiate placement after boot.
#[test]
fn inventory_entries_are_known_before_planning_and_not_negotiated_by_nodes() {
// Create the orchestrator-owned inventory and snapshot the public entries.
let mut harness = inventory::InventoryHarness::new(inventory_entries());
let before_report = harness.entries().to_vec();
// Let a node report boot health after the inventory already exists.
harness.observe_node_report(post_boot_health_report(inventory::NodeId(10)));
// Boot health may update readiness metadata, but it must not rewrite the
// node set or add placement facts.
let after_report = harness.entries().to_vec();
let before_nodes = before_report
.iter()
.map(|entry| entry.node_id)
.collect::<std::collections::BTreeSet<_>>();
let after_nodes = after_report
.iter()
.map(|entry| entry.node_id)
.collect::<std::collections::BTreeSet<_>>();
assert_eq!(after_nodes, before_nodes);
assert!(!harness.commands().iter().any(|command| {
matches!(command, inventory::InventoryCommand::AcceptPlacementNegotiation { .. })
}));
}
// This proves the planner may place stages only onto nodes present in the
// orchestrator-owned inventory, and the emitted RunPlan is the sole placement
// result.
#[test]
fn planned_stage_nodes_are_subset_of_inventory_nodes() {
// Build a valid public planning request from inventory entries.
let request = planning_request();
let inventory_nodes = request
.entries
.iter()
.map(|entry| entry.node_id)
.collect::<std::collections::BTreeSet<_>>();
// Plan through the public inventory/planner boundary.
let plan = inventory::plan_from_inventory(request).expect("valid inventory must plan");
// Prove every planned stage node came from the inventory snapshot.
for stage in &plan.stages {
assert!(
inventory_nodes.contains(&stage.node_id),
"stage used node outside inventory: {:?}",
stage.node_id
);
}
}
// This proves unknown nodes, duplicate stage assignments, and missing stage
// assignments reject as typed planning-input errors.
#[test]
fn invalid_inventory_placement_rejects_with_typed_errors() {
// Each case corrupts one public placement fact.
let cases = vec![
(
inventory::PlacementInput::FixedLinear(vec![
inventory::StagePlacement {
stage_index: 0,
node_id: inventory::NodeId(10),
},
inventory::StagePlacement {
stage_index: 1,
node_id: inventory::NodeId(99),
},
inventory::StagePlacement {
stage_index: 2,
node_id: inventory::NodeId(12),
},
]),
inventory::InventoryRejectionKind::UnknownNode {
node_id: inventory::NodeId(99),
},
),
(
inventory::PlacementInput::FixedLinear(vec![
inventory::StagePlacement {
stage_index: 0,
node_id: inventory::NodeId(10),
},
inventory::StagePlacement {
stage_index: 0,
node_id: inventory::NodeId(11),
},
inventory::StagePlacement {
stage_index: 2,
node_id: inventory::NodeId(12),
},
]),
inventory::InventoryRejectionKind::DuplicateStage { stage_index: 0 },
),
(
inventory::PlacementInput::FixedLinear(vec![
inventory::StagePlacement {
stage_index: 0,
node_id: inventory::NodeId(10),
},
inventory::StagePlacement {
stage_index: 2,
node_id: inventory::NodeId(12),
},
]),
inventory::InventoryRejectionKind::MissingStage { stage_index: 1 },
),
];
for (placement, expected_kind) in cases {
// Replace only the placement fact in an otherwise valid request.
let mut request = planning_request();
request.placement = placement;
// The rejection must be typed and no RunPlan may be emitted.
let rejection = inventory::plan_from_inventory(request)
.expect_err("invalid inventory placement must reject");
assert_eq!(rejection.kind, expected_kind);
}
}
// This proves planning does not mutate the candidate pool and does not derive
// hidden nodes outside the inventory.
#[test]
fn planning_preserves_candidate_pool_and_emits_no_hidden_nodes() {
// Snapshot the public inventory before planning.
let request = planning_request();
let original_entries = request.entries.clone();
let original_nodes = original_entries
.iter()
.map(|entry| entry.node_id)
.collect::<std::collections::BTreeSet<_>>();
// Plan using a cloned request so the caller-owned facts remain inspectable.
let plan = inventory::plan_from_inventory(request.clone()).expect("valid inventory must plan");
// The caller's candidate facts must be unchanged.
assert_eq!(request.entries, original_entries);
// Every node mentioned by the plan must be explainable by the original
// inventory snapshot.
let planned_nodes = plan
.stages
.iter()
.map(|stage| stage.node_id)
.collect::<std::collections::BTreeSet<_>>();
assert!(planned_nodes.is_subset(&original_nodes));
}
// This proves inventory authority ends at planner input. After provisioning,
// stages consume their RunPlan-derived assignment and do not reinterpret
// inventory reports.
#[test]
fn provisioned_stages_do_not_reinterpret_inventory() {
// Commit a plan and provision its stages.
let plan = inventory::plan_from_inventory(planning_request()).expect("valid inventory must plan");
let mut harness = inventory::InventoryHarness::new(inventory_entries());
harness.commit_plan(plan.clone());
harness.provision_stages();
// Send a later inventory report that would be dangerous if treated as a
// graph rewrite.
harness.observe_node_report(inventory::NodeReport::CapacityChanged {
node_id: inventory::NodeId(11),
gpu_class: inventory::GpuClass::TestLarge,
});
// No provisioned stage may be asked to reinterpret its assignment.
assert!(!harness.commands().iter().any(|command| {
matches!(command, inventory::InventoryCommand::RewriteStagePlacement { .. })
}));
// The committed plan remains the only graph-visible placement fact.
assert_eq!(harness.committed_plan(), Some(&plan));
}