Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6d11e41
test(storage-api): certify the 95 percent Rust coverage floor
DecisionNerd Aug 3, 2026
1b946aa
test(storage-api): close remaining coverage boundaries
DecisionNerd Aug 3, 2026
219b1e2
test(coverage): measure production Rust honestly
DecisionNerd Aug 3, 2026
830ef2b
test(rust): cover remaining production boundaries
DecisionNerd Aug 3, 2026
5724935
test(rust): exercise fragmented production branches
DecisionNerd Aug 3, 2026
8efac44
test(rust): cover closed runtime contracts
DecisionNerd Aug 3, 2026
21b2dd2
test(rust): exercise remaining facade boundaries
DecisionNerd Aug 3, 2026
1f6ebb6
test(rust): close remaining boundary gaps
DecisionNerd Aug 3, 2026
244ff49
test(rust): exercise residual production contracts
DecisionNerd Aug 4, 2026
14aaa90
test(rust): broaden production boundary coverage
DecisionNerd Aug 4, 2026
fa98783
test(rust): probe defensive facade paths
DecisionNerd Aug 4, 2026
bb027a3
refactor(rust): consolidate defensive validation paths
DecisionNerd Aug 4, 2026
b407eb0
test(rust): exercise remaining execution paths
DecisionNerd Aug 4, 2026
1fae930
test(rust): cover public execution boundaries
DecisionNerd Aug 4, 2026
90485db
test(rust): harden publication and value boundaries
DecisionNerd Aug 4, 2026
f8072da
test(rust): cover remaining public error contracts
DecisionNerd Aug 4, 2026
12af9a7
test(rust): cover persistent boundary contracts
DecisionNerd Aug 4, 2026
303ba1c
test(rust): exercise durable semantic matrices
DecisionNerd Aug 4, 2026
56d1633
test(rust): target uncovered fail-closed paths
DecisionNerd Aug 4, 2026
24fe86c
test(rust): exercise exact uncovered contracts
DecisionNerd Aug 4, 2026
6949b09
test(rust): close remaining storage and facade coverage gaps
DecisionNerd Aug 4, 2026
0ee3d02
fix(storage): import Schema in adjacency coverage tests
DecisionNerd Aug 4, 2026
2414e8a
fix(tests): align coverage probes with fail-closed contracts
DecisionNerd Aug 4, 2026
8e42bd6
test(rust): cover reverse Utf8 and missing label type_ids paths
DecisionNerd Aug 4, 2026
e93ef26
test(storage): exercise parquet and io error helpers
DecisionNerd Aug 4, 2026
cc1ea82
style: apply rustfmt and ruff on coverage probes
DecisionNerd Aug 4, 2026
6f0d836
style(ci): satisfy ruff C420 in coverage ledger tests
DecisionNerd Aug 4, 2026
bf22d28
test(rust): harden coverage probes from review findings
DecisionNerd Aug 4, 2026
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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ test-tck: ## Run TCK compliance tests via Rust BDD runner
cargo test -p graphforge-core --test bdd

# Multi-surface coverage thresholds (#742 §2). Override per surface as needed.
COVERAGE_FAIL_UNDER_RUST ?= 93.5
COVERAGE_FAIL_UNDER_RUST ?= 95
COVERAGE_FAIL_UNDER_RUST_CRATE ?= 80
COVERAGE_FAIL_UNDER_RUST_PATCH ?= 90
COVERAGE_FAIL_UNDER_RUST_PYTHON_ADAPTER ?= 80
Expand Down Expand Up @@ -141,7 +141,7 @@ check-coverage-python: ## Validate Python wrapper coverage (≥85% default)
(echo "❌ Python wrapper coverage below $(COVERAGE_FAIL_UNDER_PYTHON)%" && exit 1)
@echo "✅ Python wrapper coverage meets threshold"

check-coverage-rust: ## Validate core (≥93.5%), crates (≥80%), patch (≥90%), adapters (≥80%)
check-coverage-rust: ## Validate core (≥95%), crates (≥80%), patch (≥90%), adapters (≥80%)
@test -f build/coverage-rust/ledger.json || \
(echo "❌ Missing build/coverage-rust/ledger.json — run make coverage-rust first"; exit 1)
@COVERAGE_FAIL_UNDER_RUST=$(COVERAGE_FAIL_UNDER_RUST) \
Expand Down
98 changes: 97 additions & 1 deletion crates/graphforge-api/src/algorithm_runs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ fn api_error(code: ApiErrorCode, message: impl Into<String>) -> GfError {

#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::collections::{BTreeMap, HashMap};
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};

Expand Down Expand Up @@ -795,6 +795,102 @@ mod tests {
(graph, descriptor)
}

#[test]
fn wave10_request_identity_and_error_translation_guards_are_structured() {
let root = tempfile::tempdir().unwrap();
let (_, descriptor) = recorded_fixture(&root);
let base = RecordedAlgorithmRequest {
context: WriteContext {
operation_uuid: OperationId(uuid7(20)),
actor_uuid: None,
},
run_uuid: uuid7(21),
descriptor,
cancellation: None,
};

let mut request = base.clone();
request.context.operation_uuid = OperationId(Uuid::nil());
assert_eq!(
validate_request(&request).unwrap_err().code(),
"GF_VALIDATION"
);
let mut request = base.clone();
request.context.actor_uuid = Some(Uuid::nil());
assert_eq!(
validate_request(&request).unwrap_err().code(),
"GF_VALIDATION"
);
let mut request = base.clone();
request.run_uuid = Uuid::nil();
assert_eq!(
validate_request(&request).unwrap_err().code(),
"GF_VALIDATION"
);
let mut request = base;
request.run_uuid = Uuid::from_u128(1);
assert_eq!(
validate_request(&request).unwrap_err().code(),
"GF_VALIDATION"
);

assert_eq!(
invocation_error(InvocationError::SchemaMismatch).code(),
"GF_SCHEMA_MISMATCH"
);
assert_eq!(
invocation_error(InvocationError::ProjectionChanged).code(),
"GF_EXECUTION"
);
assert_eq!(
invocation_error(InvocationError::Graph(GfError::Validation("bad".into()))).code(),
"GF_VALIDATION"
);
let descriptor_error = InvocationDescriptor::new(
Algorithm::Rank(RankAlgorithm::Degree),
[0; 32],
BTreeMap::new(),
)
.unwrap_err();
assert_eq!(
invocation_error(InvocationError::Descriptor(descriptor_error)).code(),
"GF_VALIDATION"
);
}

#[test]
fn wave10_existing_run_lifecycle_errors_distinguish_all_terminal_states() {
let completed = AlgorithmRunEvent::new(
uuid7(30),
uuid7(31),
AlgorithmRunState::Completed,
Some([1; 32]),
None,
2,
uuid7(32),
)
.unwrap();
assert_eq!(
existing_lifecycle_error(Some(&completed)).code(),
"GF_RESULT_NOT_RETAINED"
);
let failed = AlgorithmRunEvent::new(
uuid7(33),
uuid7(31),
AlgorithmRunState::Failed,
None,
Some("GF_TEST".into()),
3,
uuid7(34),
)
.unwrap();
assert_eq!(
existing_lifecycle_error(Some(&failed)).code(),
"GF_EXECUTION"
);
assert_eq!(existing_lifecycle_error(None).code(), "GF_EXECUTION");
}

#[test]
fn recorded_dispatch_commits_start_and_completed_before_return() {
let root = tempfile::tempdir().unwrap();
Expand Down
231 changes: 230 additions & 1 deletion crates/graphforge-api/src/belief_projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1393,7 +1393,7 @@ mod tests {
use std::sync::{Arc, mpsc};
use std::time::Duration;

use arrow::array::{FixedSizeBinaryBuilder, ListBuilder, StringBuilder};
use arrow::array::{FixedSizeBinaryArray, FixedSizeBinaryBuilder, ListBuilder, StringBuilder};
use arrow::datatypes::{DataType, Field, Schema};

use super::*;
Expand All @@ -1411,6 +1411,226 @@ mod tests {
Uuid::from_bytes(bytes)
}

#[test]
fn wave12_attachment_and_projection_helper_errors_are_exact_and_closed() {
let duplicate_policy = BeliefProjectionPolicyV1 {
included_statuses: vec![AssertionStatus::Supported, AssertionStatus::Supported],
statusless: StatuslessPolicyV1::Include,
supersession_branches: SupersessionBranchPolicyV1::Reject,
hypotheses: HypothesisSelectionPolicyV1::IncludeAllCurrentMembers,
};
assert_eq!(
duplicate_policy.canonical_bytes().unwrap_err().code(),
"GF_VALIDATION"
);
let descriptor = InvocationDescriptor::new(
graphforge_core::algorithms::Algorithm::Rank(
graphforge_core::algorithms::RankAlgorithm::Degree,
),
[0; 32],
BTreeMap::from([
("directed".into(), crate::InvocationParameter::Bool(false)),
(
"label".into(),
crate::InvocationParameter::Utf8("Person".into()),
),
("via".into(), crate::InvocationParameter::Utf8("*".into())),
]),
)
.unwrap();
let valid = AttachResolvedRunRequest {
context: WriteContext {
operation_uuid: OperationId(uuid7(1)),
actor_uuid: None,
},
attachment_uuid: uuid7(2),
run_uuid: uuid7(3),
descriptor,
};
assert!(validate_attachment_request(&valid).is_ok());
for (mut request, message) in [
(
AttachResolvedRunRequest {
context: WriteContext {
operation_uuid: OperationId(Uuid::nil()),
actor_uuid: None,
},
..valid.clone()
},
"operation_uuid must not be nil",
),
(
AttachResolvedRunRequest {
attachment_uuid: Uuid::nil(),
..valid.clone()
},
"attachment_uuid must not be nil",
),
(
AttachResolvedRunRequest {
run_uuid: Uuid::nil(),
..valid.clone()
},
"run_uuid must not be nil",
),
(
AttachResolvedRunRequest {
context: WriteContext {
operation_uuid: OperationId(uuid7(4)),
actor_uuid: Some(Uuid::nil()),
},
..valid.clone()
},
"actor_uuid must not be nil",
),
(
AttachResolvedRunRequest {
attachment_uuid: Uuid::from_u128(5),
..valid
},
"attachment_uuid must be UUIDv7",
),
] {
let error = validate_attachment_request(&request).unwrap_err();
assert_eq!(error.code(), "GF_VALIDATION");
assert_eq!(error.to_string(), format!("validation error: {message}"));
request.context.actor_uuid = None;
}
assert_eq!(schema("bad schema").code(), "GF_SCHEMA_MISMATCH");
assert_eq!(ambiguous("two branches").code(), "GF_AMBIGUOUS_PROJECTION");
assert_eq!(
transaction_conflict("drift").code(),
"GF_IDEMPOTENCY_CONFLICT"
);
assert_eq!(
descriptor_fingerprint(b"not a descriptor")
.unwrap_err()
.code(),
"GF_SCHEMA_MISMATCH"
);

let empty = RecordBatch::new_empty(Arc::new(Schema::empty()));
assert_eq!(
exact_fingerprint(
&empty,
&FixedSizeBinaryArray::try_from_iter([vec![0; 32]].into_iter()).unwrap(),
)
.unwrap_err()
.code(),
"GF_SCHEMA_MISMATCH"
);
for result in [
strings(&empty, "missing").map(|_| ()),
fixed(&empty, "missing").map(|_| ()),
lists(&empty, "missing").map(|_| ()),
] {
assert_eq!(result.unwrap_err().code(), "GF_SCHEMA_MISMATCH");
}
let no_batches = graphforge_exec::ExecutionResult {
schema: Arc::new(Schema::empty()),
batches: vec![],
stats: graphforge_exec::ExecutionStats::default(),
side_effects: None,
mutation_receipt: None,
};
assert_eq!(
one_batch(&no_batches, "empty").unwrap_err().code(),
"GF_SCHEMA_MISMATCH"
);
let null_uuid =
FixedSizeBinaryArray::try_from_sparse_iter_with_size([None::<Vec<u8>>].into_iter(), 16)
.unwrap();
assert_eq!(
uuid_at(&null_uuid, 0, "assertion_uuid").unwrap_err().code(),
"GF_SCHEMA_MISMATCH"
);
let fingerprints =
FixedSizeBinaryArray::try_from_iter([vec![1; 32], vec![2; 32]].into_iter()).unwrap();
let fingerprint_batch = RecordBatch::try_new(
Arc::new(Schema::new(vec![Field::new(
"result_fingerprint",
DataType::FixedSizeBinary(32),
false,
)])),
vec![Arc::new(fingerprints.clone())],
)
.unwrap();
assert_eq!(
exact_fingerprint(&fingerprint_batch, &fingerprints)
.unwrap_err()
.code(),
"GF_SCHEMA_MISMATCH"
);
}

#[test]
fn attachment_replay_requires_exact_resolved_content() {
let graph = GraphForge::new(None).unwrap();
let policy = BeliefProjectionPolicyV1 {
included_statuses: vec![AssertionStatus::Supported],
statusless: StatuslessPolicyV1::Include,
supersession_branches: SupersessionBranchPolicyV1::Reject,
hypotheses: HypothesisSelectionPolicyV1::IncludeAllCurrentMembers,
};
let (policy_bytes, policy_fingerprint) = policy_fingerprint(&policy).unwrap();
let descriptor = InvocationDescriptor::new(
graphforge_core::algorithms::Algorithm::Rank(
graphforge_core::algorithms::RankAlgorithm::Degree,
),
[0; 32],
BTreeMap::from([
("directed".into(), crate::InvocationParameter::Bool(false)),
(
"label".into(),
crate::InvocationParameter::Utf8("Person".into()),
),
("via".into(), crate::InvocationParameter::Utf8("*".into())),
]),
)
.unwrap();
let projection = ResolvedBeliefProjection {
graph: Box::new(graph),
source_generation_uuid: uuid7(20),
transaction_cutoff_micros: 10,
valid_time_micros: None,
policy_bytes: policy_bytes.clone(),
policy_fingerprint,
snapshot_fingerprint: [1; 32],
valid_time_fingerprint: None,
graph_content_fingerprint: [2; 32],
source_record_uuids: vec![uuid7(21)],
};
let request = AttachResolvedRunRequest {
context: context(22),
attachment_uuid: uuid7(23),
run_uuid: uuid7(24),
descriptor,
};
let row = BeliefProjectionAttachment::new(
request.attachment_uuid,
uuid7(25),
projection.source_generation_uuid,
projection.transaction_cutoff_micros,
projection.valid_time_micros,
BELIEF_PROJECTION_POLICY_VERSION,
policy_bytes,
projection.snapshot_fingerprint,
projection.valid_time_fingerprint,
projection.graph_content_fingerprint,
*request.descriptor.fingerprint(),
projection.source_record_uuids.clone(),
uuid7(26),
11,
)
.unwrap();
assert_eq!(
verify_existing_attachment(&row, &projection, &request)
.unwrap_err()
.code(),
"GF_IDEMPOTENCY_CONFLICT"
);
}

fn context(seed: u8) -> WriteContext {
WriteContext {
operation_uuid: OperationId(uuid7(seed)),
Expand Down Expand Up @@ -1485,6 +1705,15 @@ mod tests {
policy: policy.clone(),
})
.unwrap();
assert_ne!(projection.source_generation_uuid(), Uuid::nil());
assert!(!projection.policy_bytes().is_empty());
assert_ne!(projection.policy_fingerprint(), [0; 32]);
assert_ne!(projection.snapshot_fingerprint(), [0; 32]);
assert_eq!(projection.transaction_cutoff_micros(), i64::MAX);
assert_eq!(projection.valid_time_micros(), None);
assert_eq!(projection.valid_time_fingerprint(), None);
assert_ne!(projection.graph_content_fingerprint(), [0; 32]);
assert_eq!(projection.source_record_uuids(), &[uuid7(5)]);
assert_eq!(
graphforge_storage::read_nodes(&projection.graph.dir)
.unwrap()
Expand Down
Loading
Loading