#![allow(dead_code)]
use beads_rust::model::{Issue, Priority};
use std::fmt::Debug;
pub fn assert_ordered_by<T, K>(items: &[T], key_fn: impl Fn(&T) -> K, name: &str)
where
K: Ord + Debug,
T: Debug,
{
if items.len() < 2 {
return;
}
for window in items.windows(2) {
let a_key = key_fn(&window[0]);
let b_key = key_fn(&window[1]);
assert!(
a_key <= b_key,
"{name}: ordering invariant violated\n item[i] = {:?} (key={:?})\n item[i+1] = {:?} (key={:?})\n",
window[0],
a_key,
window[1],
b_key
);
}
}
pub fn assert_priority_ordered(issues: &[Issue]) {
assert_ordered_by(
issues,
|i| i.priority,
"assert_priority_ordered (P0 < P1 < P2 < P3 < P4)",
);
}
pub fn assert_oldest_first(issues: &[Issue]) {
assert_ordered_by(
issues,
|i| i.created_at,
"assert_oldest_first (oldest created_at first)",
);
}
pub fn assert_hybrid_ordered(issues: &[Issue]) {
let mut high_tier_ended = false;
for (idx, issue) in issues.iter().enumerate() {
let is_high = issue.priority <= Priority::HIGH;
if !is_high {
high_tier_ended = true;
} else {
assert!(
!high_tier_ended,
"assert_hybrid_ordered: P{} issue at idx={idx} appears AFTER a low-tier issue (high tier must come first)\n full order: {:?}",
issue.priority.0,
issues
.iter()
.map(|i| (&i.id, i.priority))
.collect::<Vec<_>>()
);
}
}
}
pub fn assert_contains_exactly_one(issues: &[Issue], pred: impl Fn(&Issue) -> bool, name: &str) {
let count = issues.iter().filter(|i| pred(i)).count();
assert_eq!(
count,
1,
"{name}: expected exactly 1 matching issue; got {count}\n full list: {:?}",
issues.iter().map(|i| (&i.id, &i.title)).collect::<Vec<_>>()
);
}
pub fn assert_no_duplicate_ids(issues: &[Issue]) {
let mut seen = std::collections::HashSet::new();
for issue in issues {
assert!(
seen.insert(&issue.id),
"assert_no_duplicate_ids: duplicate id {:?} in result; full list: {:?}",
issue.id,
issues.iter().map(|i| &i.id).collect::<Vec<_>>()
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use beads_rust::model::{IssueType, Status};
use chrono::{Duration, TimeZone, Utc};
fn make_issue(id: &str, priority: Priority, age_offset_secs: i64) -> Issue {
let base = Utc.timestamp_opt(1_735_689_600, 0).unwrap();
Issue {
id: id.to_string(),
title: format!("Test {id}"),
status: Status::Open,
priority,
issue_type: IssueType::Task,
created_at: base + Duration::seconds(age_offset_secs),
updated_at: base + Duration::seconds(age_offset_secs + 1),
content_hash: None,
description: None,
design: None,
acceptance_criteria: None,
notes: None,
assignee: None,
owner: None,
estimated_minutes: None,
created_by: None,
closed_at: None,
close_reason: None,
closed_by_session: None,
due_at: None,
defer_until: None,
external_ref: None,
source_system: None,
source_repo: None,
source_repo_path: None,
agent_context: None,
deleted_at: None,
deleted_by: None,
delete_reason: None,
original_type: None,
compaction_level: None,
compacted_at: None,
compacted_at_commit: None,
original_size: None,
sender: None,
ephemeral: false,
pinned: false,
is_template: false,
labels: vec![],
dependencies: vec![],
comments: vec![],
}
}
#[test]
fn assert_ordered_by_passes_on_sorted_input() {
let nums = [1, 2, 3, 5, 8];
assert_ordered_by(&nums, |n| *n, "fibonacci-ish");
}
#[test]
#[should_panic(expected = "ordering invariant violated")]
fn assert_ordered_by_panics_with_descriptive_message_on_unsorted_input() {
let nums = [1, 5, 3];
assert_ordered_by(&nums, |n| *n, "test_name_in_msg");
}
#[test]
fn assert_priority_ordered_treats_critical_as_lowest_value() {
let issues = [
make_issue("a", Priority::CRITICAL, 0),
make_issue("b", Priority::HIGH, 1),
make_issue("c", Priority::MEDIUM, 2),
make_issue("d", Priority::LOW, 3),
];
assert_priority_ordered(&issues);
}
#[test]
#[should_panic(expected = "ordering invariant violated")]
fn assert_priority_ordered_panics_when_higher_priority_comes_after_lower() {
let issues = [
make_issue("a", Priority::MEDIUM, 0),
make_issue("b", Priority::CRITICAL, 1), ];
assert_priority_ordered(&issues);
}
#[test]
fn assert_no_duplicate_ids_passes_on_unique_set() {
let issues = [
make_issue("a", Priority::MEDIUM, 0),
make_issue("b", Priority::MEDIUM, 0),
make_issue("c", Priority::MEDIUM, 0),
];
assert_no_duplicate_ids(&issues);
}
}