use super::rust_index::{
self, FunctionSummary, OracleFact, RustIndex, TestSummary, extract_identifier_tokens,
};
use super::seams::{ExpectedSink, RepoSeam, SeamId, SeamKind};
use crate::domain::{
Confidence, MissingDiscriminatorFact, OracleKind, OracleStrength, StageEvidence, StageState,
ValueContext, ValueFact,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::path::{Path, PathBuf};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct TestGripEvidence {
pub(crate) seam_id: SeamId,
pub(crate) related_tests: Vec<RelatedTestGrip>,
pub(crate) reach: StageEvidence,
pub(crate) activate: StageEvidence,
pub(crate) propagate: StageEvidence,
pub(crate) observe: StageEvidence,
pub(crate) discriminate: StageEvidence,
pub(crate) observed_values: Vec<ValueFact>,
pub(crate) missing_discriminators: Vec<MissingDiscriminatorFact>,
}
const COMPACT_RELATED_TEST_LIMIT: usize = 12;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct RelatedTestGrip {
pub(crate) test_name: String,
pub(crate) file: PathBuf,
pub(crate) line: usize,
pub(crate) oracle_kind: OracleKind,
pub(crate) oracle_strength: OracleStrength,
pub(crate) evidence_summary: String,
pub(crate) relation_reason: RelationReason,
pub(crate) relation_confidence: RelationConfidence,
}
pub(crate) struct CompactGripContext<'a> {
index: &'a RustIndex,
tests: Vec<CompactTest<'a>>,
}
struct CompactTest<'a> {
test: &'a TestSummary,
path_normalized: String,
module_path: Option<String>,
name_lower: String,
call_names: BTreeSet<String>,
assertion_tokens: BTreeSet<String>,
code_lines: Vec<String>,
}
impl<'a> CompactGripContext<'a> {
pub(crate) fn new(index: &'a RustIndex) -> Self {
let tests = index
.tests
.iter()
.map(|test| {
let call_names = test
.calls
.iter()
.map(|call| call.name.clone())
.collect::<BTreeSet<_>>();
let mut assertion_tokens = BTreeSet::new();
for assertion in &test.assertions {
for token in extract_identifier_tokens(&assertion.text) {
assertion_tokens.insert(token);
}
}
let code_lines = test
.body
.lines()
.map(strip_comments_and_strings)
.collect::<Vec<_>>();
CompactTest {
test,
path_normalized: normalize_path(&test.file),
module_path: module_path_for(&test.file),
name_lower: test.name.to_ascii_lowercase(),
call_names,
assertion_tokens,
code_lines,
}
})
.collect();
Self { index, tests }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum RelationReason {
DirectOwnerCall,
AssertionTargetAffinity,
SameTestFile,
SameModule,
OwnerNamedTest,
ImportPathAffinity,
FixtureOwnerAffinity,
}
impl RelationReason {
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::DirectOwnerCall => "direct_owner_call",
Self::AssertionTargetAffinity => "assertion_target_affinity",
Self::SameTestFile => "same_test_file",
Self::SameModule => "same_module",
Self::OwnerNamedTest => "owner_named_test",
Self::ImportPathAffinity => "import_path_affinity",
Self::FixtureOwnerAffinity => "fixture_owner_affinity",
}
}
fn priority(self) -> u8 {
match self {
Self::DirectOwnerCall => 0,
Self::AssertionTargetAffinity => 1,
Self::SameTestFile => 2,
Self::SameModule => 3,
Self::OwnerNamedTest => 4,
Self::ImportPathAffinity => 5,
Self::FixtureOwnerAffinity => 6,
}
}
fn confidence(self) -> RelationConfidence {
match self {
Self::DirectOwnerCall | Self::AssertionTargetAffinity => RelationConfidence::High,
Self::SameTestFile
| Self::SameModule
| Self::OwnerNamedTest
| Self::ImportPathAffinity => RelationConfidence::Medium,
Self::FixtureOwnerAffinity => RelationConfidence::Low,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum RelationConfidence {
High,
Medium,
Low,
Opaque,
}
impl RelationConfidence {
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::High => "high",
Self::Medium => "medium",
Self::Low => "low",
Self::Opaque => "opaque",
}
}
fn rank(self) -> u8 {
match self {
Self::High => 0,
Self::Medium => 1,
Self::Low => 2,
Self::Opaque => 3,
}
}
}
pub(crate) fn evidence_for_seams(seams: &[RepoSeam], index: &RustIndex) -> Vec<TestGripEvidence> {
let mut out: Vec<TestGripEvidence> = seams
.iter()
.map(|seam| evidence_for_seam(seam, index))
.collect();
out.sort_by(|a, b| a.seam_id.as_str().cmp(b.seam_id.as_str()));
out
}
pub(crate) fn evidence_for_seam(seam: &RepoSeam, index: &RustIndex) -> TestGripEvidence {
let related_with_reason = find_related_tests(seam, index);
let owner_fn = find_owner_function(seam, index);
let related: Vec<&TestSummary> = related_with_reason.iter().map(|(t, _)| *t).collect();
let reach = reach_evidence(seam, &related);
let (activate, observed_values, missing_discriminators) =
activate_evidence(seam, &related, owner_fn, index);
let propagate = propagate_evidence(seam, &related);
let observe = observe_evidence(&related);
let discriminate = discriminate_evidence(seam, &related);
let mut related_tests: Vec<RelatedTestGrip> = related_with_reason
.iter()
.map(|(test, reason)| related_test_grip(seam, test, *reason))
.collect();
related_tests.sort_by(|a, b| {
a.relation_confidence
.rank()
.cmp(&b.relation_confidence.rank())
.then(
a.relation_reason
.priority()
.cmp(&b.relation_reason.priority()),
)
.then(a.file.cmp(&b.file))
.then(a.test_name.cmp(&b.test_name))
.then(a.line.cmp(&b.line))
});
TestGripEvidence {
seam_id: seam.id().clone(),
related_tests,
reach,
activate,
propagate,
observe,
discriminate,
observed_values,
missing_discriminators,
}
}
pub(crate) fn compact_evidence_for_seam(
seam: &RepoSeam,
context: &CompactGripContext<'_>,
) -> TestGripEvidence {
let related = find_related_tests_compact(seam, context);
let owner_fn = find_owner_function(seam, context.index);
let reach = reach_evidence(seam, &related);
let (activate, missing_discriminators) =
compact_activate_evidence(seam, &related, owner_fn, context.index);
let propagate = propagate_evidence(seam, &related);
let observe = observe_evidence(&related);
let discriminate = discriminate_evidence(seam, &related);
TestGripEvidence {
seam_id: seam.id().clone(),
related_tests: Vec::new(),
reach,
activate,
propagate,
observe,
discriminate,
observed_values: Vec::new(),
missing_discriminators,
}
}
fn find_related_tests<'a>(
seam: &RepoSeam,
index: &'a RustIndex,
) -> Vec<(&'a TestSummary, RelationReason)> {
let owner_fn = find_owner_function(seam, index);
let owner_name = owner_fn.map(|f| f.name.as_str()).unwrap_or("");
let owner_name_lower = owner_name.to_ascii_lowercase();
let owner_file = owner_fn.map(|f| f.file.as_path());
let owner_file_stem = owner_file
.and_then(|p| p.file_stem())
.and_then(|s| s.to_str())
.unwrap_or("");
let owner_module_path = owner_file.and_then(module_path_for);
let prefix = owner_fn.and_then(|f| package_prefix(&f.file));
let discriminator_tokens = required_discriminator_tokens(seam);
let sink_tokens = extract_identifier_tokens(seam.expected_sink().as_str());
let target_tokens: Vec<String> = discriminator_tokens
.into_iter()
.chain(sink_tokens)
.collect();
let mut related: Vec<(&'a TestSummary, RelationReason)> = Vec::new();
let mut seen: std::collections::HashSet<(String, std::path::PathBuf, usize)> =
std::collections::HashSet::new();
for test in &index.tests {
let test_path = normalize_path(&test.file);
if let Some(prefix) = &prefix
&& !test_path.starts_with(prefix)
{
continue;
}
let test_module_path = module_path_for(&test.file);
let test_name_lower = test.name.to_ascii_lowercase();
let reason =
if !owner_name.is_empty() && test.calls.iter().any(|call| call.name == owner_name) {
Some(RelationReason::DirectOwnerCall)
} else if !target_tokens.is_empty() && assertion_targets_seam(test, &target_tokens) {
Some(RelationReason::AssertionTargetAffinity)
} else if !owner_file_stem.is_empty() && same_test_file(&test.file, owner_file_stem) {
Some(RelationReason::SameTestFile)
} else if let (Some(owner_mod), Some(test_mod)) =
(owner_module_path.as_deref(), test_module_path.as_deref())
&& same_module(owner_mod, test_mod)
{
Some(RelationReason::SameModule)
} else if !owner_name_lower.is_empty() && test_name_lower.contains(&owner_name_lower) {
Some(RelationReason::OwnerNamedTest)
} else if !owner_name.is_empty() && test_imports_owner(test, owner_name) {
Some(RelationReason::ImportPathAffinity)
} else if test_uses_owner_fixture(test, owner_file, index) {
Some(RelationReason::FixtureOwnerAffinity)
} else {
None
};
let Some(reason) = reason else { continue };
let key = (test.name.clone(), test.file.clone(), test.start_line);
if seen.insert(key) {
related.push((test, reason));
}
}
related
}
fn find_related_tests_compact<'a>(
seam: &RepoSeam,
context: &'a CompactGripContext<'a>,
) -> Vec<&'a TestSummary> {
let owner_fn = find_owner_function(seam, context.index);
let owner_name = owner_fn.map(|f| f.name.as_str()).unwrap_or("");
let owner_name_lower = owner_name.to_ascii_lowercase();
let owner_file = owner_fn.map(|f| f.file.as_path());
let owner_file_stem = owner_file
.and_then(|p| p.file_stem())
.and_then(|s| s.to_str())
.unwrap_or("");
let owner_module_path = owner_file.and_then(module_path_for);
let prefix = owner_fn.and_then(|f| package_prefix(&f.file));
let fixture_names = owner_file
.and_then(|file| context.index.files.get(file))
.map(fixture_names_for_owner_file)
.unwrap_or_default();
let discriminator_tokens = required_discriminator_tokens(seam);
let sink_tokens = extract_identifier_tokens(seam.expected_sink().as_str());
let target_tokens: BTreeSet<String> = discriminator_tokens
.into_iter()
.chain(sink_tokens)
.collect();
let mut related: Vec<(&'a TestSummary, RelationReason)> = Vec::new();
let mut seen: std::collections::HashSet<(String, std::path::PathBuf, usize)> =
std::collections::HashSet::new();
for indexed in &context.tests {
if let Some(prefix) = &prefix
&& !indexed.path_normalized.starts_with(prefix)
{
continue;
}
let reason = if !owner_name.is_empty() && indexed.call_names.contains(owner_name) {
Some(RelationReason::DirectOwnerCall)
} else if !target_tokens.is_empty()
&& indexed
.assertion_tokens
.iter()
.any(|token| target_tokens.contains(token))
{
Some(RelationReason::AssertionTargetAffinity)
} else if !owner_file_stem.is_empty() && same_test_file(&indexed.test.file, owner_file_stem)
{
Some(RelationReason::SameTestFile)
} else if let (Some(owner_mod), Some(test_mod)) =
(owner_module_path.as_deref(), indexed.module_path.as_deref())
&& same_module(owner_mod, test_mod)
{
Some(RelationReason::SameModule)
} else if !owner_name_lower.is_empty() && indexed.name_lower.contains(&owner_name_lower) {
Some(RelationReason::OwnerNamedTest)
} else if !owner_name.is_empty() && test_imports_owner_compact(indexed, owner_name) {
Some(RelationReason::ImportPathAffinity)
} else if !fixture_names.is_empty()
&& indexed
.call_names
.iter()
.any(|call| fixture_names.contains(call))
{
Some(RelationReason::FixtureOwnerAffinity)
} else {
None
};
let Some(reason) = reason else { continue };
let key = (
indexed.test.name.clone(),
indexed.test.file.clone(),
indexed.test.start_line,
);
if seen.insert(key) {
related.push((indexed.test, reason));
}
}
related.sort_by(|(test_a, reason_a), (test_b, reason_b)| {
reason_a
.confidence()
.rank()
.cmp(&reason_b.confidence().rank())
.then(reason_a.priority().cmp(&reason_b.priority()))
.then(test_a.file.cmp(&test_b.file))
.then(test_a.name.cmp(&test_b.name))
.then(test_a.start_line.cmp(&test_b.start_line))
});
related
.into_iter()
.take(COMPACT_RELATED_TEST_LIMIT)
.map(|(test, _reason)| test)
.collect()
}
fn fixture_names_for_owner_file(facts: &rust_index::FileFacts) -> BTreeSet<String> {
facts
.functions
.iter()
.filter(|f| !f.is_test && (is_fixture_named(&f.name) || f.body.contains("#[fixture]")))
.map(|f| f.name.clone())
.collect()
}
fn required_discriminator_tokens(seam: &RepoSeam) -> Vec<String> {
use super::seams::RequiredDiscriminator;
let text = match seam.required_discriminator() {
RequiredDiscriminator::BoundaryValue { description }
| RequiredDiscriminator::ReturnValue { description } => description.as_str(),
RequiredDiscriminator::ErrorVariant { variant } => variant.as_str(),
RequiredDiscriminator::FieldValue { field } => field.as_str(),
RequiredDiscriminator::Effect { sink } => sink.as_str(),
RequiredDiscriminator::MatchArmTaken { arm } => arm.as_str(),
RequiredDiscriminator::CallSite { target } => target.as_str(),
};
extract_identifier_tokens(text)
}
fn assertion_targets_seam(test: &TestSummary, tokens: &[String]) -> bool {
if tokens.is_empty() {
return false;
}
for assertion in &test.assertions {
let assertion_tokens = extract_identifier_tokens(&assertion.text);
if assertion_tokens
.iter()
.any(|at| tokens.iter().any(|t| at == t))
{
return true;
}
}
false
}
fn same_test_file(test_file: &Path, owner_stem: &str) -> bool {
let stem = match test_file.file_stem().and_then(|s| s.to_str()) {
Some(s) => s,
None => return false,
};
if stem == owner_stem {
return true;
}
if let Some(prefix) = stem.strip_suffix("_test")
&& prefix == owner_stem
{
return true;
}
if let Some(prefix) = stem.strip_suffix("_tests")
&& prefix == owner_stem
{
return true;
}
false
}
fn module_path_for(file: &Path) -> Option<String> {
let normalized = normalize_path(file);
let body = normalized
.rfind("/src/")
.map(|idx| &normalized[idx + "/src/".len()..])
.or_else(|| {
normalized
.rfind("/tests/")
.map(|idx| &normalized[idx + "/tests/".len()..])
})
.or_else(|| normalized.strip_prefix("src/"))
.or_else(|| normalized.strip_prefix("tests/"))?;
let trimmed = body.strip_suffix(".rs").unwrap_or(body);
if trimmed.is_empty() {
None
} else {
Some(trimmed.to_string())
}
}
fn same_module(owner_module: &str, test_module: &str) -> bool {
let parent = match owner_module.rsplit_once('/') {
Some((parent, _leaf)) => parent,
None => return false,
};
if parent.is_empty() {
return false;
}
test_module == parent
|| test_module.starts_with(&format!("{parent}/"))
|| test_module.starts_with(&format!("{}/", parent.replace('/', "_")))
}
fn test_imports_owner(test: &TestSummary, owner_name: &str) -> bool {
if owner_name.is_empty() {
return false;
}
let qualified = format!("::{owner_name}");
for raw_line in test.body.lines() {
let code = strip_comments_and_strings(raw_line);
if code.contains(&qualified) {
return true;
}
if code.trim_start().starts_with("use ")
&& extract_identifier_tokens(&code)
.iter()
.any(|t| t == owner_name)
{
return true;
}
}
false
}
fn test_imports_owner_compact(test: &CompactTest<'_>, owner_name: &str) -> bool {
if owner_name.is_empty() {
return false;
}
let qualified = format!("::{owner_name}");
for code in &test.code_lines {
if code.contains(&qualified) {
return true;
}
if code.trim_start().starts_with("use ")
&& extract_identifier_tokens(code)
.iter()
.any(|token| token == owner_name)
{
return true;
}
}
false
}
fn strip_comments_and_strings(line: &str) -> String {
let without_comment = match line.find("//") {
Some(idx) => &line[..idx],
None => line,
};
let mut out = String::with_capacity(without_comment.len());
let mut in_string = false;
let mut escaped = false;
for ch in without_comment.chars() {
if in_string {
if escaped {
escaped = false;
continue;
}
match ch {
'\\' => escaped = true,
'"' => in_string = false,
_ => {}
}
continue;
}
if ch == '"' {
in_string = true;
continue;
}
out.push(ch);
}
out
}
fn test_uses_owner_fixture(
test: &TestSummary,
owner_file: Option<&Path>,
index: &RustIndex,
) -> bool {
let Some(owner_file) = owner_file else {
return false;
};
let Some(owner_facts) = index.files.get(owner_file) else {
return false;
};
for call in &test.calls {
let Some(target) = owner_facts
.functions
.iter()
.find(|f| f.name == call.name && !f.is_test)
else {
continue;
};
if is_fixture_named(&target.name) || target.body.contains("#[fixture]") {
return true;
}
}
false
}
fn is_fixture_named(name: &str) -> bool {
let prefixes = ["fixture_", "setup_", "make_", "build_", "new_", "mock_"];
let suffixes = ["_fixture", "_factory"];
prefixes.iter().any(|p| name.starts_with(p)) || suffixes.iter().any(|s| name.ends_with(s))
}
fn find_owner_function<'a>(seam: &RepoSeam, index: &'a RustIndex) -> Option<&'a FunctionSummary> {
rust_index::find_owner_function(index, seam.file(), seam.display_line())
}
fn normalize_path(path: &Path) -> String {
path.to_string_lossy()
.replace('\\', "/")
.trim_start_matches("./")
.to_string()
}
fn package_prefix(path: &Path) -> Option<String> {
let normalized = normalize_path(path);
if let Some(rest) = normalized.strip_prefix("crates/")
&& let Some((crate_name, crate_relative)) = rest.split_once('/')
&& (crate_relative.starts_with("src/") || crate_relative.starts_with("tests/"))
{
return Some(format!("crates/{crate_name}/"));
}
for marker in ["/src/", "/tests/"] {
if let Some(idx) = normalized.rfind(marker) {
let prefix = &normalized[..idx];
if prefix.is_empty() {
return None;
}
return Some(format!("{prefix}/"));
}
}
None
}
fn reach_evidence(seam: &RepoSeam, related: &[&TestSummary]) -> StageEvidence {
if related.is_empty() {
return StageEvidence::new(
StageState::No,
Confidence::Medium,
format!(
"No static test path found for seam owner `{}`",
seam.owner()
),
);
}
let names: Vec<&str> = related.iter().take(3).map(|t| t.name.as_str()).collect();
StageEvidence::new(
StageState::Yes,
Confidence::Medium,
format!(
"Related tests appear to reach `{}`: {}",
seam.owner(),
names.join(", ")
),
)
}
fn activate_evidence(
seam: &RepoSeam,
related: &[&TestSummary],
owner_fn: Option<&FunctionSummary>,
index: &RustIndex,
) -> (StageEvidence, Vec<ValueFact>, Vec<MissingDiscriminatorFact>) {
let owner_name = owner_fn.map(|f| f.name.as_str()).unwrap_or("");
let mut observed: Vec<ValueFact> = Vec::new();
if !owner_name.is_empty() {
for test in related {
let env = super::value_resolution::ValueEnv::build(seam, test, index);
for call in &test.calls {
if call.name != owner_name {
continue;
}
let Some(args) = call_arguments(&call.text, owner_name) else {
continue;
};
for arg in args {
let mut emitted = false;
for value in scalar_values(&arg) {
observed.push(ValueFact {
line: call.line,
text: call.text.clone(),
value,
context: ValueContext::FunctionArgument,
});
emitted = true;
}
if emitted {
continue;
}
for (value, context) in env.resolve(&arg) {
observed.push(ValueFact {
line: call.line,
text: call.text.clone(),
value,
context,
});
}
}
}
observed.extend(env.builder_facts());
}
}
sort_value_facts(&mut observed);
let missing = missing_discriminators_for(seam, &observed);
let state = if related.is_empty() {
StageState::No
} else if !observed.is_empty() {
StageState::Yes
} else {
StageState::Unknown
};
let stage = StageEvidence::new(
state,
if observed.is_empty() {
Confidence::Low
} else {
Confidence::Medium
},
if observed.is_empty() {
format!(
"No concrete activation values observed for seam `{}`",
seam.expression()
.lines()
.next()
.unwrap_or(seam.expression())
)
} else {
format!(
"Observed {} concrete activation value(s) for seam `{}`",
observed.len(),
seam.expression()
.lines()
.next()
.unwrap_or(seam.expression())
)
},
);
(stage, observed, missing)
}
fn compact_activate_evidence(
seam: &RepoSeam,
related: &[&TestSummary],
owner_fn: Option<&FunctionSummary>,
index: &RustIndex,
) -> (StageEvidence, Vec<MissingDiscriminatorFact>) {
if seam.kind() == SeamKind::PredicateBoundary {
let (stage, _observed, missing) = activate_evidence(seam, related, owner_fn, index);
return (stage, missing);
}
let owner_name = owner_fn.map(|f| f.name.as_str()).unwrap_or("");
let direct_owner_call = !owner_name.is_empty()
&& related
.iter()
.any(|test| test.calls.iter().any(|call| call.name == owner_name));
let state = if related.is_empty() {
StageState::No
} else if direct_owner_call {
StageState::Yes
} else {
StageState::Unknown
};
let stage = StageEvidence::new(
state.clone(),
if direct_owner_call {
Confidence::Medium
} else {
Confidence::Low
},
format!(
"Compact activation evidence for seam `{}` is `{}`",
seam.expression()
.lines()
.next()
.unwrap_or(seam.expression()),
state.as_str()
),
);
(stage, Vec::new())
}
fn missing_discriminators_for(
seam: &RepoSeam,
observed: &[ValueFact],
) -> Vec<MissingDiscriminatorFact> {
match seam.kind() {
SeamKind::PredicateBoundary => {
let expression = seam.expression();
if !boundary_predicate_uses_equal_op(expression) {
return Vec::new();
}
let boundary_token = boundary_rhs_token(expression);
if boundary_token.is_empty() {
return Vec::new();
}
let any_observed = !observed.is_empty();
if !any_observed {
return vec![MissingDiscriminatorFact {
value: format!("{boundary_token} (boundary value)"),
reason: "no observed activation values for boundary predicate".to_string(),
flow_sink: None,
}];
}
let equality_seen = observed
.iter()
.any(|v| v.value.as_str() == boundary_token.as_str());
if equality_seen {
Vec::new()
} else {
vec![MissingDiscriminatorFact {
value: format!("{boundary_token} (equality boundary)"),
reason:
"observed values do not include the equality-boundary case for this predicate"
.to_string(),
flow_sink: None,
}]
}
}
SeamKind::ErrorVariant => Vec::new(),
SeamKind::ReturnValue
| SeamKind::FieldConstruction
| SeamKind::SideEffect
| SeamKind::MatchArm
| SeamKind::CallPresence => Vec::new(),
}
}
fn boundary_predicate_uses_equal_op(expression: &str) -> bool {
expression.contains(" >= ")
|| expression.contains(" <= ")
|| expression.contains(" == ")
|| expression.contains(" != ")
}
fn boundary_rhs_token(expression: &str) -> String {
for op in [" >= ", " <= ", " == ", " != ", " > ", " < "] {
if let Some(idx) = expression.find(op) {
let rhs = expression[idx + op.len()..].trim();
let token: String = rhs
.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect();
if !token.is_empty() {
return token;
}
}
}
String::new()
}
fn propagate_evidence(seam: &RepoSeam, related: &[&TestSummary]) -> StageEvidence {
if related.is_empty() {
return StageEvidence::new(
StageState::No,
Confidence::Medium,
"No related tests; cannot infer propagation",
);
}
let any_oracle = related.iter().any(|t| !t.assertions.is_empty());
let any_matching_sink = related
.iter()
.any(|t| oracles_match_sink(&t.assertions, seam.expected_sink()));
let state = match (any_oracle, any_matching_sink) {
(true, true) => StageState::Yes,
(true, false) => StageState::Unknown,
(false, _) => StageState::Unknown,
};
let summary = format!(
"Static propagation to `{}` sink is {}",
seam.expected_sink().as_str(),
state.as_str()
);
StageEvidence::new(state, Confidence::Low, summary)
}
fn oracles_match_sink(oracles: &[OracleFact], sink: ExpectedSink) -> bool {
oracles.iter().any(|oracle| match sink {
ExpectedSink::ReturnValue | ExpectedSink::OutputField => matches!(
oracle.kind,
OracleKind::ExactValue
| OracleKind::WholeObjectEquality
| OracleKind::Snapshot
| OracleKind::RelationalCheck
),
ExpectedSink::ErrorChannel => matches!(
oracle.kind,
OracleKind::ExactErrorVariant | OracleKind::BroadError
),
ExpectedSink::SideEffect => matches!(oracle.kind, OracleKind::MockExpectation),
})
}
fn observe_evidence(related: &[&TestSummary]) -> StageEvidence {
if related.is_empty() {
return StageEvidence::new(
StageState::No,
Confidence::Medium,
"No related tests; nothing observes the seam",
);
}
let any_oracle = related.iter().any(|t| !t.assertions.is_empty());
let any_smoke_only = related.iter().all(|t| {
!t.assertions.is_empty() && t.assertions.iter().all(|o| o.kind == OracleKind::SmokeOnly)
});
let state = if !any_oracle {
StageState::No
} else if any_smoke_only {
StageState::Weak
} else {
StageState::Yes
};
let summary = format!("Observation evidence is `{}`", state.as_str());
StageEvidence::new(state, Confidence::Medium, summary)
}
fn discriminate_evidence(seam: &RepoSeam, related: &[&TestSummary]) -> StageEvidence {
if related.is_empty() {
return StageEvidence::new(
StageState::No,
Confidence::Medium,
"No related tests; oracle cannot discriminate",
);
}
let mut best = OracleStrength::None;
let mut best_kind_matches_seam = false;
for test in related {
for oracle in &test.assertions {
if oracle.strength.rank() > best.rank() {
best = oracle.strength.clone();
}
if oracle_kind_matches_seam(seam, &oracle.kind) {
best_kind_matches_seam = true;
}
}
}
let state = match (best_kind_matches_seam, &best) {
(_, OracleStrength::None) => StageState::No,
(_, OracleStrength::Unknown) => StageState::Unknown,
(_, OracleStrength::Weak | OracleStrength::Smoke) => StageState::Weak,
(true, OracleStrength::Strong | OracleStrength::Medium) => StageState::Yes,
(false, OracleStrength::Strong | OracleStrength::Medium) => StageState::Weak,
};
let summary = format!(
"Strongest oracle for seam kind `{}` is `{}` (kind-match {})",
seam.kind().as_str(),
best.as_str(),
best_kind_matches_seam
);
StageEvidence::new(state, Confidence::Medium, summary)
}
fn oracle_kind_matches_seam(seam: &RepoSeam, oracle: &OracleKind) -> bool {
match seam.kind() {
SeamKind::PredicateBoundary
| SeamKind::ReturnValue
| SeamKind::MatchArm
| SeamKind::FieldConstruction => matches!(
oracle,
OracleKind::ExactValue
| OracleKind::WholeObjectEquality
| OracleKind::Snapshot
| OracleKind::RelationalCheck
),
SeamKind::ErrorVariant => matches!(oracle, OracleKind::ExactErrorVariant),
SeamKind::SideEffect | SeamKind::CallPresence => {
matches!(oracle, OracleKind::MockExpectation)
}
}
}
fn related_test_grip(
seam: &RepoSeam,
test: &TestSummary,
reason: RelationReason,
) -> RelatedTestGrip {
let (kind, strength) = best_oracle(test, seam);
let summary = if matches!(strength, OracleStrength::None) {
"no oracle in test body".to_string()
} else {
match kind {
OracleKind::ExactValue => "exact value assertion".to_string(),
OracleKind::ExactErrorVariant => "exact error-variant assertion".to_string(),
OracleKind::WholeObjectEquality => "whole-object equality".to_string(),
OracleKind::Snapshot => "snapshot oracle".to_string(),
OracleKind::RelationalCheck => "relational check".to_string(),
OracleKind::BroadError => "is_err / broad-error assertion".to_string(),
OracleKind::SmokeOnly => "smoke-only assertion".to_string(),
OracleKind::MockExpectation => "mock expectation".to_string(),
OracleKind::Unknown => "no recognised oracle".to_string(),
}
};
let confidence = reason.confidence();
RelatedTestGrip {
test_name: test.name.clone(),
file: test.file.clone(),
line: test.start_line,
oracle_kind: kind,
oracle_strength: strength,
evidence_summary: summary,
relation_reason: reason,
relation_confidence: confidence,
}
}
fn best_oracle(test: &TestSummary, seam: &RepoSeam) -> (OracleKind, OracleStrength) {
let mut best_kind = OracleKind::Unknown;
let mut best_strength = OracleStrength::None;
for oracle in &test.assertions {
if oracle.strength.rank() > best_strength.rank() {
best_strength = oracle.strength.clone();
best_kind = oracle.kind.clone();
} else if oracle.strength.rank() == best_strength.rank()
&& oracle_kind_matches_seam(seam, &oracle.kind)
{
best_kind = oracle.kind.clone();
}
}
(best_kind, best_strength)
}
fn call_arguments(text: &str, callee: &str) -> Option<Vec<String>> {
let needle = format!("{callee}(");
let start = text.find(&needle)?;
let after = &text[start + needle.len()..];
let close = after.rfind(')')?;
let inside = &after[..close];
Some(split_top_level_commas(inside))
}
fn split_top_level_commas(input: &str) -> Vec<String> {
let mut out = Vec::new();
let mut depth = 0i32;
let mut current = String::new();
for ch in input.chars() {
match ch {
'(' | '[' | '{' => {
depth += 1;
current.push(ch);
}
')' | ']' | '}' => {
depth -= 1;
current.push(ch);
}
',' if depth == 0 => {
out.push(current.trim().to_string());
current.clear();
}
_ => current.push(ch),
}
}
let trailing = current.trim().to_string();
if !trailing.is_empty() {
out.push(trailing);
}
out
}
fn scalar_values(arg: &str) -> Vec<String> {
let trimmed = arg.trim().trim_end_matches([',', ';']);
if trimmed.is_empty() {
return Vec::new();
}
if trimmed.starts_with('"') || trimmed.starts_with('\'') {
return vec![trimmed.to_string()];
}
let numeric_body = trimmed.strip_prefix('-').unwrap_or(trimmed);
if !numeric_body.is_empty()
&& numeric_body
.chars()
.next()
.is_some_and(|c| c.is_ascii_digit())
&& numeric_body
.chars()
.all(|c| c.is_ascii_digit() || c == '_' || c == '.')
{
return vec![trimmed.to_string()];
}
if trimmed.contains("::")
&& trimmed
.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == ':')
{
return vec![trimmed.to_string()];
}
Vec::new()
}
fn sort_value_facts(values: &mut Vec<ValueFact>) {
values.sort_by(|a, b| {
a.line
.cmp(&b.line)
.then(a.value.cmp(&b.value))
.then(a.text.cmp(&b.text))
});
values.dedup_by(|a, b| a.line == b.line && a.value == b.value && a.text == b.text);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::analysis::rust_index::{RaRustSyntaxAdapter, RustSyntaxAdapter};
use crate::analysis::seam_inventory::inventory_seams_from_index;
fn index_from_files(files: &[(PathBuf, &str)]) -> Result<RustIndex, String> {
let adapter = RaRustSyntaxAdapter;
let mut index = RustIndex::default();
for (path, source) in files {
let facts = adapter.summarize_file(path, source)?;
index.tests.extend(facts.tests.iter().cloned());
index.functions.extend(facts.functions.iter().cloned());
index.files.insert(path.clone(), facts);
}
Ok(index)
}
#[test]
fn given_boundary_seam_when_tests_skip_equal_value_then_evidence_reports_missing_boundary_discriminator()
-> Result<(), String> {
let prod = PathBuf::from("src/pricing.rs");
let prod_src = r#"
pub fn discounted_total(amount: i32, threshold: i32) -> i32 {
if amount >= threshold { amount - 10 } else { amount }
}
"#;
let tests = PathBuf::from("tests/pricing_tests.rs");
let tests_src = r#"
#[test]
fn below_threshold_has_no_discount() {
assert_eq!(discounted_total(50, 100), 50);
}
#[test]
fn far_above_threshold_discounts() {
assert_eq!(discounted_total(10000, 100), 9990);
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "expected predicate seam".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
if evidence.related_tests.is_empty() {
return Err("expected reach evidence to find related tests".to_string());
}
if evidence.missing_discriminators.is_empty() {
return Err(format!(
"expected at least one missing-discriminator hypothesis for boundary seam `{}`",
predicate.expression()
));
}
let mentions_threshold = evidence
.missing_discriminators
.iter()
.any(|fact| fact.value.contains("threshold"));
if !mentions_threshold {
return Err(format!(
"missing-discriminator hypothesis should name the boundary identifier; got {:?}",
evidence
.missing_discriminators
.iter()
.map(|f| f.value.clone())
.collect::<Vec<_>>()
));
}
Ok(())
}
#[test]
fn given_boundary_seam_when_test_uses_equal_value_and_exact_assertion_then_discriminate_evidence_is_yes()
-> Result<(), String> {
let prod = PathBuf::from("src/pricing.rs");
let prod_src = r#"
pub fn discounted_total(amount: i32, threshold: i32) -> i32 {
if amount >= threshold { amount - 10 } else { amount }
}
"#;
let tests = PathBuf::from("tests/pricing_tests.rs");
let tests_src = r#"
#[test]
fn equality_boundary_returns_discount() {
assert_eq!(discounted_total(100, 100), 90);
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "expected predicate seam".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
if evidence.discriminate.state != StageState::Yes {
return Err(format!(
"expected discriminate=Yes, got {} ({})",
evidence.discriminate.state.as_str(),
evidence.discriminate.summary
));
}
Ok(())
}
#[test]
fn given_error_variant_seam_when_test_only_asserts_is_err_then_discriminate_evidence_is_weak()
-> Result<(), String> {
let prod = PathBuf::from("src/parse.rs");
let prod_src = r#"
pub enum AuthError { RevokedToken, Expired }
pub fn parse(value: &str) -> Result<i32, AuthError> {
if value.is_empty() {
return Err(AuthError::RevokedToken);
}
Ok(0)
}
"#;
let tests = PathBuf::from("tests/parse_tests.rs");
let tests_src = r#"
#[test]
fn parse_rejects_empty() {
assert!(parse("").is_err());
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/parse.rs")], &index);
let error_seam = seams
.iter()
.find(|s| s.kind() == SeamKind::ErrorVariant)
.ok_or_else(|| "expected error_variant seam".to_string())?;
let evidence = evidence_for_seam(error_seam, &index);
if evidence.discriminate.state != StageState::Weak
&& evidence.discriminate.state != StageState::Unknown
{
return Err(format!(
"expected discriminate=Weak|Unknown for is_err-only oracle, got {}",
evidence.discriminate.state.as_str()
));
}
Ok(())
}
#[test]
fn given_error_variant_seam_when_test_asserts_exact_variant_then_discriminate_evidence_is_yes()
-> Result<(), String> {
let prod = PathBuf::from("src/parse.rs");
let prod_src = r#"
pub enum AuthError { RevokedToken, Expired }
pub fn parse(value: &str) -> Result<i32, AuthError> {
if value.is_empty() {
return Err(AuthError::RevokedToken);
}
Ok(0)
}
"#;
let tests = PathBuf::from("tests/parse_tests.rs");
let tests_src = r#"
#[test]
fn parse_returns_revoked_token_on_empty() {
assert!(matches!(parse(""), Err(AuthError::RevokedToken)));
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/parse.rs")], &index);
let error_seam = seams
.iter()
.find(|s| s.kind() == SeamKind::ErrorVariant)
.ok_or_else(|| "expected error_variant seam".to_string())?;
let evidence = evidence_for_seam(error_seam, &index);
if evidence.discriminate.state != StageState::Yes {
return Err(format!(
"expected discriminate=Yes for matches!(...AuthError::RevokedToken), got {} ({})",
evidence.discriminate.state.as_str(),
evidence.discriminate.summary
));
}
Ok(())
}
#[test]
fn given_side_effect_seam_when_no_effect_observer_exists_then_observe_evidence_is_weak_or_unknown()
-> Result<(), String> {
let prod = PathBuf::from("src/publish.rs");
let prod_src = r#"
pub struct Service;
pub struct Event;
impl Service {
pub fn publish(&mut self, _event: Event) {}
}
pub fn publish_message(service: &mut Service, event: Event) {
service.publish(event);
}
"#;
let tests = PathBuf::from("tests/publish_tests.rs");
let tests_src = r#"
#[test]
fn publish_runs_without_panic() {
let mut service = Service;
publish_message(&mut service, Event);
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/publish.rs")], &index);
let side_effect = seams
.iter()
.find(|s| s.kind() == SeamKind::SideEffect)
.ok_or_else(|| {
format!(
"expected side_effect seam, got kinds: {:?}",
seams.iter().map(|s| s.kind().as_str()).collect::<Vec<_>>()
)
})?;
let evidence = evidence_for_seam(side_effect, &index);
match evidence.observe.state {
StageState::No | StageState::Weak | StageState::Unknown => Ok(()),
other => Err(format!(
"expected observe in {{No, Weak, Unknown}} for side-effect with no observer, got {}",
other.as_str()
)),
}
}
#[test]
fn given_side_effect_seam_when_event_assertion_exists_then_oracle_observes_effect()
-> Result<(), String> {
let prod = PathBuf::from("src/publish.rs");
let prod_src = r#"
pub struct Service;
pub struct Event;
impl Service {
pub fn publish(&mut self, _event: Event) {}
}
pub fn publish_message(service: &mut Service, event: Event) {
service.publish(event);
}
"#;
let tests = PathBuf::from("tests/publish_tests.rs");
let tests_src = r#"
#[test]
fn publish_records_event() {
let mut service = Service;
publish_message(&mut service, Event);
assert!(service.published_events().contains(&"message"));
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/publish.rs")], &index);
let side_effect = seams
.iter()
.find(|s| s.kind() == SeamKind::SideEffect)
.ok_or_else(|| "expected side_effect seam".to_string())?;
let evidence = evidence_for_seam(side_effect, &index);
assert_eq!(evidence.observe.state, StageState::Yes);
assert_eq!(evidence.propagate.state, StageState::Yes);
assert_eq!(evidence.discriminate.state, StageState::Yes);
assert!(
evidence
.related_tests
.iter()
.any(|test| test.oracle_kind == OracleKind::MockExpectation)
);
Ok(())
}
#[test]
fn given_opaque_helper_when_values_cannot_be_seen_then_evidence_records_static_limitation()
-> Result<(), String> {
let prod = PathBuf::from("src/pricing.rs");
let prod_src = r#"
pub fn discounted_total(amount: i32, threshold: i32) -> i32 {
if amount >= threshold { amount - 10 } else { amount }
}
"#;
let tests = PathBuf::from("tests/pricing_tests.rs");
let tests_src = r#"
fn make_input() -> (i32, i32) { (50, 100) }
#[test]
fn helper_path_runs() {
let (a, t) = make_input();
let _ = discounted_total(a, t);
assert!(true);
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "expected predicate seam".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
if evidence.activate.state == StageState::Yes {
return Err(format!(
"expected activate != Yes for helper-supplied values, got {} ({})",
evidence.activate.state.as_str(),
evidence.activate.summary
));
}
Ok(())
}
#[test]
fn evidence_for_seams_is_deterministic_across_input_order() -> Result<(), String> {
let prod = PathBuf::from("src/pricing.rs");
let prod_src = r#"
pub fn discounted_total(amount: i32, threshold: i32) -> i32 {
if amount >= threshold { amount - 10 } else { amount }
}
"#;
let tests = PathBuf::from("tests/pricing_tests.rs");
let tests_src = r#"
#[test]
fn boundary_case() {
assert_eq!(discounted_total(100, 100), 90);
}
#[test]
fn below_case() {
assert_eq!(discounted_total(50, 100), 50);
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let mut seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let forward_ids: Vec<String> = evidence_for_seams(&seams, &index)
.iter()
.map(|e| e.seam_id.as_str().to_string())
.collect();
seams.reverse();
let reversed_ids: Vec<String> = evidence_for_seams(&seams, &index)
.iter()
.map(|e| e.seam_id.as_str().to_string())
.collect();
if forward_ids != reversed_ids {
return Err(format!(
"evidence order is not stable:\n forward: {forward_ids:?}\n reversed: {reversed_ids:?}"
));
}
Ok(())
}
#[test]
fn given_compact_evidence_when_direct_owner_call_reaches_error_seam_then_activation_is_yes()
-> Result<(), String> {
let prod = PathBuf::from("src/parse.rs");
let prod_src = r#"
pub enum AuthError { RevokedToken, Expired }
pub fn parse(value: &str) -> Result<i32, AuthError> {
if value.is_empty() {
return Err(AuthError::RevokedToken);
}
Ok(0)
}
"#;
let tests = PathBuf::from("tests/parse_tests.rs");
let tests_src = r#"
#[test]
fn parse_rejects_empty() {
assert!(parse("").is_err());
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/parse.rs")], &index);
let error_seam = seams
.iter()
.find(|s| s.kind() == SeamKind::ErrorVariant)
.ok_or_else(|| "expected error_variant seam".to_string())?;
let context = CompactGripContext::new(&index);
let evidence = compact_evidence_for_seam(error_seam, &context);
assert_eq!(evidence.reach.state, StageState::Yes);
assert_eq!(evidence.activate.state, StageState::Yes);
assert_eq!(evidence.related_tests.len(), 0);
assert_eq!(evidence.observed_values.len(), 0);
assert_eq!(evidence.missing_discriminators.len(), 0);
Ok(())
}
#[test]
fn given_compact_evidence_when_import_affinity_has_no_owner_call_then_activation_is_unknown()
-> Result<(), String> {
let prod = PathBuf::from("src/parse.rs");
let prod_src = r#"
pub enum AuthError { RevokedToken, Expired }
pub fn parse(value: &str) -> Result<i32, AuthError> {
if value.is_empty() {
return Err(AuthError::RevokedToken);
}
Ok(0)
}
"#;
let tests = PathBuf::from("tests/wrapper_tests.rs");
let tests_src = r#"
fn helper() -> Result<i32, AuthError> { Err(AuthError::RevokedToken) }
#[test]
fn wrapper_rejects_empty() {
use crate::parse;
assert!(helper().is_err());
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/parse.rs")], &index);
let error_seam = seams
.iter()
.find(|s| s.kind() == SeamKind::ErrorVariant)
.ok_or_else(|| "expected error_variant seam".to_string())?;
let context = CompactGripContext::new(&index);
let related = find_related_tests_compact(error_seam, &context);
assert_eq!(related.len(), 1);
assert_eq!(related[0].name, "wrapper_rejects_empty");
let evidence = compact_evidence_for_seam(error_seam, &context);
assert_eq!(evidence.reach.state, StageState::Yes);
assert_eq!(evidence.activate.state, StageState::Unknown);
Ok(())
}
#[test]
fn given_compact_related_tests_when_more_than_limit_match_then_results_are_capped()
-> Result<(), String> {
let prod = PathBuf::from("src/pricing.rs");
let prod_src = r#"
pub fn discounted_total(amount: i32, threshold: i32) -> i32 {
if amount >= threshold { amount - 10 } else { amount }
}
"#;
let mut tests_src = String::new();
for idx in 0..14 {
tests_src.push_str(&format!(
"#[test]\nfn direct_{idx:02}() {{ assert_eq!(discounted_total(100, 100), 90); }}\n"
));
}
let tests = PathBuf::from("tests/pricing_tests.rs");
let index = index_from_files(&[(prod, prod_src), (tests, tests_src.as_str())])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "expected predicate seam".to_string())?;
let context = CompactGripContext::new(&index);
let related = find_related_tests_compact(predicate, &context);
assert_eq!(related.len(), COMPACT_RELATED_TEST_LIMIT);
assert_eq!(related[0].name, "direct_00");
assert_eq!(related[COMPACT_RELATED_TEST_LIMIT - 1].name, "direct_11");
Ok(())
}
#[test]
fn given_compact_import_affinity_when_owner_only_in_comment_or_string_then_no_relation_is_found()
-> Result<(), String> {
let prod = PathBuf::from("src/parse.rs");
let prod_src = r#"
pub enum AuthError { RevokedToken, Expired }
pub fn parse(value: &str) -> Result<i32, AuthError> {
if value.is_empty() {
return Err(AuthError::RevokedToken);
}
Ok(0)
}
"#;
let tests = PathBuf::from("tests/noise_tests.rs");
let tests_src = r#"
#[test]
fn wrapper_mentions_owner_only_in_non_code() {
// use crate::parse;
let _path = "crate::parse";
assert!(helper().is_err());
}
"#;
let index = index_from_files(&[(prod, prod_src), (tests, tests_src)])?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/parse.rs")], &index);
let error_seam = seams
.iter()
.find(|s| s.kind() == SeamKind::ErrorVariant)
.ok_or_else(|| "expected error_variant seam".to_string())?;
let context = CompactGripContext::new(&index);
let related = find_related_tests_compact(error_seam, &context);
assert_eq!(related.len(), 0);
Ok(())
}
fn first_grip_for(
seam_file: &str,
prod_src: &str,
tests: &[(&str, &str)],
) -> Result<RelatedTestGrip, String> {
let mut files: Vec<(PathBuf, &str)> = vec![(PathBuf::from(seam_file), prod_src)];
for (path, src) in tests {
files.push((PathBuf::from(*path), *src));
}
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from(seam_file)], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
evidence
.related_tests
.into_iter()
.next()
.ok_or_else(|| "at least one related test".to_string())
}
#[test]
fn given_direct_owner_call_and_same_file_match_when_related_tests_are_ranked_then_direct_call_is_first()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let same_file_only = (
"tests/pricing_tests.rs",
"#[test] fn pricing_smoke() { assert_eq!(1, 1); }\n",
);
let direct = (
"tests/unrelated.rs",
"#[test] fn calls_owner() { assert_eq!(discounted_total(100, 100), 90); }\n",
);
let files: Vec<(PathBuf, &str)> = vec![
(PathBuf::from("src/pricing.rs"), prod_src),
(PathBuf::from(same_file_only.0), same_file_only.1),
(PathBuf::from(direct.0), direct.1),
];
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
let first = evidence
.related_tests
.first()
.ok_or_else(|| "at least one related test".to_string())?;
let labels: Vec<_> = evidence
.related_tests
.iter()
.map(|g| (g.test_name.clone(), g.relation_reason))
.collect();
assert_eq!(
first.relation_reason,
RelationReason::DirectOwnerCall,
"direct owner call must outrank same-file affinity; got grips {labels:?}"
);
assert_eq!(first.relation_confidence, RelationConfidence::High);
Ok(())
}
#[test]
fn given_owner_named_test_without_call_when_related_tests_are_ranked_then_confidence_is_medium()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/billing.rs",
"#[test] fn discounted_total_smoke() { assert_eq!(1, 1); }\n",
);
let grip = first_grip_for("src/pricing.rs", prod_src, &[test])?;
assert_eq!(grip.relation_reason, RelationReason::OwnerNamedTest);
assert_eq!(grip.relation_confidence, RelationConfidence::Medium);
Ok(())
}
#[test]
fn given_fixture_only_affinity_when_related_tests_are_ranked_then_confidence_is_low()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n\
pub fn make_quote() -> i32 { 100 }\n";
let test = (
"tests/integration.rs",
"#[test] fn quote_smoke() { let _ = make_quote(); assert!(true); }\n",
);
let grip = first_grip_for("src/pricing.rs", prod_src, &[test])?;
assert_eq!(grip.relation_reason, RelationReason::FixtureOwnerAffinity);
assert_eq!(grip.relation_confidence, RelationConfidence::Low);
Ok(())
}
#[test]
fn given_assertion_target_affinity_uses_token_aware_match_not_substring() -> Result<(), String>
{
let prod_src = "pub fn discounted_total(amount: i32, discount_threshold: i32) -> i32 \
{ if amount >= discount_threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/billing.rs",
"fn other() -> i32 { 0 }\n\
#[test] fn smoke() { let discount_threshold_factor = 5; assert_eq!(other(), 0); let _ = discount_threshold_factor; }\n",
);
let files: Vec<(PathBuf, &str)> = vec![
(PathBuf::from("src/pricing.rs"), prod_src),
(PathBuf::from(test.0), test.1),
];
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
for grip in &evidence.related_tests {
assert_ne!(
grip.relation_reason,
RelationReason::AssertionTargetAffinity,
"substring hit (`discount_threshold_factor`) must not match \
assertion_target_affinity; got {grip:?}"
);
}
Ok(())
}
#[test]
fn given_related_tests_with_same_confidence_when_sorted_then_order_is_stable_by_file_name_line()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test_a = (
"tests/zeta.rs",
"#[test] fn discounted_total_one() { assert_eq!(1, 1); }\n",
);
let test_b = (
"tests/alpha.rs",
"#[test] fn discounted_total_two() { assert_eq!(1, 1); }\n",
);
let files: Vec<(PathBuf, &str)> = vec![
(PathBuf::from("src/pricing.rs"), prod_src),
(PathBuf::from(test_a.0), test_a.1),
(PathBuf::from(test_b.0), test_b.1),
];
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
assert!(
evidence.related_tests.len() >= 2,
"expected at least 2 related tests, got {}",
evidence.related_tests.len()
);
assert_eq!(evidence.related_tests[0].file, Path::new("tests/alpha.rs"));
assert_eq!(evidence.related_tests[1].file, Path::new("tests/zeta.rs"));
Ok(())
}
#[test]
fn given_higher_confidence_related_test_when_sorted_then_it_comes_before_lower_confidence()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n\
pub fn make_quote() -> i32 { 100 }\n";
let fixture_user = (
"tests/a_first.rs",
"#[test] fn fx() { let _ = make_quote(); assert!(true); }\n",
);
let direct_caller = (
"tests/z_last.rs",
"#[test] fn caller() { assert_eq!(discounted_total(100, 100), 90); }\n",
);
let files: Vec<(PathBuf, &str)> = vec![
(PathBuf::from("src/pricing.rs"), prod_src),
(PathBuf::from(fixture_user.0), fixture_user.1),
(PathBuf::from(direct_caller.0), direct_caller.1),
];
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
let first = evidence
.related_tests
.first()
.ok_or_else(|| "at least one related test".to_string())?;
assert_eq!(first.relation_reason, RelationReason::DirectOwnerCall);
assert_eq!(first.relation_confidence, RelationConfidence::High);
Ok(())
}
#[test]
fn given_import_path_affinity_without_direct_call_when_related_tests_are_ranked_then_confidence_is_medium()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/integration_smoke.rs",
"#[test] fn smoke() { let _f = crate::pricing::discounted_total; assert_eq!(1, 1); }\n",
);
let grip = first_grip_for("src/pricing.rs", prod_src, &[test])?;
assert_eq!(grip.relation_reason, RelationReason::ImportPathAffinity);
assert_eq!(grip.relation_confidence, RelationConfidence::Medium);
Ok(())
}
#[test]
fn given_qualified_owner_path_only_in_comment_or_string_when_related_tests_are_ranked_then_import_path_affinity_does_not_fire()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let comment_only = (
"tests/integration_a.rs",
"#[test] fn smoke_a() { \
// see crate::pricing::discounted_total for background \n\
assert_eq!(1, 1); \
}\n",
);
let string_only = (
"tests/integration_b.rs",
"#[test] fn smoke_b() { \
let _doc = \"crate::pricing::discounted_total\"; \
let _ = _doc; assert_eq!(1, 1); \
}\n",
);
for (path, src) in [comment_only, string_only] {
let files: Vec<(PathBuf, &str)> = vec![
(PathBuf::from("src/pricing.rs"), prod_src),
(PathBuf::from(path), src),
];
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
for grip in &evidence.related_tests {
assert_ne!(
grip.relation_reason,
RelationReason::ImportPathAffinity,
"qualified path inside comment/string in {path} must not match \
ImportPathAffinity; got {grip:?}"
);
}
}
Ok(())
}
#[test]
fn given_owner_and_module_tokens_without_import_path_when_related_tests_are_ranked_then_import_path_affinity_does_not_fire()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/billing.rs",
"#[test] fn discounted_total_token_smoke() { \
let pricing = \"pricing\"; let discounted_total = 5; \
let _ = (pricing, discounted_total); assert_eq!(1, 1); \
}\n",
);
let files: Vec<(PathBuf, &str)> = vec![
(PathBuf::from("src/pricing.rs"), prod_src),
(PathBuf::from(test.0), test.1),
];
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
for grip in &evidence.related_tests {
assert_ne!(
grip.relation_reason,
RelationReason::ImportPathAffinity,
"token co-occurrence (`pricing` + `discounted_total` in body without \
`::` path syntax) must not match ImportPathAffinity; got {grip:?}"
);
}
Ok(())
}
#[test]
fn given_same_module_test_without_direct_call_when_related_tests_are_ranked_then_confidence_is_medium()
-> Result<(), String> {
let prod_src = "pub fn apply_discount(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing/integration.rs",
"#[test] fn module_neighbour() { assert_eq!(1, 1); }\n",
);
let files: Vec<(PathBuf, &str)> = vec![
(PathBuf::from("src/pricing/discount.rs"), prod_src),
(PathBuf::from(test.0), test.1),
];
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing/discount.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
let grip = evidence.related_tests.first().ok_or_else(|| {
"expected at least one related test for same-module pairing".to_string()
})?;
assert_eq!(grip.relation_reason, RelationReason::SameModule);
assert_eq!(grip.relation_confidence, RelationConfidence::Medium);
Ok(())
}
#[test]
fn relation_reason_as_str_priority_and_confidence_are_pinned_per_variant() {
let table = [
(
RelationReason::DirectOwnerCall,
"direct_owner_call",
0u8,
RelationConfidence::High,
),
(
RelationReason::AssertionTargetAffinity,
"assertion_target_affinity",
1,
RelationConfidence::High,
),
(
RelationReason::SameTestFile,
"same_test_file",
2,
RelationConfidence::Medium,
),
(
RelationReason::SameModule,
"same_module",
3,
RelationConfidence::Medium,
),
(
RelationReason::OwnerNamedTest,
"owner_named_test",
4,
RelationConfidence::Medium,
),
(
RelationReason::ImportPathAffinity,
"import_path_affinity",
5,
RelationConfidence::Medium,
),
(
RelationReason::FixtureOwnerAffinity,
"fixture_owner_affinity",
6,
RelationConfidence::Low,
),
];
for (reason, name, prio, conf) in table {
assert_eq!(reason.as_str(), name, "{reason:?}.as_str()");
assert_eq!(reason.priority(), prio, "{reason:?}.priority()");
assert_eq!(reason.confidence(), conf, "{reason:?}.confidence()");
}
}
#[test]
fn relation_confidence_as_str_and_rank_are_pinned_per_variant() {
let table = [
(RelationConfidence::High, "high", 0u8),
(RelationConfidence::Medium, "medium", 1),
(RelationConfidence::Low, "low", 2),
(RelationConfidence::Opaque, "opaque", 3),
];
for (conf, name, rank) in table {
assert_eq!(conf.as_str(), name, "{conf:?}.as_str()");
assert_eq!(conf.rank(), rank, "{conf:?}.rank()");
}
}
#[test]
fn required_discriminator_tokens_extracts_text_from_every_variant() {
use crate::analysis::seams::{ExpectedSink, RepoSeam, RequiredDiscriminator};
let make = |rd: RequiredDiscriminator| {
RepoSeam::new(
"src/x.rs",
"x::owner",
SeamKind::PredicateBoundary,
0,
1,
"irrelevant",
rd,
ExpectedSink::ReturnValue,
)
};
let cases: Vec<(RequiredDiscriminator, &str)> = vec![
(
RequiredDiscriminator::BoundaryValue {
description: "boundary_token".to_string(),
},
"boundary_token",
),
(
RequiredDiscriminator::ReturnValue {
description: "returnval_token".to_string(),
},
"returnval_token",
),
(
RequiredDiscriminator::ErrorVariant {
variant: "errvar_token".to_string(),
},
"errvar_token",
),
(
RequiredDiscriminator::FieldValue {
field: "fieldval_token".to_string(),
},
"fieldval_token",
),
(
RequiredDiscriminator::Effect {
sink: "effect_token".to_string(),
},
"effect_token",
),
(
RequiredDiscriminator::MatchArmTaken {
arm: "matcharm_token".to_string(),
},
"matcharm_token",
),
(
RequiredDiscriminator::CallSite {
target: "callsite_token".to_string(),
},
"callsite_token",
),
];
for (rd, expected_token) in cases {
let seam = make(rd.clone());
let tokens = required_discriminator_tokens(&seam);
assert!(
tokens.iter().any(|t| t == expected_token),
"{rd:?} -> tokens {tokens:?} must contain {expected_token}"
);
}
}
#[test]
fn same_test_file_accepts_stem_match_and_test_suffixes() {
assert!(same_test_file(Path::new("tests/foo.rs"), "foo"));
assert!(same_test_file(Path::new("tests/foo_test.rs"), "foo"));
assert!(same_test_file(Path::new("tests/foo_tests.rs"), "foo"));
assert!(!same_test_file(Path::new("tests/bar.rs"), "foo"));
assert!(!same_test_file(Path::new(""), "foo"));
}
#[test]
fn module_path_for_handles_every_root_shape() {
let cases: Vec<(&str, Option<&str>)> = vec![
("src/foo.rs", Some("foo")),
("tests/cli_smoke.rs", Some("cli_smoke")),
("crates/ripr/src/auth/login.rs", Some("auth/login")),
("crates/ripr/tests/integration.rs", Some("integration")),
("docs/note.rs", None),
("src/.rs", None),
];
for (input, expected) in cases {
let got = module_path_for(Path::new(input));
let want = expected.map(str::to_string);
assert_eq!(got, want, "module_path_for({input})");
}
}
#[test]
fn same_module_matches_parent_prefix_and_underscore_form() {
assert!(same_module("pricing/discount", "pricing/integration"));
assert!(same_module("a/b/c", "a_b/d"));
assert!(!same_module("flat", "anything"));
assert!(!same_module("pricing/discount", "billing/integration"));
}
#[test]
fn is_fixture_named_recognises_each_prefix_and_suffix() {
let positives = [
"fixture_quote",
"setup_db",
"make_quote",
"build_request",
"new_user",
"mock_clock",
"quote_fixture",
"quote_factory",
];
for name in positives {
assert!(is_fixture_named(name), "{name} should be fixture-named");
}
for name in ["compute_total", "discount", "verify"] {
assert!(
!is_fixture_named(name),
"{name} should NOT be fixture-named"
);
}
}
#[test]
fn given_assertion_target_token_in_test_assertion_when_related_tests_are_ranked_then_assertion_target_affinity_fires()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, discount_threshold: i32) -> i32 \
{ if amount >= discount_threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/billing.rs",
"fn other() -> i32 { 0 }\n\
#[test] fn smoke() { let discount_threshold = 5; assert_eq!(discount_threshold, 5); }\n",
);
let grip = first_grip_for("src/pricing.rs", prod_src, &[test])?;
assert_eq!(
grip.relation_reason,
RelationReason::AssertionTargetAffinity
);
assert_eq!(grip.relation_confidence, RelationConfidence::High);
Ok(())
}
#[test]
fn assertion_targets_seam_returns_false_for_empty_token_list() {
use crate::analysis::rust_index::TestFact;
let test = TestFact {
name: "synth".to_string(),
file: PathBuf::from("tests/x.rs"),
start_line: 1,
end_line: 5,
body: "assert_eq!(1, 1);".to_string(),
calls: Vec::new(),
assertions: Vec::new(),
literals: Vec::new(),
attrs: Vec::new(),
};
assert!(!assertion_targets_seam(&test, &[]));
}
#[test]
fn package_prefix_resolves_crates_and_nested_src_tests_layouts() {
assert_eq!(
package_prefix(Path::new("crates/ripr/src/auth/login.rs")).as_deref(),
Some("crates/ripr/")
);
assert_eq!(
package_prefix(Path::new("crates/ripr/tests/integration.rs")).as_deref(),
Some("crates/ripr/")
);
assert_eq!(
package_prefix(Path::new("workspaces/foo/src/auth/login.rs")).as_deref(),
Some("workspaces/foo/")
);
assert_eq!(package_prefix(Path::new("src/foo.rs")), None);
assert_eq!(package_prefix(Path::new("docs/note.rs")), None);
}
#[test]
fn given_owner_in_workspace_crate_when_test_is_in_other_crate_then_it_is_filtered_out()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let other_pkg_test = (
"crates/ripr_other/tests/x.rs",
"#[test] fn discounted_total_other_pkg() { assert_eq!(1, 1); }\n",
);
let files: Vec<(PathBuf, &str)> = vec![
(
PathBuf::from("crates/ripr_pricing/src/discount.rs"),
prod_src,
),
(PathBuf::from(other_pkg_test.0), other_pkg_test.1),
];
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(
&[PathBuf::from("crates/ripr_pricing/src/discount.rs")],
&index,
);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
for grip in &evidence.related_tests {
assert_ne!(
grip.file,
Path::new("crates/ripr_other/tests/x.rs"),
"test in unrelated package should be filtered by package_prefix; \
got {grip:?}"
);
}
Ok(())
}
#[test]
fn given_test_calls_helper_with_fixture_attribute_then_fixture_owner_affinity_fires()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n\
pub fn provide_quote() -> i32 {\n // #[fixture]\n 100\n}\n";
let test = (
"tests/integration.rs",
"#[test] fn quote_smoke() { let _ = provide_quote(); assert!(true); }\n",
);
let grip = first_grip_for("src/pricing.rs", prod_src, &[test])?;
assert_eq!(grip.relation_reason, RelationReason::FixtureOwnerAffinity);
Ok(())
}
fn observed_values_for(prod_src: &str, tests: &[(&str, &str)]) -> Result<Vec<String>, String> {
let mut files: Vec<(PathBuf, &str)> = vec![(PathBuf::from("src/pricing.rs"), prod_src)];
for (path, src) in tests {
files.push((PathBuf::from(*path), *src));
}
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
Ok(evidence
.observed_values
.into_iter()
.map(|v| v.value)
.collect())
}
#[test]
fn given_let_binding_values_when_owner_call_uses_identifiers_then_observed_values_are_resolved()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn at_threshold() { let amount = 100; let threshold = 100; \
assert_eq!(discounted_total(amount, threshold), 90); }\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
values.iter().any(|v| v == "100"),
"let-resolved 100 must appear in observed values; got {values:?}"
);
Ok(())
}
#[test]
fn given_same_file_const_when_owner_call_uses_identifier_then_observed_value_is_resolved()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"const THRESHOLD: i32 = 100;\n\
#[test] fn at_threshold() { \
assert_eq!(discounted_total(THRESHOLD, THRESHOLD), 90); \
}\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
values.iter().any(|v| v == "100"),
"const-resolved 100 must appear; got {values:?}"
);
Ok(())
}
#[test]
fn given_table_driven_cases_when_owner_call_uses_row_values_then_each_case_value_is_recorded()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn table() { \
for (amount, threshold, expected) in [(50, 100, 50), (100, 100, 90)] { \
assert_eq!(discounted_total(amount, threshold), expected); \
} \
}\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
values.iter().any(|v| v == "50"),
"table row value 50 must appear; got {values:?}"
);
assert!(
values.iter().any(|v| v == "100"),
"table row value 100 must appear; got {values:?}"
);
Ok(())
}
#[test]
fn given_option_result_constructor_when_owner_call_uses_shape_then_inner_value_is_recorded()
-> Result<(), String> {
let prod_src = "pub fn process(value: Option<i32>, threshold: i32) -> i32 \
{ match value { Some(v) if v >= threshold => v - 10, _ => 0 } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn at_boundary() { \
assert_eq!(process(Some(100), 100), 90); \
}\n",
);
let mut files: Vec<(PathBuf, &str)> = vec![(PathBuf::from("src/pricing.rs"), prod_src)];
files.push((PathBuf::from(test.0), test.1));
let index = index_from_files(&files)?;
let seams = inventory_seams_from_index(&[PathBuf::from("src/pricing.rs")], &index);
let predicate = seams
.iter()
.find(|s| s.kind() == SeamKind::PredicateBoundary)
.ok_or_else(|| "predicate seam present".to_string())?;
let evidence = evidence_for_seam(predicate, &index);
let values: Vec<String> = evidence
.observed_values
.iter()
.map(|v| v.value.clone())
.collect();
assert!(
values.iter().any(|v| v == "100"),
"Some(100) must unwrap and contribute 100; got {values:?}"
);
Ok(())
}
#[test]
fn given_builder_methods_matching_parameter_tokens_then_observed_values_are_recorded()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, discount_threshold: i32) -> i32 \
{ if amount >= discount_threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn via_builder() { \
let q = Quote::new().amount(100).discount_threshold(100).build(); \
assert_eq!(discounted_total(q.amount, q.discount_threshold), 90); \
}\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
values.iter().filter(|v| v.as_str() == "100").count() >= 1,
"builder method 100 must be recorded; got {values:?}"
);
Ok(())
}
#[test]
fn given_fixture_factory_override_methods_matching_seam_tokens_then_values_are_recorded()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn via_fixture_override() { \
let q = QuoteFixture::default().with_amount(100).with_threshold(100).build(); \
assert_eq!(discounted_total(q.amount, q.threshold), 90); \
}\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
values.iter().filter(|v| v.as_str() == "100").count() >= 1,
"fixture override 100 must be recorded; got {values:?}"
);
Ok(())
}
#[test]
fn given_builder_method_with_unrelated_name_then_value_is_not_counted_for_seam_activation()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn via_unrelated_builder() { \
let _q = Foo::new().with_seed(42).build(); \
assert_eq!(discounted_total(50, 100), 50); \
}\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
!values.iter().any(|v| v == "42"),
"unrelated builder literal 42 must NOT count; got {values:?}"
);
Ok(())
}
#[test]
fn given_unrelated_string_literal_mentions_value_when_extracting_values_then_no_observed_discriminator_is_recorded()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn string_only() { \
let _doc = \"threshold = 100\"; \
let unresolved = make_amount(); \
assert_eq!(discounted_total(unresolved, unresolved), 0); \
}\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
!values.iter().any(|v| v == "100"),
"string literal 100 must NOT be observed; got {values:?}"
);
Ok(())
}
#[test]
fn given_shared_fixture_module_constant_when_extracting_v2_values_then_no_cross_file_value_is_resolved()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let common = (
"tests/common/mod.rs",
"pub const SHARED_THRESHOLD: i32 = 100;\n",
);
let test = (
"tests/pricing_tests.rs",
"#[test] fn cross_file() { \
assert_eq!(discounted_total(SHARED_THRESHOLD, SHARED_THRESHOLD), 90); \
}\n",
);
let values = observed_values_for(prod_src, &[test, common])?;
assert!(
!values.iter().any(|v| v == "100"),
"cross-file SHARED_THRESHOLD = 100 must NOT resolve in v2; got {values:?}"
);
Ok(())
}
#[test]
fn given_let_binding_shadowed_by_comment_when_extracting_then_real_binding_wins()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn at_threshold() { \
// let amount = 999; let threshold = 999;\n\
let amount = 100; let threshold = 100; \
assert_eq!(discounted_total(amount, threshold), 90); \
}\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
values.iter().any(|v| v == "100"),
"real let binding 100 must be observed; got {values:?}"
);
assert!(
!values.iter().any(|v| v == "999"),
"commented-out let binding 999 must NOT be observed; got {values:?}"
);
Ok(())
}
#[test]
fn given_unresolved_identifier_arg_when_extracting_values_then_no_observed_value_is_recorded()
-> Result<(), String> {
let prod_src = "pub fn discounted_total(amount: i32, threshold: i32) -> i32 \
{ if amount >= threshold { amount - 10 } else { amount } }\n";
let test = (
"tests/pricing_tests.rs",
"#[test] fn opaque() { \
let amount = make_amount(); \
let threshold = make_threshold(); \
assert_eq!(discounted_total(amount, threshold), 0); \
}\n",
);
let values = observed_values_for(prod_src, &[test])?;
assert!(
values.is_empty()
|| values
.iter()
.all(|v| !matches!(v.as_str(), "100" | "0" | "make_amount")),
"opaque args must not produce a fake observed value; got {values:?}"
);
Ok(())
}
}