Give the read side a mutant per rule
`tools/eureka-cut9-mutations.psd1`, V1-V23: every rule the views decide, with the edit that removes it and the test that must fail while it is applied. A rule with two weakenings gets two entries, `Vn` for the rule absent and `VnL` for the rule weakened rather than removed, so a suite that only catches the crude form does not pass for the subtle one. 34 entries; M0 green and every one killed. V1 is the proof that in force has one owner: its mutant lands in `docs.rs`, and V1L is H41's own edit run against the views instead of the rules. One mutant, two suites. Four tests grew to put a mutant in reach of a real failure, which is the point of writing the entries before trusting the suite: - Two receipts naming one write, forged and planted, so the integrity check that refuses them is not dead text. - A cut spec whose withdrawal is itself withdrawn, so `base` has two steps to walk and stopping at one is visible. - A `cut-1` spec beside `cut-9` and `cut-10`, so the prefix that ends at the dot is not the same answer as the prefix that does not. - A second report of one cut, judged by nothing, so "this cut has a verdict" and "this report has one" come apart. Three weakenings the map names have no entry and say why in the file's header: the `semantic` check moved below the filters still refuses; `resolutions_of` ignoring a subject's kind cannot matter while an id carries its own kind segment; `assignments_of` ignoring the instance cannot matter while A5 refuses a stewardship naming another. A mutation that cannot fail is not a proof, and listing it would only make the suite look thorough. Nine cut-8 entries moved with their code in the first commit; that suite is green as well, H1-H67, M0 included. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
49cc6e3e62
commit
8fc39b127e
2 changed files with 491 additions and 6 deletions
|
|
@ -425,6 +425,7 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
use crate::fixtures::*;
|
||||
use crate::mind::schema_cache;
|
||||
use crate::store::test_stores::MemoryStore;
|
||||
use chrono::{TimeZone, Utc};
|
||||
use epiphany_pipeline::{
|
||||
|
|
@ -635,6 +636,29 @@ mod tests {
|
|||
Some(MindRefusal::Unavailable { detail: detail.clone() })
|
||||
);
|
||||
assert_eq!(orphaned.query(&PipelineQuery::default()).err(), Some(MindRefusal::Unavailable { detail }));
|
||||
|
||||
// The other half of the same rule: A10 admits one write per identity,
|
||||
// so two receipts naming one document is a store this organ cannot
|
||||
// read, not a choice of which receipt to believe.
|
||||
let doubled = MemoryStore::new();
|
||||
for row in store.rows() {
|
||||
doubled.plant(row);
|
||||
}
|
||||
let forged = crate::receipt::candidate(
|
||||
&slug(INSTANCE),
|
||||
provenance(Faculty::Soul),
|
||||
&[],
|
||||
&[prepare(&question("Q1", &["A", "B"], "A")), prepare(&question("Q8", &["A", "B"], "A"))],
|
||||
now(),
|
||||
)
|
||||
.unwrap();
|
||||
doubled.plant(schema_cache().unwrap().prepare_entry_named(&forged.receipt_id, &forged).unwrap().0);
|
||||
assert_eq!(
|
||||
opened(doubled, INSTANCE).query(&PipelineQuery::default()).err(),
|
||||
Some(MindRefusal::Unavailable {
|
||||
detail: format!("document {}/{q1} is written by two receipts", K::Question.type_id()),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/// R-C: `stored_at` is the store's stamp and no derivation reads it.
|
||||
|
|
@ -680,6 +704,7 @@ mod tests {
|
|||
committed(admit(&mut mind, vec![
|
||||
question("Q1", &["A", "B"], "A"),
|
||||
D::Ruling(ruling("R1")),
|
||||
D::CutSpec(cut_spec("1", 1)),
|
||||
D::CutSpec(cut_spec("9", 1)),
|
||||
D::CutSpec(cut_spec("10", 1)),
|
||||
follow_up("FU-1", r(K::Question, &id("question", "Q1"))),
|
||||
|
|
@ -693,6 +718,14 @@ mod tests {
|
|||
D::CutSpec(cut_spec("9", 2)),
|
||||
resolution(r(K::CutSpec, &id("cut_spec", "cut-9.r1")), superseded(&[r(K::CutSpec, &id("cut_spec", "cut-9.r2"))])),
|
||||
]));
|
||||
// A resolution of a resolution, so a filter reading through the base
|
||||
// has two steps to walk: the cut-10 spec is withdrawn and its
|
||||
// withdrawal is itself withdrawn, which puts the spec back in force.
|
||||
committed(admit(&mut mind, vec![resolution(r(K::CutSpec, &id("cut_spec", "cut-10.r1")), withdrawn())]));
|
||||
committed(admit(&mut mind, vec![resolution(
|
||||
r(K::Resolution, &id("resolution", "cut_spec.cut-10.r1.n1")),
|
||||
withdrawn(),
|
||||
)]));
|
||||
committed(admit_as(&mut mind, Faculty::Soul, vec![D::Ruling(ruling("R2"))]));
|
||||
// One batch at a later clock, so the window filters have a boundary.
|
||||
let later = Utc.with_ymd_and_hms(2026, 9, 17, 12, 0, 0).unwrap();
|
||||
|
|
@ -745,7 +778,12 @@ mod tests {
|
|||
assert_eq!(matching(PipelineQuery { cut: Some(l("10")), ..Default::default() }), vec![
|
||||
id("cut_report", "cut-10.h1"),
|
||||
id("cut_spec", "cut-10.r1"),
|
||||
id("resolution", "cut_spec.cut-10.r1.n1"),
|
||||
id("resolution", "resolution.cut_spec.cut-10.r1.n1.n1"),
|
||||
]);
|
||||
// The prefix ends at the dot, so cut-1 is not every cut that starts
|
||||
// with its label.
|
||||
assert_eq!(matching(PipelineQuery { cut: Some(l("1")), ..Default::default() }), vec![id("cut_spec", "cut-1.r1")]);
|
||||
|
||||
// `kinds`, empty for every kind.
|
||||
assert_eq!(matching(PipelineQuery { kinds: vec![K::Question], ..Default::default() }), vec![
|
||||
|
|
@ -756,7 +794,10 @@ mod tests {
|
|||
|
||||
// `in_force`, both ways: the superseded revision is the one thing this
|
||||
// mind has closed.
|
||||
assert_eq!(matching(PipelineQuery { in_force: Some(false), ..Default::default() }), vec![id("cut_spec", "cut-9.r1")]);
|
||||
assert_eq!(matching(PipelineQuery { in_force: Some(false), ..Default::default() }), vec![
|
||||
id("cut_spec", "cut-9.r1"),
|
||||
id("resolution", "cut_spec.cut-10.r1.n1"),
|
||||
]);
|
||||
let standing = matching(PipelineQuery { in_force: Some(true), ..Default::default() });
|
||||
assert!(!standing.contains(&id("cut_spec", "cut-9.r1")));
|
||||
assert!(standing.contains(&id("cut_spec", "cut-9.r2")));
|
||||
|
|
@ -807,7 +848,7 @@ mod tests {
|
|||
let questions = PipelineQuery { kinds: vec![K::Question], ..Default::default() };
|
||||
let page = mind.query(&PipelineQuery { limit: Some(500), ..questions.clone() }).unwrap();
|
||||
assert_eq!(page.matched, 201);
|
||||
assert_eq!(page.items.len(), QUERY_LIMIT_MAX);
|
||||
assert_eq!(page.items.len(), 200, "the cap, spelled out rather than read from the constant under test");
|
||||
assert_eq!(page.items[0].admission.admitted_at, "2026-09-17T12:00:00Z", "the earliest batch first");
|
||||
assert_eq!(page.items[0].id.id.0, id("question", "Q10"), "and inside it, key order");
|
||||
assert_ne!(page.items[0].id.id.0, id("question", "Q1"), "Q1 sorts first by id and is admitted last");
|
||||
|
|
@ -824,7 +865,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(page.matched as usize - page.items.len(), 1);
|
||||
|
||||
for (limit, expected) in [(None, QUERY_LIMIT_MAX), (Some(500), QUERY_LIMIT_MAX), (Some(0), 1), (Some(5), 5)] {
|
||||
for (limit, expected) in [(None, 200), (Some(500), 200), (Some(0), 1), (Some(5), 5)] {
|
||||
let page = mind.query(&PipelineQuery { limit, ..questions.clone() }).unwrap();
|
||||
assert_eq!(page.items.len(), expected, "limit {limit:?}");
|
||||
assert_eq!(page.matched, 201, "the count is taken before the cap");
|
||||
|
|
@ -853,7 +894,9 @@ mod tests {
|
|||
// cut-11: a spec with a report, that report with a verdict, and two
|
||||
// findings, one of which is then fixed.
|
||||
committed(admit(&mut mind, vec![D::CutSpec(cut_spec("11", 1))]));
|
||||
committed(admit(&mut mind, vec![D::CutReport(cut_report("11", 1))]));
|
||||
// Two reports of one cut, one of them judged, so "this cut has a
|
||||
// verdict" is not the same answer as "this report has one".
|
||||
committed(admit(&mut mind, vec![D::CutReport(cut_report("11", 1)), D::CutReport(cut_report("11", 2))]));
|
||||
let f1 = id("finding", "cut-11.s1.F1");
|
||||
let f2 = id("finding", "cut-11.s1.F2");
|
||||
committed(admit(&mut mind, vec![
|
||||
|
|
@ -878,8 +921,8 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
ids(&open.reports_without_verdict),
|
||||
vec![id("cut_report", "cut-10.h1")],
|
||||
"a report is not resolvable, so only the verdict that names it closes it"
|
||||
vec![id("cut_report", "cut-10.h1"), id("cut_report", "cut-11.h2")],
|
||||
"a report is not resolvable, so only the verdict that names it, exactly, closes it"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
442
tools/eureka-cut9-mutations.psd1
Normal file
442
tools/eureka-cut9-mutations.psd1
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
# Cut 9 mutations, V1-V23: the read side's rules, each entry restoring one
|
||||
# permissiveness the cut closed and naming the test that must fail while it is
|
||||
# applied. A rule with two weakenings has two entries: `Vn` is the revert, the
|
||||
# rule simply absent, and `VnL` is the loosening, the rule weakened rather than
|
||||
# removed. Run through Epiphany's harness from this repo:
|
||||
#
|
||||
# $env:CARGO_TARGET_DIR = 'C:\Users\Meta\.cargo-target-codex'
|
||||
# powershell -File F:\Projects\Epiphany\tools\eureka-mutations.ps1 -Repo F:\Projects\Huginn `
|
||||
# -Entries tools/eureka-cut9-mutations.psd1 `
|
||||
# -Target crates/huginn-mind/src/docs.rs,crates/huginn-mind/src/query.rs `
|
||||
# -Test 'cargo test -p huginn-mind --lib'
|
||||
#
|
||||
# `docs.rs` is a target for V1 alone, which is the point of V1: in force has
|
||||
# one owner, and the mutant that breaks it for the rules is the mutant that
|
||||
# breaks it for the views. V1L is H41's own edit, listed here as well as in
|
||||
# `eureka-cut8-mutations.psd1` -- one mutant, two suites, one owner.
|
||||
#
|
||||
# Three weakenings the spec names have no entry because they change no
|
||||
# behaviour this Body can reach, and a mutation that cannot fail is not a
|
||||
# proof:
|
||||
#
|
||||
# - The `semantic` check moved below the filters. It still refuses every query
|
||||
# that carries one; only the work done before the refusal changes.
|
||||
# - `resolutions_of` comparing a subject by `id` and ignoring `kind`. A
|
||||
# pipeline id carries its own kind segment and the leaf refuses a
|
||||
# `PipelineRef` whose kind and id disagree, so no two subjects of different
|
||||
# kinds can share an id.
|
||||
# - `assignments_of` comparing the repo and ignoring the instance. A5 refuses
|
||||
# a stewardship naming another instance, so every stewardship a mind holds
|
||||
# already names that mind.
|
||||
@{
|
||||
Mutations = @(
|
||||
@{
|
||||
Id = 'V1'
|
||||
Rule = 'One owner for in force: the views derive status through the same closing_resolution the rules ask.'
|
||||
Test = 'query::tests::status_is_the_derivation_admission_uses'
|
||||
File = 'crates/huginn-mind/src/docs.rs'
|
||||
Old = @'
|
||||
self.resolutions().find(|&(key, resolution)| {
|
||||
resolution.subject.kind == kind
|
||||
&& resolution.subject.id.0 == id
|
||||
&& !own(resolution)
|
||||
&& self.in_force(PipelineKind::Resolution, key)
|
||||
})
|
||||
'@
|
||||
New = @'
|
||||
let _ = (kind, id, own);
|
||||
None
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V1L'
|
||||
Rule = 'In force is recursive for a reader as it is for a rule: a withdrawn resolution stops closing its subject. H41''s edit, run against the views.'
|
||||
Test = 'query::tests::status_is_the_derivation_admission_uses'
|
||||
File = 'crates/huginn-mind/src/docs.rs'
|
||||
Old = @'
|
||||
&& !own(resolution)
|
||||
&& self.in_force(PipelineKind::Resolution, key)
|
||||
'@
|
||||
New = @'
|
||||
&& !own(resolution)
|
||||
&& { let _ = key; true }
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V2'
|
||||
Rule = 'admitted_at is the receipt''s committed_at, never the store''s stored_at stamp (R-C).'
|
||||
Test = 'query::tests::views_read_admitted_at_from_the_receipt_not_stored_at'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' admitted_at: receipt.committed_at.clone(),'
|
||||
New = ' admitted_at: mind.raw_envelope(&write.document_type, &write.document_key).map_or_else(|| receipt.committed_at.clone(), |envelope| envelope.stored_at.clone()),'
|
||||
}
|
||||
@{
|
||||
Id = 'V3'
|
||||
Rule = 'The facts come from a receipt''s writes only, so a document a later batch cited keeps the receipt that wrote it.'
|
||||
Test = 'query::tests::views_join_admission_facts_from_the_receipt_that_wrote_them'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' for write in receipt.writes.iter() {'
|
||||
New = ' for write in receipt.writes.iter().chain(&receipt.strong_reads) {'
|
||||
}
|
||||
@{
|
||||
Id = 'V4'
|
||||
Rule = 'A row no receipt wrote refuses, from view and query alike, and is never skipped.'
|
||||
Test = 'query::tests::views_join_admission_facts_from_the_receipt_that_wrote_them'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' let view = reader.view_of(held)?;'
|
||||
New = ' let Ok(view) = reader.view_of(held) else { continue };'
|
||||
}
|
||||
@{
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' reader.held(id.kind, &id.id.0).map(|held| reader.view_of(held)).transpose()'
|
||||
New = ' Ok(reader.held(id.kind, &id.id.0).and_then(|held| reader.view_of(held).ok()))'
|
||||
}
|
||||
)
|
||||
}
|
||||
@{
|
||||
Id = 'V4L'
|
||||
Rule = 'Two receipts naming one write is an integrity fault, not a choice of which to believe.'
|
||||
Test = 'query::tests::views_join_admission_facts_from_the_receipt_that_wrote_them'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
if facts.insert(identity, landed).is_some() {
|
||||
return Err(integrity(&write.document_type, &write.document_key, "is written by two receipts"));
|
||||
}
|
||||
'@
|
||||
New = @'
|
||||
facts.insert(identity, landed);
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V5'
|
||||
Rule = 'The cap: a caller''s limit is clamped into 1..=QUERY_LIMIT_MAX.'
|
||||
Test = 'query::tests::query_orders_by_admitted_at_then_id_and_caps_at_200_with_the_match_count'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = '.clamp(1, QUERY_LIMIT_MAX));'
|
||||
New = '.max(1));'
|
||||
}
|
||||
@{
|
||||
Id = 'V5L'
|
||||
Rule = 'The cap is 200, and the test spells it out rather than reading the constant under test.'
|
||||
Test = 'query::tests::query_orders_by_admitted_at_then_id_and_caps_at_200_with_the_match_count'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = 'pub const QUERY_LIMIT_MAX: usize = 200;'
|
||||
New = 'pub const QUERY_LIMIT_MAX: usize = 201;'
|
||||
}
|
||||
@{
|
||||
Id = 'V6'
|
||||
Rule = 'The order is (admitted_at, id): the batch decides, and the key only breaks a tie inside one.'
|
||||
Test = 'query::tests::query_orders_by_admitted_at_then_id_and_caps_at_200_with_the_match_count'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
(&left.admission.admitted_at, &left.id.id.0).cmp(&(&right.admission.admitted_at, &right.id.id.0))
|
||||
'@
|
||||
New = @'
|
||||
left.id.id.0.cmp(&right.id.id.0)
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V6L'
|
||||
Rule = 'A tie inside one batch falls to the key, which is root-first and so is not the image''s (type, key) order.'
|
||||
Test = 'query::tests::query_orders_by_admitted_at_then_id_and_caps_at_200_with_the_match_count'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
(&left.admission.admitted_at, &left.id.id.0).cmp(&(&right.admission.admitted_at, &right.id.id.0))
|
||||
'@
|
||||
New = @'
|
||||
left.admission.admitted_at.cmp(&right.admission.admitted_at)
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V7'
|
||||
Rule = 'matched is the count before the cap, so a truncated page is visible as one.'
|
||||
Test = 'query::tests::query_orders_by_admitted_at_then_id_and_caps_at_200_with_the_match_count'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
let matched = items.len() as u32;
|
||||
ordered(&mut items);
|
||||
let limit = query.limit.map_or(QUERY_LIMIT_MAX, |limit| (limit as usize).clamp(1, QUERY_LIMIT_MAX));
|
||||
items.truncate(limit);
|
||||
'@
|
||||
New = @'
|
||||
ordered(&mut items);
|
||||
let limit = query.limit.map_or(QUERY_LIMIT_MAX, |limit| (limit as usize).clamp(1, QUERY_LIMIT_MAX));
|
||||
items.truncate(limit);
|
||||
let matched = items.len() as u32;
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V8'
|
||||
Rule = 'semantic is refused typed until Cut 11, never answered with an empty page.'
|
||||
Test = 'query::tests::semantic_query_refuses_typed_until_wired'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
return Err(MindRefusal::Unavailable {
|
||||
detail: "semantic query: the index is not wired (Cut 11)".into(),
|
||||
});
|
||||
'@
|
||||
New = @'
|
||||
return Ok(PipelineQueryPage { items: vec![], matched: 0 });
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V9'
|
||||
Rule = 'History is in the scope''s own sequence order, not the image''s key order.'
|
||||
Test = 'query::tests::a_subjects_history_lists_every_resolution_with_its_status_and_receipt'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' records.sort_by_key(|(sequence, _)| *sequence);'
|
||||
New = ' let _ = &records;'
|
||||
}
|
||||
@{
|
||||
Id = 'V10'
|
||||
Rule = 'A subject''s history is selected by the subject field, so a resolution''s own withdrawals are not in it.'
|
||||
Test = 'query::tests::a_subjects_history_lists_every_resolution_with_its_status_and_receipt'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
.resolutions_of(subject)
|
||||
.map(|(key, resolution)| (resolution.sequence, key.to_string()))
|
||||
'@
|
||||
New = @'
|
||||
.resolutions()
|
||||
.map(|(key, resolution)| { let _ = subject; (resolution.sequence, key.to_string()) })
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V11'
|
||||
Rule = 'A repo''s history is selected by (this mind, this repo), so another repo''s assignments are another history.'
|
||||
Test = 'query::tests::a_repos_stewardship_history_on_a_mind_lists_every_assignment'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
.assignments_of(reader.mind.instance(), repo)
|
||||
.map(|(key, stewardship)| (stewardship.sequence, key.to_string()))
|
||||
'@
|
||||
New = @'
|
||||
.stewardships()
|
||||
.map(|(key, stewardship)| { let _ = repo; (stewardship.sequence, key.to_string()) })
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V12'
|
||||
Rule = 'Open questions, findings and follow-ups are the ones in force.'
|
||||
Test = 'query::tests::open_items_are_derived_from_in_force_and_citation'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' of_kind(kind).filter(|view| view.status == PipelineStatus::InForce).cloned().collect::<Vec<_>>()'
|
||||
New = ' of_kind(kind).cloned().collect::<Vec<_>>()'
|
||||
}
|
||||
@{
|
||||
Id = 'V13'
|
||||
Rule = 'A spec without a report is open only while it is in force: a superseded revision is not open work.'
|
||||
Test = 'query::tests::open_items_are_derived_from_in_force_and_citation'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' specs_without_report: in_force(PipelineKind::CutSpec)'
|
||||
New = ' specs_without_report: of_kind(PipelineKind::CutSpec).cloned().collect::<Vec<_>>()'
|
||||
}
|
||||
@{
|
||||
Id = 'V14'
|
||||
Rule = 'A report is closed by the verdict that names it; a report is not resolvable, so nothing else closes it.'
|
||||
Test = 'query::tests::open_items_are_derived_from_in_force_and_citation'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' .filter(|view| !judged.contains(&view.id.id.0.as_str()))'
|
||||
New = ' .filter(|view| { let _ = &judged; true })'
|
||||
}
|
||||
@{
|
||||
Id = 'V14L'
|
||||
Rule = 'The citation is compared as the exact id, not as the cut it belongs to: one cut may have two reports and one verdict.'
|
||||
Test = 'query::tests::open_items_are_derived_from_in_force_and_citation'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' .filter(|view| !judged.contains(&view.id.id.0.as_str()))'
|
||||
New = ' .filter(|view| !judged.iter().any(|cited| root_and_local(cited).1.split(''.'').next() == root_and_local(&view.id.id.0).1.split(''.'').next()))'
|
||||
}
|
||||
@{
|
||||
Id = 'V15'
|
||||
Rule = 'Open items are one campaign''s, selected by the key''s root.'
|
||||
Test = 'query::tests::open_items_are_derived_from_in_force_and_citation'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' .filter(|view| root_and_local(&view.id.id.0).0 == campaign.0)'
|
||||
New = ' .filter(|view| { let _ = &campaign; true })'
|
||||
}
|
||||
@{
|
||||
Id = 'V16'
|
||||
Rule = 'The cut filter is the label, not every cut.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' && !root_and_local(&base.key).1.starts_with(&format!("cut-{}.", cut.0))'
|
||||
New = ' && !root_and_local(&base.key).1.starts_with("cut-")'
|
||||
}
|
||||
@{
|
||||
Id = 'V16L'
|
||||
Rule = 'The cut prefix ends at the dot, so cut-1 does not match cut-10.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' && !root_and_local(&base.key).1.starts_with(&format!("cut-{}.", cut.0))'
|
||||
New = ' && !root_and_local(&base.key).1.starts_with(&format!("cut-{}", cut.0))'
|
||||
}
|
||||
@{
|
||||
Id = 'V17'
|
||||
Rule = 'A filter reaches a resolution through its base, the document it is a record of.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
let mut current = held;
|
||||
while let PipelineDocument::Resolution(resolution) = ¤t.document {
|
||||
let Some(subject) = self.held(resolution.subject.kind, &resolution.subject.id.0) else {
|
||||
return current;
|
||||
};
|
||||
current = subject;
|
||||
}
|
||||
current
|
||||
'@
|
||||
New = @'
|
||||
held
|
||||
'@
|
||||
}
|
||||
@{
|
||||
Id = 'V17L'
|
||||
Rule = 'The base is walked to the first non-resolution, not one step: a withdrawal of a resolution is two steps from its document.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' while let PipelineDocument::Resolution(resolution) = ¤t.document {'
|
||||
New = ' if let PipelineDocument::Resolution(resolution) = ¤t.document {'
|
||||
}
|
||||
@{
|
||||
Id = 'V18'
|
||||
Rule = 'campaign is the key''s root segment.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
if let Some(campaign) = &query.campaign
|
||||
&& root_and_local(&held.key).0 != campaign.0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
}
|
||||
@{
|
||||
Id = 'V18L'
|
||||
Rule = 'The root, not a campaign field: a stewardship under an instance root and a resolution under its subject''s root carry no campaign of their own.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' && root_and_local(&held.key).0 != campaign.0'
|
||||
New = ' && !matches!(&held.document, PipelineDocument::Campaign(value) if value.slug == *campaign)'
|
||||
}
|
||||
@{
|
||||
Id = 'V19'
|
||||
Rule = 'kinds selects by the document''s kind, and an empty list matches every kind.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
if !query.kinds.is_empty() && !query.kinds.contains(&held.kind) {
|
||||
return false;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
}
|
||||
@{
|
||||
Id = 'V20'
|
||||
Rule = 'in_force selects by the derived status.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
if let Some(in_force) = query.in_force
|
||||
&& in_force != (view.status == PipelineStatus::InForce)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
}
|
||||
@{
|
||||
Id = 'V20L'
|
||||
Rule = 'in_force selects the status the caller asked for, not its opposite.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' && in_force != (view.status == PipelineStatus::InForce)'
|
||||
New = ' && in_force == (view.status == PipelineStatus::InForce)'
|
||||
}
|
||||
@{
|
||||
Id = 'V21'
|
||||
Rule = 'faculty selects by the receipt''s provenance: attribution filters, and grants nothing (ruling 18).'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
if let Some(faculty) = query.faculty
|
||||
&& view.admission.provenance.faculty != faculty
|
||||
{
|
||||
return false;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
}
|
||||
@{
|
||||
Id = 'V22'
|
||||
Rule = 'The admission window selects by admitted_at.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
if let Some(after) = &query.admitted_after
|
||||
&& view.admission.admitted_at <= after.0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
}
|
||||
@{
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
if let Some(before) = &query.admitted_before
|
||||
&& view.admission.admitted_at >= before.0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
}
|
||||
)
|
||||
}
|
||||
@{
|
||||
Id = 'V22L'
|
||||
Rule = 'Both ends of the window are exclusive at the exact boundary.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
Edits = @(
|
||||
@{
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' && view.admission.admitted_at <= after.0'
|
||||
New = ' && view.admission.admitted_at < after.0'
|
||||
}
|
||||
@{
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' && view.admission.admitted_at >= before.0'
|
||||
New = ' && view.admission.admitted_at > before.0'
|
||||
}
|
||||
)
|
||||
}
|
||||
@{
|
||||
Id = 'V23'
|
||||
Rule = 'repo selects by the repo the document names, read through the base.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = @'
|
||||
if let Some(repo) = &query.repo
|
||||
&& !repo_matches(&base.document, repo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
'@
|
||||
New = ''
|
||||
}
|
||||
@{
|
||||
Id = 'V23L'
|
||||
Rule = 'A campaign matches through the repos it lists, as the five single-repo kinds match through their field.'
|
||||
Test = 'query::tests::query_filters_each_select_by_one_field'
|
||||
File = 'crates/huginn-mind/src/query.rs'
|
||||
Old = ' D::Campaign(campaign) => campaign.repos.contains(repo),'
|
||||
New = ' D::Campaign(_) => false,'
|
||||
}
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue