Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 173 additions & 6 deletions crates/nms-cli/tests/find_integration.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
use assert_cmd::cargo::cargo_bin_cmd;
use nms_core::address::GalacticAddress;
use nms_core::biome::Biome;
use nms_graph::GalaxyModel;
use nms_query::display::format_find_results;
use nms_core::player::PlayerState;
use nms_core::system::System;
use nms_graph::{GalaxyModel, SystemId};
use nms_query::display::{format_find_results, format_show_system};
use nms_query::find::{FindQuery, ReferencePoint, execute_find};
use nms_query::show::{ShowQuery, ShowResult, execute_show};
use nms_query::theme::Theme;
use predicates::prelude::*;
use std::collections::HashSet;

fn test_save_json() -> &'static str {
r#"{
Expand Down Expand Up @@ -37,7 +44,32 @@ fn find_pipeline_end_to_end() {
let results = execute_find(&model, &query).unwrap();
let output = format_find_results(&results, &Theme::none());

assert!(!results.is_empty());
assert_eq!(results.len(), 2);
let expected = HashSet::from([
(
SystemId::from_address(&GalacticAddress::from_packed(0x001000000064, 0)),
1,
),
(
SystemId::from_address(&GalacticAddress::from_packed(0x002000000C80, 0)),
1,
),
]);
let actual: HashSet<_> = results
.iter()
.map(|result| {
let system_id = SystemId::from_address(&result.system.address);
let associated_planet = result
.system
.planets
.iter()
.find(|planet| planet.index == result.planet.index)
.unwrap();
assert_eq!(associated_planet, &result.planet);
(system_id, result.planet.index)
})
.collect();
assert_eq!(actual, expected);
assert!(output.contains("Portal Glyphs"));
}

Expand All @@ -51,9 +83,13 @@ fn find_with_biome_filter() {
..Default::default()
};
let results = execute_find(&model, &query).unwrap();
for r in &results {
assert_eq!(r.planet.biome, Some(Biome::Lush));
}
assert_eq!(results.len(), 1);
assert_eq!(results[0].planet.biome, Some(Biome::Lush));
assert_eq!(
SystemId::from_address(&results[0].system.address),
SystemId::from_address(&GalacticAddress::from_packed(0x001000000064, 0))
);
assert_eq!(results[0].planet.index, 1);
}

#[test]
Expand Down Expand Up @@ -82,3 +118,134 @@ fn find_nearest_respects_limit() {
let results = execute_find(&model, &query).unwrap();
assert!(results.len() <= 1);
}

#[test]
fn find_infested_flag_filters_the_real_cli_process_result() {
let source = test_save_json();
let original = r#""VP": ["0xCD", 1]"#;
let modified = r#""VP": ["0xCD", 65536]"#;
assert!(source.contains(original));
let json = source.replace(original, modified);
let fixture = tempfile::NamedTempFile::new().unwrap();
std::fs::write(fixture.path(), json).unwrap();

cargo_bin_cmd!("nms")
.args([
"find",
"--save",
fixture.path().to_str().unwrap(),
"--infested",
])
.assert()
.success()
.stdout(predicates::str::contains("002000000C80"))
.stdout(predicates::str::contains("001000000064").not());
}

#[test]
fn find_formatter_keeps_named_planets_paired_with_their_systems() {
let origin = GalacticAddress::new(0, 0, 0, 1, 0, 0);
let cedar_address = GalacticAddress::new(0, 0, 0, 1, 0, 0);
let maple_address = GalacticAddress::new(1, 0, 0, 1, 0, 0);
let mut model = GalaxyModel::new();
model.player_state = Some(PlayerState::new(origin, 0, None, None, 0, 0, 0));
model.insert_system(System::new(
cedar_address,
Some("Cedar".into()),
Some("Explorer".into()),
None,
vec![nms_core::system::Planet::new(
1,
Some(Biome::Lush),
None,
false,
Some("Bud-A".into()),
None,
)],
));
model.insert_system(System::new(
maple_address,
Some("Maple".into()),
Some("Explorer".into()),
None,
vec![nms_core::system::Planet::new(
1,
Some(Biome::Lush),
None,
false,
Some("Bud-B".into()),
None,
)],
));

let results = execute_find(&model, &FindQuery::default()).unwrap();
assert_eq!(results.len(), 2);
let actual: Vec<_> = results
.iter()
.map(|result| {
(
SystemId::from_address(&result.system.address),
result.planet.name.as_deref().unwrap(),
result.system.name.as_deref().unwrap(),
result.distance_ly,
)
})
.collect();
assert_eq!(
actual,
vec![
(
SystemId::from_address(&cedar_address),
"Bud-A",
"Cedar",
0.0,
),
(
SystemId::from_address(&maple_address),
"Bud-B",
"Maple",
400.0,
),
]
);

let output = format_find_results(&results, &Theme::none());
let bud_a_lines: Vec<_> = output
.lines()
.filter(|line| line.contains("Bud-A"))
.collect();
let bud_b_lines: Vec<_> = output
.lines()
.filter(|line| line.contains("Bud-B"))
.collect();
assert_eq!(bud_a_lines.len(), 1);
assert!(bud_a_lines[0].contains("Cedar"));
assert!(!bud_a_lines[0].contains("Maple"));
assert_eq!(bud_b_lines.len(), 1);
assert!(bud_b_lines[0].contains("Maple"));
assert!(!bud_b_lines[0].contains("Cedar"));
}

#[test]
fn show_formatter_omits_distance_when_player_and_system_galaxies_differ() {
let player = GalacticAddress::new(0, 0, 0, 1, 0, 0);
let remote = GalacticAddress::new(0, 0, 0, 1, 0, 9);
let mut model = GalaxyModel::new();
model.player_state = Some(PlayerState::new(player, 0, None, None, 0, 0, 0));
model.set_active_galaxy(9);
model.insert_system(System::new(
remote,
Some("Remote".into()),
None,
None,
vec![],
));

let ShowResult::System(result) =
execute_show(&model, &ShowQuery::System("Remote".into())).unwrap()
else {
panic!("expected system result");
};
assert_eq!(result.distance_from_player, None);
assert!(!format_show_system(&result, &Theme::none()).contains("Distance"));
}
Binary file not shown.
171 changes: 171 additions & 0 deletions crates/nms-copilot/tests/query_cache_integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use nms_core::address::GalacticAddress;
use nms_core::biome::Biome;
use nms_core::player::PlayerState;
use nms_core::system::{Planet, System};
use nms_graph::{GalaxyModel, GraphError, SystemId};
use nms_query::find::{FindQuery, ReferencePoint, execute_find};
use nms_query::show::{ShowQuery, ShowResult, execute_show};

const PREDECESSOR_ARCHIVE: &[u8] = include_bytes!("fixtures/predecessor-schema2-semantic2.rkyv");

fn system(galaxy: u8, name: &str, planet_name: &str, infested: bool) -> System {
System::new(
GalacticAddress::new(0, 0, 0, 1, 0, galaxy),
Some(name.into()),
Some("Explorer".into()),
None,
vec![Planet::new(
1,
Some(Biome::Lush),
None,
infested,
Some(planet_name.into()),
None,
)],
)
}

fn fresh_model() -> GalaxyModel {
let mut model = GalaxyModel::new();
let player = GalacticAddress::new(0, 0, 0, 1, 0, 0);
model.player_state = Some(PlayerState::new(player, 0, None, None, 7, 8, 9));
model.insert_system(system(0, "Shared", "Zero", false));
model.insert_system(system(9, "Shared", "Nine", true));
model.set_active_galaxy(9);
model
}

fn query_projection(
model: &GalaxyModel,
galaxy: u8,
) -> Vec<(SystemId, Planet, System, f64, String)> {
let address = GalacticAddress::new(0, 0, 0, 1, 0, galaxy);
execute_find(
model,
&FindQuery {
from: ReferencePoint::Address(address),
biome: Some(Biome::Lush),
..Default::default()
},
)
.unwrap()
.into_iter()
.map(|result| {
(
SystemId::from_address(&result.system.address),
result.planet,
result.system,
result.distance_ly,
result.portal_hex,
)
})
.collect()
}

#[test]
fn schema2_semantic2_archive_rebuilds_corrected_indexes_and_matches_fresh_queries() {
assert_eq!(PREDECESSOR_ARCHIVE.len(), 360);
assert_eq!(
&PREDECESSOR_ARCHIVE[..16],
&[
0x4e, 0x4d, 0x53, 0x43, 0x41, 0x43, 0x48, 0x45, 2, 0, 0, 0, 2, 0, 0, 0
]
);

let prior_data = nms_cache::serialize::deserialize(PREDECESSOR_ARCHIVE).unwrap();
assert_eq!(prior_data.systems.len(), 2);
assert_eq!(
prior_data
.systems
.iter()
.map(|system| system.planets.len())
.sum::<usize>(),
2
);
assert_eq!(prior_data.bases.len(), 0);
assert_eq!(prior_data.save_version, 4720);
assert_eq!(prior_data.active_galaxy, 9);
let prior_player = prior_data.player_state.as_ref().unwrap();
assert_eq!(prior_player.current_address.reality_index, 0);
assert_eq!(prior_player.current_reality, 0);
assert_eq!(
(
prior_player.units,
prior_player.nanites,
prior_player.quicksilver
),
(7, 8, 9)
);

let prior = nms_cache::rebuild_model(&prior_data);
let fresh = fresh_model();
assert_eq!(prior.system_count(), 2);
assert_eq!(prior.planet_count(), 2);
assert_eq!(prior.active_galaxy, 9);
assert_eq!(prior.player_position(), fresh.player_position());
assert_eq!(prior.player_state.as_ref().unwrap().units, 7);
assert_eq!(prior.player_state.as_ref().unwrap().nanites, 8);
assert_eq!(prior.player_state.as_ref().unwrap().quicksilver, 9);

let id0 = SystemId::from_address(&GalacticAddress::new(0, 0, 0, 1, 0, 0));
let id9 = SystemId::from_address(&GalacticAddress::new(0, 0, 0, 1, 0, 9));
for (model, expected_id, expected_planet) in [(&prior, id0, "Zero"), (&prior, id9, "Nine")] {
let (id, system) = model
.system_by_name_in_galaxy("sHaReD", expected_id.reality_index())
.unwrap();
assert_eq!(*id, expected_id);
assert_eq!(system.planets.len(), 1);
assert_eq!(system.planets[0].index, 1);
assert_eq!(system.planets[0].biome, Some(Biome::Lush));
assert_eq!(system.planets[0].name.as_deref(), Some(expected_planet));
assert_eq!(system.planets[0].infested, expected_planet == "Nine");
assert_eq!(system.discoverer.as_deref(), Some("Explorer"));
}

for galaxy in [0, 9] {
let prior_projection = query_projection(&prior, galaxy);
let fresh_projection = query_projection(&fresh, galaxy);
assert_eq!(prior_projection, fresh_projection);
assert_eq!(prior_projection.len(), 1);
assert_eq!(prior_projection[0].0.reality_index(), galaxy);
assert_eq!(
prior_projection[0].1.name.as_deref(),
Some(if galaxy == 0 { "Zero" } else { "Nine" })
);
assert_eq!(prior_projection[0].3, 0.0);
}

assert!(matches!(
execute_find(&prior, &FindQuery::default()),
Err(GraphError::ReferenceGalaxyMismatch {
reference: 0,
active: 9
})
));
let ShowResult::System(prior_show) =
execute_show(&prior, &ShowQuery::System("Shared".into())).unwrap()
else {
panic!("expected system result");
};
assert_eq!(prior_show.system.name.as_deref(), Some("Shared"));
assert_eq!(prior_show.system.planets[0].name.as_deref(), Some("Nine"));
assert_eq!(prior_show.distance_from_player, None);

let current_bytes =
nms_cache::serialize::serialize(&nms_cache::extract_cache_data(&fresh, 4720)).unwrap();
assert_eq!(&current_bytes[..16], &PREDECESSOR_ARCHIVE[..16]);
let current_data = nms_cache::serialize::deserialize(&current_bytes).unwrap();
let current = nms_cache::rebuild_model(&current_data);
assert_eq!(current.system_count(), 2);
assert_eq!(current.planet_count(), 2);
assert_eq!(current.active_galaxy, 9);
assert_eq!(query_projection(&prior, 0), query_projection(&current, 0));
assert_eq!(query_projection(&prior, 9), query_projection(&current, 9));
let ShowResult::System(current_show) =
execute_show(&current, &ShowQuery::System("Shared".into())).unwrap()
else {
panic!("expected system result");
};
assert_eq!(current_show.system, prior_show.system);
assert_eq!(current_show.distance_from_player, None);
}
14 changes: 14 additions & 0 deletions crates/nms-graph/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,18 @@ pub enum GraphError {

#[error("no player position available")]
NoPlayerPosition,

/// The player's current-position galaxy differs from the active model galaxy.
///
/// Explicit address and base references use their own galaxy and do not
/// produce this error.
#[error(
"reference galaxy {reference} differs from active galaxy {active}; use an explicit address or base"
)]
ReferenceGalaxyMismatch {
/// Reality index encoded by the player's current-position address.
reference: u8,
/// Reality index selected by the model for unqualified queries.
active: u8,
},
}
Loading
Loading