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
10 changes: 3 additions & 7 deletions validation/core/src/models/class_diagram_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub struct InternalApiIndex {

impl InternalApiIndex {
/// Build an [`InternalApiIndex`] from internal-API diagram inputs.
pub fn build_index(diagrams: &[ClassDiagramInput], _result: &mut ValidationResult) -> Self {
pub fn build_index(diagrams: &[ClassDiagramInput]) -> Self {
let mut interfaces = Vec::new();

for diagram in diagrams {
Expand Down Expand Up @@ -211,10 +211,8 @@ mod tests {
],
}];

let mut result = ValidationResult::default();
let index = InternalApiIndex::build_index(&diagrams, &mut result);
let index = InternalApiIndex::build_index(&diagrams);

assert!(result.is_empty());
assert!(index
.interfaces()
.find(|interface| interface.id == "InternalAPI.InternalInterface")
Expand Down Expand Up @@ -260,10 +258,8 @@ mod tests {
],
}];

let mut result = ValidationResult::default();
let index = InternalApiIndex::build_index(&diagrams, &mut result);
let index = InternalApiIndex::build_index(&diagrams);

assert!(result.is_empty());
let interface_ids: BTreeSet<&str> = index
.interfaces()
.map(|interface| interface.id.as_str())
Expand Down
2 changes: 1 addition & 1 deletion validation/core/src/profiles/architectural_design.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub fn run(inputs: &ArchitecturalDesignInputs) -> Result<ProfileRun, String> {
let internal_api = read_and_convert::<ClassDiagramReader, InternalApiIndex>(
inputs.internal_api_diagrams.as_slice(),
&mut result,
|raw: ClassDiagramInputs, errs| InternalApiIndex::build_index(&raw, errs),
|raw: ClassDiagramInputs, _result| InternalApiIndex::build_index(&raw),
)?;

let validators = registered_validators(&component, &sequence, &internal_api);
Expand Down
37 changes: 36 additions & 1 deletion validation/core/src/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub fn finish_profile_validation(
profile: Profile,
profile_run: &ProfileRun,
) -> Result<(), String> {
if !profile_run.ran_validator {
if !profile_run.ran_validator && profile_run.result.is_empty() {
log::info!(
"Skipping validation profile {}: no selected validators have their required inputs.",
profile.as_str()
Expand Down Expand Up @@ -107,3 +107,38 @@ fn format_error_details(profile_run: &ProfileRun, prefix: &str) -> String {
.collect::<Vec<_>>()
.join("\n\n")
}

#[cfg(test)]
mod tests {
use super::*;
use validation::ValidationResult;

#[test]
fn skips_when_no_validator_ran_and_result_is_empty() {
let profile_run = ProfileRun {
ran_validator: false,
result: ValidationResult::default(),
};

let result =
finish_profile_validation(None, false, Profile::ArchitecturalDesign, &profile_run);

assert!(result.is_ok());
}

#[test]
fn reports_failure_when_no_validator_ran_but_read_model_failed() {
let mut validation_result = ValidationResult::default();
validation_result.add_failure("read model failed".to_string());
let profile_run = ProfileRun {
ran_validator: false,
result: validation_result,
};

let result =
finish_profile_validation(None, false, Profile::ArchitecturalDesign, &profile_run);

assert!(result.is_err());
assert!(result.unwrap_err().contains("read model failed"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl ComponentInternalApiValidator {
internal_api_diagram: &InternalApiIndex,
) -> Self {
Self {
component_interface_ids: collect_component_interface_ids(component_diagram),
component_interface_ids: collect_component_internal_interface_ids(component_diagram),
internal_api_interface_ids: collect_internal_api_interface_ids(internal_api_diagram),
result: ValidationResult::default(),
}
Expand Down Expand Up @@ -88,13 +88,13 @@ fn append_debug_log(
}
}

fn collect_component_interface_ids(
fn collect_component_internal_interface_ids(
component_diagram: &ComponentDiagramArchitecture,
) -> BTreeSet<String> {
component_diagram
.entities
.iter()
.filter(|entity| entity.is_interface())
.filter(|entity| entity.is_interface() && entity.parent_id.is_some())
.map(|entity| entity.id.clone())
.collect()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,116 +21,147 @@ fn validate(
) -> ValidationResult {
let mut setup_result = ValidationResult::default();
let component_arch = component_diagrams.to_diagram_architecture(&mut setup_result);

assert!(
setup_result.is_empty(),
"test fixture setup failed: {:?}",
setup_result.failures
);
validate_component_internal_api(&component_arch, internal_api)
}

#[test]
fn reports_missing_component_interface_declared_by_internal_api() {
let component_diagrams = component_diagrams_with_entities(vec![
unit("u1", &["InternalInterface"]),
unit("u2", &["InternalInterface"]),
interface("InternalInterface"),
let component_diagrams = component_diagram(vec![
unit("u1", &["InternalInterface"], &[]),
unit("u2", &["InternalInterface"], &[]),
interface_with_parent_id("InternalInterface", "component_example"),
]);
let internal_api = internal_api_index(vec![("OtherInterface", vec!["GetData"])]);

let validation_result = validate(component_diagrams, &internal_api);

assert_eq!(validation_result.failures.len(), 1);
assert!(validation_result.failures[0].contains("Missing internal API interface"));
assert!(validation_result.failures[0].contains("Missing interfaces : \"InternalInterface\""));
assert!(validation_result.failures[0]
.contains("Missing interfaces : \"component_example.InternalInterface\""));
assert!(!validation_result.failures[0].contains("Unit :"));
}

#[test]
fn reports_each_missing_component_interface_once() {
let component_diagrams = component_diagrams_with_entities(vec![
unit("u1", &["InternalInterface", "InternalInterface1"]),
unit("u2", &["InternalInterface"]),
interface("InternalInterface"),
interface("InternalInterface1"),
let component_diagrams = component_diagram(vec![
unit("u1", &["InternalInterface", "InternalInterface1"], &[]),
unit("u2", &["InternalInterface"], &[]),
interface_with_parent_id("InternalInterface", "component_example"),
interface_with_parent_id("InternalInterface1", "component_example"),
]);
let internal_api = internal_api_index(vec![("InternalInterface", vec!["GetData"])]);
let internal_api = internal_api_index(vec![(
"component_example.InternalInterface",
vec!["GetData"],
)]);

let validation_result = validate(component_diagrams, &internal_api);

assert_eq!(validation_result.failures.len(), 1);
assert!(validation_result.failures[0].contains("Missing internal API interface"));
assert!(validation_result.failures[0].contains("Missing interfaces : \"InternalInterface1\""));
assert!(validation_result.failures[0]
.contains("Missing interfaces : \"component_example.InternalInterface1\""));
}

#[test]
fn reports_missing_component_interface_even_without_unit_relation() {
let component_diagrams =
component_diagrams_with_entities(vec![unit("u1", &[]), interface("UnusedInterface")]);
let component_diagrams = component_diagram(vec![
unit_without_interfaces("u1"),
interface_with_parent_id("UnusedInterface", "component_example"),
]);
let internal_api = internal_api_index(vec![]);

let validation_result = validate(component_diagrams, &internal_api);

assert_eq!(validation_result.failures.len(), 1);
assert!(validation_result.failures[0].contains("Missing internal API interface"));
assert!(validation_result.failures[0].contains("Missing interfaces : \"UnusedInterface\""));
assert!(validation_result.failures[0]
.contains("Missing interfaces : \"component_example.UnusedInterface\""));
}

#[test]
fn reports_all_missing_component_interfaces_in_one_message() {
let component_diagrams = component_diagrams_with_entities(vec![
unit("u1", &["InternalInterface", "InternalInterface1"]),
unit("u2", &["InternalInterface"]),
unit("u3", &["InternalInterface"]),
interface("InternalInterface"),
interface("InternalInterface1"),
let component_diagrams = component_diagram(vec![
unit("u1", &["InternalInterface", "InternalInterface1"], &[]),
unit("u2", &["InternalInterface"], &[]),
unit("u3", &["InternalInterface"], &[]),
interface_with_parent_id("InternalInterface", "component_example"),
interface_with_parent_id("InternalInterface1", "component_example"),
]);
let internal_api = internal_api_index(vec![("InternalInterface", vec!["GetData"])]);
let internal_api = internal_api_index(vec![(
"component_example.InternalInterface",
vec!["GetData"],
)]);

let validation_result = validate(component_diagrams, &internal_api);

assert_eq!(validation_result.failures.len(), 1);
assert!(validation_result.failures[0].contains("Missing internal API interface"));
assert!(validation_result.failures[0].contains("Missing interfaces : \"InternalInterface1\""));
assert!(validation_result.failures[0]
.contains("Missing interfaces : \"component_example.InternalInterface1\""));
}

#[test]
fn reports_missing_component_interface_without_sequence_method_call() {
let component_diagrams = component_diagrams_with_entities(vec![
unit("u1", &["InternalInterface"]),
interface("InternalInterface"),
let component_diagrams = component_diagram(vec![
unit("u1", &["InternalInterface"], &[]),
interface_with_parent_id("InternalInterface", "component_example"),
]);
let internal_api = internal_api_index(vec![("OtherInterface", vec!["GetData"])]);

let validation_result = validate(component_diagrams, &internal_api);

assert_eq!(validation_result.failures.len(), 1);
assert!(validation_result.failures[0].contains("Missing internal API interface"));
assert!(validation_result.failures[0].contains("Missing interfaces : \"InternalInterface\""));
assert!(validation_result.failures[0]
.contains("Missing interfaces : \"component_example.InternalInterface\""));
}

#[test]
fn reports_case_mismatch_between_component_and_internal_api_interface_names() {
let component_diagrams = component_diagrams_with_entities(vec![
unit("u1", &["InternalInterface"]),
unit("u2", &["InternalInterface"]),
interface("InternalInterface"),
let component_diagrams = component_diagram(vec![
unit("u1", &["InternalInterface"], &[]),
unit("u2", &["InternalInterface"], &[]),
interface_with_parent_id("InternalInterface", "component_example"),
]);
let internal_api = internal_api_index(vec![("internalinterface", vec!["GetData"])]);

let validation_result = validate(component_diagrams, &internal_api);

assert_eq!(validation_result.failures.len(), 1);
assert!(validation_result.failures[0].contains("Missing internal API interface"));
assert!(validation_result.failures[0].contains("Missing interfaces : \"InternalInterface\""));
assert!(validation_result.failures[0]
.contains("Missing interfaces : \"component_example.InternalInterface\""));
}

#[test]
fn matches_internal_api_by_component_interface_id_when_alias_differs() {
let component_diagrams = component_diagrams_with_entities(vec![
unit("u1", &["pkg.InternalInterface"]),
unit("u2", &["pkg.InternalInterface"]),
interface_with_id("pkg.InternalInterface", "InternalInterface"),
let component_diagrams = component_diagram(vec![
unit("u1", &["pkg.InternalInterface"], &[]),
unit("u2", &["pkg.InternalInterface"], &[]),
interface_with_parent_id("InternalInterface", "pkg"),
]);
let internal_api = internal_api_index(vec![("pkg.InternalInterface", vec!["GetData"])]);

let validation_result = validate(component_diagrams, &internal_api);

assert!(validation_result.failures.is_empty());
}

#[test]
fn ignores_component_interface_without_parent_id() {
let component_diagrams = component_diagram(vec![
unit("u1", &["InternalInterface"], &[]),
interface("InternalInterface"), // no parent_id
]);
let internal_api = internal_api_index(vec![]);

let validation_result = validate(component_diagrams, &internal_api);

assert!(validation_result.failures.is_empty());
}
Loading
Loading