fix(timeline): report true total_events in robot JSON meta
The robot JSON envelope's meta.total_events field was incorrectly reporting events.len() (the post-limit count), making it identical to meta.showing. This defeated the purpose of having both fields. Changes across the pipeline to fix this: - collect_events now returns (Vec<TimelineEvent>, usize) where the second element is the total event count before truncation - TimelineResult gains a total_events_before_limit field (serde-skipped) so the value flows cleanly from collect through to the renderer - main.rs passes the real total instead of the events.len() workaround Additional cleanup in this pass: - Derive PartialEq/Eq/PartialOrd/Ord on TimelineEventType, replacing the hand-rolled event_type_discriminant() function. Variant declaration order now defines sort tiebreak, documented in a doc comment. - Validate --since input with a proper LoreError::Other instead of silently treating invalid values as None - Fix ANSI-aware tag column padding with console::pad_str (colored tags like "[merged]" were misaligned because ANSI escapes consumed width) - Remove dead print_timeline_json and infer_max_depth functions that were superseded by print_timeline_json_with_meta Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ pub fn collect_events(
|
||||
evidence_notes: &[TimelineEvent],
|
||||
since_ms: Option<i64>,
|
||||
limit: usize,
|
||||
) -> Result<Vec<TimelineEvent>> {
|
||||
) -> Result<(Vec<TimelineEvent>, usize)> {
|
||||
let mut all_events: Vec<TimelineEvent> = Vec::new();
|
||||
|
||||
// Collect events for seed entities
|
||||
@@ -41,10 +41,13 @@ pub fn collect_events(
|
||||
all_events.retain(|e| e.timestamp >= since);
|
||||
}
|
||||
|
||||
// Capture total before applying limit (for meta.total_events vs meta.showing)
|
||||
let total_before_limit = all_events.len();
|
||||
|
||||
// Apply limit
|
||||
all_events.truncate(limit);
|
||||
|
||||
Ok(all_events)
|
||||
Ok((all_events, total_before_limit))
|
||||
}
|
||||
|
||||
/// Collect all events for a single entity.
|
||||
@@ -480,7 +483,7 @@ mod tests {
|
||||
let issue_id = insert_issue(&conn, project_id, 1);
|
||||
let seeds = vec![make_entity_ref("issue", issue_id, 1)];
|
||||
|
||||
let events = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
assert_eq!(events.len(), 1);
|
||||
assert!(matches!(events[0].event_type, TimelineEventType::Created));
|
||||
assert_eq!(events[0].timestamp, 1000);
|
||||
@@ -498,7 +501,7 @@ mod tests {
|
||||
insert_state_event(&conn, project_id, Some(issue_id), None, "reopened", 4000);
|
||||
|
||||
let seeds = vec![make_entity_ref("issue", issue_id, 1)];
|
||||
let events = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
|
||||
// Created + 2 state changes = 3
|
||||
assert_eq!(events.len(), 3);
|
||||
@@ -523,7 +526,7 @@ mod tests {
|
||||
insert_state_event(&conn, project_id, None, Some(mr_id), "merged", 5000);
|
||||
|
||||
let seeds = vec![make_entity_ref("merge_request", mr_id, 10)];
|
||||
let events = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
|
||||
// Should have Created + Merged (not Created + StateChanged{merged} + Merged)
|
||||
let merged_count = events
|
||||
@@ -548,7 +551,7 @@ mod tests {
|
||||
insert_label_event(&conn, project_id, Some(issue_id), None, "add", None, 2000);
|
||||
|
||||
let seeds = vec![make_entity_ref("issue", issue_id, 1)];
|
||||
let events = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
|
||||
let label_event = events.iter().find(|e| {
|
||||
matches!(&e.event_type, TimelineEventType::LabelAdded { label } if label == "[deleted label]")
|
||||
@@ -565,7 +568,7 @@ mod tests {
|
||||
insert_milestone_event(&conn, project_id, Some(issue_id), None, "add", None, 2000);
|
||||
|
||||
let seeds = vec![make_entity_ref("issue", issue_id, 1)];
|
||||
let events = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
|
||||
let ms_event = events.iter().find(|e| {
|
||||
matches!(&e.event_type, TimelineEventType::MilestoneSet { milestone } if milestone == "[deleted milestone]")
|
||||
@@ -585,7 +588,7 @@ mod tests {
|
||||
let seeds = vec![make_entity_ref("issue", issue_id, 1)];
|
||||
|
||||
// Since 4000: should exclude Created (1000) and closed (3000)
|
||||
let events = collect_events(&conn, &seeds, &[], &[], Some(4000), 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &[], Some(4000), 100).unwrap();
|
||||
assert_eq!(events.len(), 1);
|
||||
assert_eq!(events[0].timestamp, 5000);
|
||||
}
|
||||
@@ -612,7 +615,7 @@ mod tests {
|
||||
make_entity_ref("issue", issue_id, 1),
|
||||
make_entity_ref("merge_request", mr_id, 10),
|
||||
];
|
||||
let events = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
|
||||
// Verify chronological order
|
||||
for window in events.windows(2) {
|
||||
@@ -638,8 +641,10 @@ mod tests {
|
||||
}
|
||||
|
||||
let seeds = vec![make_entity_ref("issue", issue_id, 1)];
|
||||
let events = collect_events(&conn, &seeds, &[], &[], None, 5).unwrap();
|
||||
let (events, total) = collect_events(&conn, &seeds, &[], &[], None, 5).unwrap();
|
||||
assert_eq!(events.len(), 5);
|
||||
// 20 state changes + 1 created = 21 total before limit
|
||||
assert_eq!(total, 21);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -666,7 +671,7 @@ mod tests {
|
||||
}];
|
||||
|
||||
let seeds = vec![make_entity_ref("issue", issue_id, 1)];
|
||||
let events = collect_events(&conn, &seeds, &[], &evidence, None, 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &evidence, None, 100).unwrap();
|
||||
|
||||
let note_event = events.iter().find(|e| {
|
||||
matches!(
|
||||
@@ -688,7 +693,7 @@ mod tests {
|
||||
insert_state_event(&conn, project_id, None, Some(mr_id), "merged", 5000);
|
||||
|
||||
let seeds = vec![make_entity_ref("merge_request", mr_id, 10)];
|
||||
let events = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
let (events, _) = collect_events(&conn, &seeds, &[], &[], None, 100).unwrap();
|
||||
|
||||
let merged = events
|
||||
.iter()
|
||||
|
||||
Reference in New Issue
Block a user