@@ -3,133 +3,148 @@ use crate::statistics::StatisticsCalculator;
33use crate :: time_config:: TimeConfig ;
44use std:: collections:: HashMap ;
55
6- fn create_mock_stats ( ) -> StatisticsData {
7- // Create 3 days of data:
8- // Day 1 (2023-01-01): 5 pages, 10 mins (600s)
9- // Day 2 (2023-01-02): 20 pages, 5 mins (300s)
10- // Day 3 (2023-01-03): 2 pages, 60 mins (3600s)
11-
6+ #[ test]
7+ fn test_filter_stats_per_book_per_day ( ) {
8+ // Comprehensive test for per-book-per-day filtering
9+ // Tests multiple scenarios:
10+ // - Day 1: Book 1 (10 pages, 600s) passes pages threshold, Book 2 (5 pages, 300s) fails both
11+ // - Day 2: Book 1 (2 pages, 3600s) passes time threshold, Book 2 (15 pages, 200s) passes pages threshold
12+ // - Day 3: Book 1 (3 pages, 100s) fails both, Book 2 (4 pages, 500s) fails both
13+
1214 let day1_ts = 1672531200 ; // 2023-01-01 00:00:00 UTC
1315 let day2_ts = 1672617600 ; // 2023-01-02 00:00:00 UTC
1416 let day3_ts = 1672704000 ; // 2023-01-03 00:00:00 UTC
15-
1617 let mut page_stats = Vec :: new ( ) ;
17-
18- // Day 1: 5 pages, 10 mins
19- for i in 0 ..5 {
18+
19+ // Day 1: Book 1 - 10 pages, 600s (passes pages threshold)
20+ for i in 0 ..10 {
2021 page_stats. push ( PageStat {
2122 id_book : 1 ,
2223 page : i,
23- start_time : day1_ts + i * 60 , // 1 min per page roughly
24- duration : 120 , // 2 mins per page -> total 10 mins
24+ start_time : day1_ts + i * 60 ,
25+ duration : 60 ,
2526 } ) ;
2627 }
27-
28- // Day 2: 20 pages, 5 mins
29- for i in 0 ..20 {
28+
29+ // Day 1: Book 2 - 5 pages, 300s (fails both thresholds)
30+ for i in 0 ..5 {
3031 page_stats. push ( PageStat {
31- id_book : 1 ,
32+ id_book : 2 ,
3233 page : i,
33- start_time : day2_ts + i * 10 ,
34- duration : 15 , // 15s per page -> total 300s (5 mins)
34+ start_time : day1_ts + i * 60 ,
35+ duration : 60 ,
3536 } ) ;
3637 }
37-
38- // Day 3: 2 pages, 60 mins
38+
39+ // Day 2: Book 1 - 2 pages, 3600s (passes time threshold)
3940 for i in 0 ..2 {
4041 page_stats. push ( PageStat {
4142 id_book : 1 ,
42- page : i,
43- start_time : day3_ts + i * 1800 ,
44- duration : 1800 , // 30 mins per page -> total 60 mins
43+ page : i + 10 ,
44+ start_time : day2_ts + i * 1800 ,
45+ duration : 1800 ,
4546 } ) ;
4647 }
47-
48- StatisticsData {
49- books : vec ! [ StatBook {
50- id: 1 ,
51- title: "Test Book" . to_string( ) ,
52- authors: "Author" . to_string( ) ,
53- notes: None ,
54- last_open: None ,
55- highlights: None ,
56- pages: None ,
57- md5: "abc" . to_string( ) ,
58- total_read_time: None ,
59- total_read_pages: None ,
60- completions: None ,
61- } ] ,
62- page_stats,
63- stats_by_md5 : HashMap :: new ( ) ,
48+
49+ // Day 2: Book 2 - 15 pages, 200s (passes pages threshold)
50+ for i in 0 ..15 {
51+ page_stats. push ( PageStat {
52+ id_book : 2 ,
53+ page : i + 5 ,
54+ start_time : day2_ts + i * 10 ,
55+ duration : 13 ,
56+ } ) ;
6457 }
65- }
66-
67- #[ test]
68- fn test_filter_stats_min_pages ( ) {
69- let mut data = create_mock_stats ( ) ;
70- let time_config = TimeConfig :: new ( None , 0 ) ; // UTC by default
71-
72- // Filter: min 10 pages
73- // Should keep Day 2 (20 pages), remove Day 1 (5) and Day 3 (2)
74- StatisticsCalculator :: filter_stats ( & mut data, & time_config, Some ( 10 ) , None ) ;
75-
76- let remaining_days: Vec < i64 > = data. page_stats . iter ( ) . map ( |s| s. start_time ) . collect ( ) ;
77- assert ! ( !remaining_days. is_empty( ) ) ;
78-
79- for ts in remaining_days {
80- // All timestamps should be from Day 2
81- assert ! ( ts >= 1672617600 && ts < 1672704000 ) ;
58+
59+ // Day 3: Book 1 - 3 pages, 100s (fails both)
60+ for i in 0 ..3 {
61+ page_stats. push ( PageStat {
62+ id_book : 1 ,
63+ page : i + 12 ,
64+ start_time : day3_ts + i * 30 ,
65+ duration : 33 ,
66+ } ) ;
8267 }
83- }
84-
85- #[ test]
86- fn test_filter_stats_min_time ( ) {
87- let mut data = create_mock_stats ( ) ;
88- let time_config = TimeConfig :: new ( None , 0 ) ;
89-
90- // Filter: min 30 mins (1800s)
91- // Should keep Day 3 (60 mins), remove Day 1 (10 mins) and Day 2 (5 mins)
92- StatisticsCalculator :: filter_stats ( & mut data, & time_config, None , Some ( 1800 ) ) ;
93-
94- let remaining_days: Vec < i64 > = data. page_stats . iter ( ) . map ( |s| s. start_time ) . collect ( ) ;
95- assert ! ( !remaining_days. is_empty( ) ) ;
96-
97- for ts in remaining_days {
98- // All timestamps should be from Day 3
99- assert ! ( ts >= 1672704000 ) ;
68+
69+ // Day 3: Book 2 - 4 pages, 500s (fails both)
70+ for i in 0 ..4 {
71+ page_stats. push ( PageStat {
72+ id_book : 2 ,
73+ page : i + 20 ,
74+ start_time : day3_ts + i * 100 ,
75+ duration : 125 ,
76+ } ) ;
10077 }
101- }
102-
103- #[ test]
104- fn test_filter_stats_or_logic ( ) {
105- let mut data = create_mock_stats ( ) ;
78+
79+ let mut data = StatisticsData {
80+ books : vec ! [
81+ StatBook {
82+ id: 1 ,
83+ title: "Book 1" . to_string( ) ,
84+ authors: "Author 1" . to_string( ) ,
85+ notes: None ,
86+ last_open: None ,
87+ highlights: None ,
88+ pages: None ,
89+ md5: "abc" . to_string( ) ,
90+ total_read_time: None ,
91+ total_read_pages: None ,
92+ completions: None ,
93+ } ,
94+ StatBook {
95+ id: 2 ,
96+ title: "Book 2" . to_string( ) ,
97+ authors: "Author 2" . to_string( ) ,
98+ notes: None ,
99+ last_open: None ,
100+ highlights: None ,
101+ pages: None ,
102+ md5: "def" . to_string( ) ,
103+ total_read_time: None ,
104+ total_read_pages: None ,
105+ completions: None ,
106+ } ,
107+ ] ,
108+ page_stats,
109+ stats_by_md5 : HashMap :: new ( ) ,
110+ } ;
111+
106112 let time_config = TimeConfig :: new ( None , 0 ) ;
107-
108- // Filter: min 10 pages OR min 30 mins
109- // Should keep Day 2 (20 pages) AND Day 3 (60 mins)
110- // Should remove Day 1 (5 pages, 10 mins)
111- StatisticsCalculator :: filter_stats ( & mut data, & time_config, Some ( 10 ) , Some ( 1800 ) ) ;
112-
113- let remaining_days: Vec < i64 > = data. page_stats . iter ( ) . map ( |s| s. start_time ) . collect ( ) ;
114- assert ! ( !remaining_days. is_empty( ) ) ;
115-
116- let mut has_day2 = false ;
117- let mut has_day3 = false ;
118- let mut has_day1 = false ;
119-
120- for ts in remaining_days {
121- if ts >= 1672531200 && ts < 1672617600 {
122- has_day1 = true ;
123- }
124- if ts >= 1672617600 && ts < 1672704000 {
125- has_day2 = true ;
126- }
127- if ts >= 1672704000 {
128- has_day3 = true ;
113+ // Filter: min 8 pages OR min 1800s (30 mins)
114+ StatisticsCalculator :: filter_stats ( & mut data, & time_config, Some ( 8 ) , Some ( 1800 ) ) ;
115+
116+ // Expected results:
117+ // Day 1: Book 1 kept (10 pages), Book 2 filtered (5 pages, 300s)
118+ // Day 2: Book 1 kept (3600s), Book 2 kept (15 pages)
119+ // Day 3: Both filtered
120+ // Total: 10 + 2 + 15 = 27 pages
121+
122+ assert_eq ! ( data. page_stats. len( ) , 27 , "Should have 27 pages remaining" ) ;
123+
124+ let mut book1_day1 = 0 ;
125+ let mut book1_day2 = 0 ;
126+ let mut book1_day3 = 0 ;
127+ let mut book2_day1 = 0 ;
128+ let mut book2_day2 = 0 ;
129+ let mut book2_day3 = 0 ;
130+
131+ for stat in & data. page_stats {
132+ match ( stat. id_book , stat. start_time ) {
133+ ( 1 , ts) if ts >= day1_ts && ts < day2_ts => book1_day1 += 1 ,
134+ ( 1 , ts) if ts >= day2_ts && ts < day3_ts => book1_day2 += 1 ,
135+ ( 1 , ts) if ts >= day3_ts => book1_day3 += 1 ,
136+ ( 2 , ts) if ts >= day1_ts && ts < day2_ts => book2_day1 += 1 ,
137+ ( 2 , ts) if ts >= day2_ts && ts < day3_ts => book2_day2 += 1 ,
138+ ( 2 , ts) if ts >= day3_ts => book2_day3 += 1 ,
139+ _ => { }
129140 }
130141 }
131-
132- assert ! ( !has_day1, "Day 1 should be removed" ) ;
133- assert ! ( has_day2, "Day 2 should be kept (pages)" ) ;
134- assert ! ( has_day3, "Day 3 should be kept (time)" ) ;
142+
143+ assert_eq ! ( book1_day1, 10 , "Book 1 Day 1: 10 pages (passes pages threshold)" ) ;
144+ assert_eq ! ( book1_day2, 2 , "Book 1 Day 2: 2 pages (passes time threshold)" ) ;
145+ assert_eq ! ( book1_day3, 0 , "Book 1 Day 3: filtered (fails both)" ) ;
146+ assert_eq ! ( book2_day1, 0 , "Book 2 Day 1: filtered (fails both)" ) ;
147+ assert_eq ! ( book2_day2, 15 , "Book 2 Day 2: 15 pages (passes pages threshold)" ) ;
148+ assert_eq ! ( book2_day3, 0 , "Book 2 Day 3: filtered (fails both)" ) ;
135149}
150+
0 commit comments