Skip to content

Commit 6d1d6b7

Browse files
committed
fix(cli): exclude undated stats sessions from days filter
1 parent 7954d02 commit 6d1d6b7

1 file changed

Lines changed: 87 additions & 4 deletions

File tree

src/cortex-cli/src/stats_cmd.rs

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,7 @@ async fn collect_stats(sessions_dir: &PathBuf, cli: &StatsCli) -> Result<UsageSt
363363
// Try to parse the session
364364
if let Ok(session_data) = parse_session_file(&path) {
365365
// Check if session is within date range
366-
if let Some(ref timestamp) = session_data.timestamp
367-
&& let Ok(session_date) = chrono::DateTime::parse_from_rfc3339(timestamp)
368-
&& session_date < start_date
369-
{
366+
if !session_is_within_date_range(session_data.timestamp.as_deref(), &start_date) {
370367
continue;
371368
}
372369

@@ -430,6 +427,21 @@ async fn collect_stats(sessions_dir: &PathBuf, cli: &StatsCli) -> Result<UsageSt
430427
Ok(stats)
431428
}
432429

430+
fn session_is_within_date_range(
431+
timestamp: Option<&str>,
432+
start_date: &chrono::DateTime<chrono::Utc>,
433+
) -> bool {
434+
let Some(timestamp) = timestamp else {
435+
return false;
436+
};
437+
438+
let Ok(session_date) = chrono::DateTime::parse_from_rfc3339(timestamp) else {
439+
return false;
440+
};
441+
442+
session_date.with_timezone(&chrono::Utc) >= *start_date
443+
}
444+
433445
/// Session data extracted from file.
434446
#[derive(Debug, Default)]
435447
struct SessionData {
@@ -735,6 +747,77 @@ mod tests {
735747
assert!((cost - 12.5).abs() < 0.001);
736748
}
737749

750+
#[test]
751+
fn test_session_date_range_requires_valid_timestamp() {
752+
let start_date = chrono::Utc::now() - chrono::Duration::days(1);
753+
let recent = chrono::Utc::now().to_rfc3339();
754+
let old = (chrono::Utc::now() - chrono::Duration::days(2)).to_rfc3339();
755+
756+
assert!(session_is_within_date_range(Some(&recent), &start_date));
757+
assert!(!session_is_within_date_range(Some(&old), &start_date));
758+
assert!(!session_is_within_date_range(None, &start_date));
759+
assert!(!session_is_within_date_range(
760+
Some("not-a-date"),
761+
&start_date
762+
));
763+
}
764+
765+
#[tokio::test]
766+
async fn test_collect_stats_excludes_missing_and_invalid_timestamps() {
767+
let temp_dir = tempfile::tempdir().unwrap();
768+
let sessions_dir = temp_dir.path().to_path_buf();
769+
let recent = chrono::Utc::now().to_rfc3339();
770+
771+
std::fs::write(
772+
sessions_dir.join("recent.json"),
773+
format!(
774+
r#"{{
775+
"created_at": "{recent}",
776+
"model": "gpt-4o",
777+
"messages": [{{"role": "user", "content": "valid"}}],
778+
"usage": {{"input_tokens": 100, "output_tokens": 100}}
779+
}}"#
780+
),
781+
)
782+
.unwrap();
783+
784+
std::fs::write(
785+
sessions_dir.join("missing.json"),
786+
r#"{
787+
"model": "gpt-4o",
788+
"messages": [{"role": "user", "content": "missing timestamp"}],
789+
"usage": {"input_tokens": 9999, "output_tokens": 9999}
790+
}"#,
791+
)
792+
.unwrap();
793+
794+
std::fs::write(
795+
sessions_dir.join("invalid.json"),
796+
r#"{
797+
"timestamp": "not-a-date",
798+
"model": "gpt-4o",
799+
"messages": [{"role": "user", "content": "invalid timestamp"}],
800+
"usage": {"input_tokens": 8888, "output_tokens": 8888}
801+
}"#,
802+
)
803+
.unwrap();
804+
805+
let cli = StatsCli {
806+
days: 1,
807+
provider: None,
808+
model: None,
809+
json: false,
810+
verbose: false,
811+
};
812+
813+
let stats = collect_stats(&sessions_dir, &cli).await.unwrap();
814+
815+
assert_eq!(stats.total_sessions, 1);
816+
assert_eq!(stats.total_messages, 1);
817+
assert_eq!(stats.input_tokens, 100);
818+
assert_eq!(stats.output_tokens, 100);
819+
}
820+
738821
#[test]
739822
fn test_validate_days_range() {
740823
// Valid values

0 commit comments

Comments
 (0)