#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::needless_raw_string_hashes,
clippy::duration_suboptimal_units,
clippy::branches_sharing_code,
clippy::used_underscore_binding,
clippy::single_char_pattern,
clippy::ignore_without_reason,
clippy::cloned_ref_to_slice_refs,
clippy::doc_overindented_list_items,
clippy::match_wildcard_for_single_variants,
clippy::ignored_unit_patterns,
clippy::needless_collect,
clippy::unnecessary_map_or,
clippy::manual_flatten,
clippy::manual_strip,
clippy::future_not_send,
clippy::unnested_or_patterns,
clippy::no_effect_underscore_binding,
clippy::literal_string_with_formatting_args
)]
use ggen_core::codegen::pipeline::GenerationPipeline;
use ggen_core::manifest::{
GenerationConfig, GenerationMode, GenerationRule, GgenManifest, InferenceConfig,
OntologyConfig, ProjectConfig, QuerySource, TemplateSource, ValidationConfig,
};
use std::collections::BTreeMap;
use std::path::PathBuf;
use tempfile::TempDir;
#[test]
fn test_rule_executed_when_ask_true() {
let rule = GenerationRule {
name: "conditional_rule".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Generated code".to_string(),
},
output_file: "output.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: false,
when: None,
};
assert_eq!(rule.name, "conditional_rule", "Rule name should match");
assert_eq!(rule.mode, GenerationMode::Create, "Mode should be Create");
}
#[test]
fn test_rule_skipped_when_ask_false() {
let rule = GenerationRule {
name: "skipped_rule".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Should not generate".to_string(),
},
output_file: "skipped_output.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: false,
when: None,
};
assert!(
!rule.skip_empty,
"Rule should not skip on empty results by default"
);
assert_eq!(rule.name, "skipped_rule", "Rule name should match");
}
#[test]
fn test_rule_executed_when_no_condition() {
let rule = GenerationRule {
name: "unconditional_rule".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Always generated".to_string(),
},
output_file: "always_output.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: false,
when: None,
};
assert!(
!rule.skip_empty,
"Rule should not skip on empty (always execute)"
);
assert_eq!(rule.name, "unconditional_rule", "Rule name should match");
}
#[test]
fn test_malformed_sparql_handled() {
let rule_invalid_syntax = GenerationRule {
name: "malformed_rule".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Code".to_string(),
},
output_file: "output.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: false,
when: None,
};
assert_eq!(
rule_invalid_syntax.name, "malformed_rule",
"Rule should be created"
);
if let QuerySource::Inline { inline } = &rule_invalid_syntax.query {
assert!(!inline.is_empty(), "Query should have content");
} else {
panic!("Expected inline query");
}
}
#[test]
fn test_condition_logging() {
let manifest = GgenManifest {
project: ProjectConfig {
name: "condition_test".to_string(),
version: "1.0.0".to_string(),
description: Some("Testing conditional execution logging".to_string()),
..Default::default()
},
ontology: OntologyConfig {
source: PathBuf::from("ontology.ttl"),
imports: vec![],
base_iri: None,
prefixes: BTreeMap::new(),
..Default::default()
},
inference: InferenceConfig {
rules: vec![],
max_reasoning_timeout_ms: 5000,
},
generation: GenerationConfig {
rules: vec![
GenerationRule {
name: "rule_with_condition".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Generated".to_string(),
},
output_file: "output.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: true, when: None,
},
GenerationRule {
name: "rule_without_condition".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Always generated".to_string(),
},
output_file: "always.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: false, when: None,
},
],
max_sparql_timeout_ms: 5000,
require_audit_trail: false,
enable_llm: false,
llm_provider: None,
llm_model: None,
determinism_salt: None,
output_dir: PathBuf::from("generated"),
},
validation: ValidationConfig::default(),
packs: vec![],
..Default::default()
};
assert_eq!(manifest.generation.rules.len(), 2, "Should have 2 rules");
assert!(
manifest.generation.rules[0].skip_empty,
"First rule should skip on empty"
);
assert!(
!manifest.generation.rules[1].skip_empty,
"Second rule should never skip"
);
}
#[test]
fn test_multiple_conditions_in_manifest() {
let manifest = GgenManifest {
project: ProjectConfig {
name: "multi_condition_test".to_string(),
version: "1.0.0".to_string(),
description: None,
..Default::default()
},
ontology: OntologyConfig {
source: PathBuf::from("ontology.ttl"),
imports: vec![],
base_iri: None,
prefixes: BTreeMap::new(),
..Default::default()
},
inference: InferenceConfig {
rules: vec![],
max_reasoning_timeout_ms: 5000,
},
generation: GenerationConfig {
rules: vec![
GenerationRule {
name: "rule1".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Rule 1".to_string(),
},
output_file: "output1.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: true, when: None,
},
GenerationRule {
name: "rule2".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Rule 2".to_string(),
},
output_file: "output2.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: true, when: None,
},
GenerationRule {
name: "rule3".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Rule 3".to_string(),
},
output_file: "output3.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: false, when: None,
},
],
max_sparql_timeout_ms: 5000,
require_audit_trail: false,
enable_llm: false,
llm_provider: None,
llm_model: None,
determinism_salt: None,
output_dir: PathBuf::from("generated"),
},
validation: ValidationConfig::default(),
packs: vec![],
..Default::default()
};
let rules = &manifest.generation.rules;
assert_eq!(rules.len(), 3, "Should have 3 rules");
assert!(rules[0].skip_empty, "Rule 1 should skip on empty");
assert!(rules[1].skip_empty, "Rule 2 should skip on empty");
assert!(!rules[2].skip_empty, "Rule 3 should never skip");
}
#[test]
fn test_complex_sparql_queries() {
let rule = GenerationRule {
name: "complex_query".to_string(),
query: QuerySource::Inline {
inline: r#"SELECT ?s ?prop WHERE {
?s a <http://example.org/Entity> .
?s <http://example.org/hasProperty> ?prop .
FILTER(?prop > 10)
}"#
.to_string(),
},
template: TemplateSource::Inline {
inline: "// Complex query result: {{s}} has property {{prop}}".to_string(),
},
output_file: "output.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: true, when: None,
};
if let QuerySource::Inline { inline } = &rule.query {
assert!(inline.contains("SELECT"), "Should be SELECT query");
assert!(inline.contains("FILTER"), "Should have FILTER clause");
assert!(
inline.contains("hasProperty"),
"Should have property pattern"
);
assert!(inline.contains("> 10"), "Should have filter condition");
} else {
panic!("Expected inline query");
}
assert!(
rule.skip_empty,
"Should skip if query returns empty (after filter)"
);
}
#[test]
fn test_query_with_prefixes() {
let rule = GenerationRule {
name: "prefixed_query".to_string(),
query: QuerySource::Inline {
inline: r#"PREFIX ex: <http://example.org/>
SELECT ?s WHERE { ?s a ex:Type }"#
.to_string(),
},
template: TemplateSource::Inline {
inline: "// Prefixed query result: {{s}}".to_string(),
},
output_file: "output.rs".to_string(),
mode: GenerationMode::Create,
skip_empty: false,
when: None,
};
if let QuerySource::Inline { inline } = &rule.query {
assert!(inline.contains("PREFIX"), "Should have PREFIX declaration");
assert!(inline.contains("ex:Type"), "Should use prefix in pattern");
} else {
panic!("Expected inline query");
}
}
fn create_test_ontology() -> String {
r#"
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
ex:Entity1 a ex:Type ;
ex:hasProperty "value1" ;
ex:enabled true .
ex:Entity2 a ex:Type ;
ex:hasProperty "value2" ;
ex:enabled false .
"#
.to_string()
}
#[test]
fn test_integration_rule_executed_when_ask_true() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let ontology_path = temp_dir.path().join("ontology.ttl");
std::fs::write(&ontology_path, create_test_ontology()).expect("Failed to write ontology");
let manifest = GgenManifest {
project: ProjectConfig {
name: "conditional_test".to_string(),
version: "1.0.0".to_string(),
description: None,
..Default::default()
},
ontology: OntologyConfig {
source: ontology_path.clone(),
imports: vec![],
base_iri: None,
prefixes: BTreeMap::new(),
..Default::default()
},
inference: InferenceConfig {
rules: vec![],
max_reasoning_timeout_ms: 5000,
},
generation: GenerationConfig {
rules: vec![GenerationRule {
name: "conditional_rule".to_string(),
query: QuerySource::Inline {
inline: "PREFIX ex: <http://example.org/>\nSELECT ?s ?prop WHERE { ?s a ex:Type . ?s ex:hasProperty ?prop }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Entity: {{s}}, Property: {{prop}}".to_string(),
},
output_file: "output.txt".to_string(),
mode: GenerationMode::Overwrite,
skip_empty: false,
when: Some("PREFIX ex: <http://example.org/>\nASK { ?s a ex:Type }".to_string()),
}],
max_sparql_timeout_ms: 5000,
require_audit_trail: false,
enable_llm: false,
llm_provider: None,
llm_model: None,
determinism_salt: None,
output_dir: temp_dir.path().to_path_buf(),
},
validation: ValidationConfig::default(),
packs: vec![],
..Default::default()
};
let mut pipeline = GenerationPipeline::new(manifest, temp_dir.path().to_path_buf());
let result = pipeline.run();
assert!(result.is_ok(), "Pipeline should succeed");
let state = result.expect("Pipeline state");
assert_eq!(
state.generated_files.len(),
1,
"Static output_file renders once with all rows -> exactly 1 file when condition is true"
);
let output_path = temp_dir.path().join("output.txt");
assert!(
output_path.exists(),
"Output file should exist when condition is true"
);
}
#[test]
fn test_integration_rule_skipped_when_ask_false() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let ontology_path = temp_dir.path().join("ontology.ttl");
std::fs::write(&ontology_path, create_test_ontology()).expect("Failed to write ontology");
let manifest = GgenManifest {
project: ProjectConfig {
name: "skip_test".to_string(),
version: "1.0.0".to_string(),
description: None,
..Default::default()
},
ontology: OntologyConfig {
source: ontology_path.clone(),
imports: vec![],
base_iri: None,
prefixes: BTreeMap::new(),
..Default::default()
},
inference: InferenceConfig {
rules: vec![],
max_reasoning_timeout_ms: 5000,
},
generation: GenerationConfig {
rules: vec![GenerationRule {
name: "skipped_rule".to_string(),
query: QuerySource::Inline {
inline: "PREFIX ex: <http://example.org/>\nSELECT ?s WHERE { ?s a ex:Type }"
.to_string(),
},
template: TemplateSource::Inline {
inline: "// Should not generate".to_string(),
},
output_file: "skipped.txt".to_string(),
mode: GenerationMode::Overwrite,
skip_empty: false,
when: Some(
"PREFIX ex: <http://example.org/>\nASK { ?s a ex:NonExistentType }".to_string(),
),
}],
max_sparql_timeout_ms: 5000,
require_audit_trail: false,
enable_llm: false,
llm_provider: None,
llm_model: None,
determinism_salt: None,
output_dir: temp_dir.path().to_path_buf(),
},
validation: ValidationConfig::default(),
packs: vec![],
..Default::default()
};
let mut pipeline = GenerationPipeline::new(manifest, temp_dir.path().to_path_buf());
let result = pipeline.run();
assert!(result.is_ok(), "Pipeline should succeed");
let state = result.expect("Pipeline state");
assert_eq!(
state.generated_files.len(),
0,
"Should generate 0 files when condition is false"
);
let output_path = temp_dir.path().join("skipped.txt");
assert!(
!output_path.exists(),
"Output file should NOT exist when condition is false"
);
}
#[test]
fn test_integration_malformed_ask_query_error() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let ontology_path = temp_dir.path().join("ontology.ttl");
std::fs::write(&ontology_path, create_test_ontology()).expect("Failed to write ontology");
let manifest = GgenManifest {
project: ProjectConfig {
name: "malformed_test".to_string(),
version: "1.0.0".to_string(),
description: None,
..Default::default()
},
ontology: OntologyConfig {
source: ontology_path.clone(),
imports: vec![],
base_iri: None,
prefixes: BTreeMap::new(),
..Default::default()
},
inference: InferenceConfig {
rules: vec![],
max_reasoning_timeout_ms: 5000,
},
generation: GenerationConfig {
rules: vec![GenerationRule {
name: "malformed_rule".to_string(),
query: QuerySource::Inline {
inline: "SELECT * WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Should not execute".to_string(),
},
output_file: "output.txt".to_string(),
mode: GenerationMode::Overwrite,
skip_empty: false,
when: Some("SELECT ?s WHERE { ?s ?p ?o }".to_string()),
}],
max_sparql_timeout_ms: 5000,
require_audit_trail: false,
enable_llm: false,
llm_provider: None,
llm_model: None,
determinism_salt: None,
output_dir: temp_dir.path().to_path_buf(),
},
validation: ValidationConfig::default(),
packs: vec![],
..Default::default()
};
let mut pipeline = GenerationPipeline::new(manifest, temp_dir.path().to_path_buf());
let result = pipeline.run();
assert!(
result.is_err(),
"Pipeline should fail on malformed ASK query"
);
let error_msg = match result {
Err(e) => e.to_string(),
Ok(_) => panic!("Expected error but got success"),
};
assert!(
error_msg.contains("ASK") || error_msg.contains("must be ASK query"),
"Error should mention ASK query requirement, got: {}",
error_msg
);
}
#[test]
fn test_integration_multiple_conditions() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let ontology_path = temp_dir.path().join("ontology.ttl");
std::fs::write(&ontology_path, create_test_ontology()).expect("Failed to write ontology");
let manifest = GgenManifest {
project: ProjectConfig {
name: "multi_condition_test".to_string(),
version: "1.0.0".to_string(),
description: None,
..Default::default()
},
ontology: OntologyConfig {
source: ontology_path.clone(),
imports: vec![],
base_iri: None,
prefixes: BTreeMap::new(),
..Default::default()
},
inference: InferenceConfig {
rules: vec![],
max_reasoning_timeout_ms: 5000,
},
generation: GenerationConfig {
rules: vec![
GenerationRule {
name: "rule1_executes".to_string(),
query: QuerySource::Inline {
inline:
"PREFIX ex: <http://example.org/>\nSELECT ?s WHERE { ?s a ex:Type }"
.to_string(),
},
template: TemplateSource::Inline {
inline: "// Rule 1 executed".to_string(),
},
output_file: "rule1.txt".to_string(),
mode: GenerationMode::Overwrite,
skip_empty: false,
when: Some(
"PREFIX ex: <http://example.org/>\nASK { ?s a ex:Type }".to_string(),
),
},
GenerationRule {
name: "rule2_skipped".to_string(),
query: QuerySource::Inline {
inline: "SELECT ?s WHERE { ?s ?p ?o }".to_string(),
},
template: TemplateSource::Inline {
inline: "// Rule 2 should not execute".to_string(),
},
output_file: "rule2.txt".to_string(),
mode: GenerationMode::Overwrite,
skip_empty: false,
when: Some(
"PREFIX ex: <http://example.org/>\nASK { ?s a ex:NonExistent }".to_string(),
),
},
GenerationRule {
name: "rule3_always".to_string(),
query: QuerySource::Inline {
inline:
"PREFIX ex: <http://example.org/>\nSELECT ?s WHERE { ?s a ex:Type }"
.to_string(),
},
template: TemplateSource::Inline {
inline: "// Rule 3 always executed".to_string(),
},
output_file: "rule3.txt".to_string(),
mode: GenerationMode::Overwrite,
skip_empty: false,
when: None, },
],
max_sparql_timeout_ms: 5000,
require_audit_trail: false,
enable_llm: false,
llm_provider: None,
llm_model: None,
determinism_salt: None,
output_dir: temp_dir.path().to_path_buf(),
},
validation: ValidationConfig::default(),
packs: vec![],
..Default::default()
};
let mut pipeline = GenerationPipeline::new(manifest, temp_dir.path().to_path_buf());
let result = pipeline.run();
assert!(result.is_ok(), "Pipeline should succeed");
let state = result.expect("Pipeline state");
assert_eq!(
state.generated_files.len(),
2,
"Static outputs render once per rule -> rule1: 1, rule3: 1, rule2 skipped = 2 files"
);
let rule1_path = temp_dir.path().join("rule1.txt");
assert!(rule1_path.exists(), "Rule 1 output should exist");
let rule2_path = temp_dir.path().join("rule2.txt");
assert!(!rule2_path.exists(), "Rule 2 output should NOT exist");
let rule3_path = temp_dir.path().join("rule3.txt");
assert!(rule3_path.exists(), "Rule 3 output should exist");
}