|
2 | 2 |
|
3 | 3 | from datetime import datetime, timezone
|
4 | 4 | from typing import List
|
| 5 | +from unittest.mock import Mock, patch |
5 | 6 |
|
6 | 7 | import pytest
|
7 | 8 |
|
@@ -620,3 +621,123 @@ def test_period_sorting(self, aggregator: UsageAggregator) -> None:
|
620 | 621 | assert monthly_result[0]["month"] == "2024-01"
|
621 | 622 | assert monthly_result[1]["month"] == "2024-02"
|
622 | 623 | assert monthly_result[2]["month"] == "2024-03"
|
| 624 | + |
| 625 | + def test_aggregate_daily_with_date_filters( |
| 626 | + self, aggregator: UsageAggregator |
| 627 | + ) -> None: |
| 628 | + """Test aggregate_daily with date range filters.""" |
| 629 | + entries = [] |
| 630 | + # Create entries across 10 days |
| 631 | + for i in range(10): |
| 632 | + date = datetime(2024, 1, i + 1, 12, 0, tzinfo=timezone.utc) |
| 633 | + entries.append( |
| 634 | + UsageEntry( |
| 635 | + timestamp=date, |
| 636 | + input_tokens=100 * (i + 1), |
| 637 | + output_tokens=50 * (i + 1), |
| 638 | + cost_usd=0.001 * (i + 1), |
| 639 | + model="claude-3-haiku", |
| 640 | + message_id=f"msg_{i}", |
| 641 | + request_id=f"req_{i}", |
| 642 | + ) |
| 643 | + ) |
| 644 | + |
| 645 | + # Filter for days 3-7 (Jan 3 to Jan 7) |
| 646 | + start_date = datetime(2024, 1, 3, tzinfo=timezone.utc) |
| 647 | + end_date = datetime(2024, 1, 8, tzinfo=timezone.utc) # End is exclusive |
| 648 | + |
| 649 | + result = aggregator.aggregate_daily(entries, start_date, end_date) |
| 650 | + |
| 651 | + # Should have 5 days (Jan 3, 4, 5, 6, 7) |
| 652 | + assert len(result) == 5 |
| 653 | + assert result[0]["date"] == "2024-01-03" |
| 654 | + assert result[-1]["date"] == "2024-01-07" |
| 655 | + |
| 656 | + # Verify token counts for first day (Jan 3 = day 3, 300 input tokens) |
| 657 | + assert result[0]["input_tokens"] == 300 |
| 658 | + assert result[0]["output_tokens"] == 150 |
| 659 | + |
| 660 | + def test_aggregate_monthly_with_date_filters( |
| 661 | + self, aggregator: UsageAggregator |
| 662 | + ) -> None: |
| 663 | + """Test aggregate_monthly with date range filters.""" |
| 664 | + entries = [] |
| 665 | + # Create entries spanning 3 months |
| 666 | + for month in [11, 12]: |
| 667 | + for day in [5, 15, 25]: |
| 668 | + date = datetime(2024, month, day, tzinfo=timezone.utc) |
| 669 | + entries.append( |
| 670 | + UsageEntry( |
| 671 | + timestamp=date, |
| 672 | + input_tokens=1000, |
| 673 | + output_tokens=500, |
| 674 | + cost_usd=0.01, |
| 675 | + model="claude-3-haiku", |
| 676 | + message_id=f"msg_{month}_{day}", |
| 677 | + request_id=f"req_{month}_{day}", |
| 678 | + ) |
| 679 | + ) |
| 680 | + |
| 681 | + # Also add January 2025 entries |
| 682 | + for day in [5, 15]: |
| 683 | + date = datetime(2025, 1, day, tzinfo=timezone.utc) |
| 684 | + entries.append( |
| 685 | + UsageEntry( |
| 686 | + timestamp=date, |
| 687 | + input_tokens=1000, |
| 688 | + output_tokens=500, |
| 689 | + cost_usd=0.01, |
| 690 | + model="claude-3-haiku", |
| 691 | + message_id=f"msg_2025_1_{day}", |
| 692 | + request_id=f"req_2025_1_{day}", |
| 693 | + ) |
| 694 | + ) |
| 695 | + |
| 696 | + # Filter to December 2024 only |
| 697 | + start_date = datetime(2024, 12, 1, tzinfo=timezone.utc) |
| 698 | + end_date = datetime(2025, 1, 1, tzinfo=timezone.utc) |
| 699 | + |
| 700 | + result = aggregator.aggregate_monthly(entries, start_date, end_date) |
| 701 | + |
| 702 | + # Should have 1 month (December 2024) |
| 703 | + assert len(result) == 1 |
| 704 | + assert result[0]["month"] == "2024-12" |
| 705 | + assert result[0]["input_tokens"] == 3000 # 3 days * 1000 |
| 706 | + assert result[0]["output_tokens"] == 1500 # 3 days * 500 |
| 707 | + |
| 708 | + @patch("claude_monitor.data.reader.load_usage_entries") |
| 709 | + def test_aggregate_with_date_filters( |
| 710 | + self, mock_load: Mock, aggregator: UsageAggregator |
| 711 | + ) -> None: |
| 712 | + """Test main aggregate method with date filters.""" |
| 713 | + entries = [] |
| 714 | + for i in range(5): |
| 715 | + date = datetime(2024, 1, i + 1, 12, 0, tzinfo=timezone.utc) |
| 716 | + entries.append( |
| 717 | + UsageEntry( |
| 718 | + timestamp=date, |
| 719 | + input_tokens=100, |
| 720 | + output_tokens=50, |
| 721 | + cost_usd=0.001, |
| 722 | + model="claude-3-haiku", |
| 723 | + message_id=f"msg_{i}", |
| 724 | + request_id=f"req_{i}", |
| 725 | + ) |
| 726 | + ) |
| 727 | + |
| 728 | + mock_load.return_value = (entries, None) |
| 729 | + |
| 730 | + # Test with date filters |
| 731 | + start_date = datetime(2024, 1, 2, tzinfo=timezone.utc) |
| 732 | + end_date = datetime(2024, 1, 4, tzinfo=timezone.utc) |
| 733 | + |
| 734 | + result = aggregator.aggregate(start_date=start_date, end_date=end_date) |
| 735 | + |
| 736 | + # Should have 2 days (Jan 2, 3) |
| 737 | + assert len(result) == 2 |
| 738 | + assert result[0]["date"] == "2024-01-02" |
| 739 | + assert result[1]["date"] == "2024-01-03" |
| 740 | + |
| 741 | + # Test without filters - should return all |
| 742 | + result_all = aggregator.aggregate() |
| 743 | + assert len(result_all) == 5 |
0 commit comments