Skip to content

Commit b5e934c

Browse files
committed
more tests
1 parent 92288ec commit b5e934c

File tree

4 files changed

+128
-0
lines changed

4 files changed

+128
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
**/*.rs.bk
33
Cargo.lock
4+
lcov.info

.justfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ check:
5050
clean:
5151
cargo clean
5252

53+
# Generate code coverage report
54+
coverage:
55+
cargo llvm-cov --all-features --workspace
56+
5357
# Run all quality checks (format, lint, test)
5458
ci: format-check lint test
5559
@echo "All CI checks passed!"

src/lib.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,4 +472,48 @@ mod tests {
472472
// December 31
473473
assert!(make_utc_datetime(2024, 12, 31, 23, 59, 59).is_ok());
474474
}
475+
476+
#[test]
477+
fn test_parse_error_display() {
478+
// Test InvalidCron
479+
let err = ParseError::InvalidCron;
480+
assert_eq!(format!("{}", err), "invalid cron");
481+
482+
// Test InvalidRange
483+
let err = ParseError::InvalidRange;
484+
assert_eq!(format!("{}", err), "invalid input");
485+
486+
// Test InvalidValue
487+
let err = ParseError::InvalidValue;
488+
assert_eq!(format!("{}", err), "invalid value");
489+
490+
// Test ParseIntError
491+
let parse_int_err = "abc".parse::<u32>().unwrap_err();
492+
let err = ParseError::ParseIntError(parse_int_err);
493+
assert!(format!("{}", err).contains("invalid digit"));
494+
495+
// Test TryFromIntError
496+
let try_from_err = u8::try_from(256u32).unwrap_err();
497+
let err = ParseError::TryFromIntError(try_from_err);
498+
assert!(format!("{}", err).contains("out of range"));
499+
500+
// Test InvalidTimezone
501+
let err = ParseError::InvalidTimezone;
502+
assert_eq!(format!("{}", err), "invalid timezone");
503+
}
504+
505+
#[test]
506+
fn test_parse_error_from_try_from_int_error() {
507+
// Test From<TryFromIntError> conversion
508+
let try_from_err = u8::try_from(256u32).unwrap_err();
509+
let parse_err: ParseError = try_from_err.into();
510+
assert!(matches!(parse_err, ParseError::TryFromIntError(_)));
511+
}
512+
513+
#[test]
514+
fn test_parse_error_implements_error_trait() {
515+
// Test that ParseError implements Error trait
516+
let err: Box<dyn Error> = Box::new(ParseError::InvalidCron);
517+
assert_eq!(err.to_string(), "invalid cron");
518+
}
475519
}

tests/tests.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,5 +436,84 @@ fn test_case_insensitive_dow() {
436436
);
437437
}
438438

439+
// Test for invalid range in step with range format (e.g., "1-2-3/5")
440+
#[test]
441+
fn test_invalid_range_in_step() {
442+
// Multiple dashes in range with step
443+
assert!(parse_field("1-2-3/5", 0, 59).is_err());
444+
assert!(parse_field("10-20-30/2", 0, 59).is_err());
445+
}
446+
447+
// Test for invalid range where start > end in step format
448+
#[test]
449+
fn test_reverse_range_with_step() {
450+
// Reverse range with step should error
451+
assert!(parse_field("20-10/2", 0, 59).is_err());
452+
assert!(parse_field("50-40/5", 0, 59).is_err());
453+
}
454+
455+
// Test cron expression that will never match within 4 years
456+
#[test]
457+
fn test_cron_never_matches() {
458+
// Invalid combination: February 30 will never occur
459+
let now = Utc.timestamp_opt(1_573_151_292, 0).unwrap();
460+
assert!(parse("0 0 30 2 *", &now).is_err());
461+
}
462+
463+
// Test with DST transition (spring forward) - skipped hour
464+
#[test]
465+
fn test_dst_spring_forward_skipped_time() {
466+
// 2024-03-10 02:00 AM PST does not exist (spring forward to 3:00 AM PDT)
467+
// Use Pacific timezone which has DST
468+
let before_dst = Pacific
469+
.with_ymd_and_hms(2024, 3, 10, 1, 30, 0)
470+
.unwrap();
471+
472+
// Schedule at 2:30 AM which doesn't exist due to DST
473+
// The parser should skip to the next valid time
474+
let result = parse("30 2 * * *", &before_dst);
475+
assert!(result.is_ok());
476+
477+
// The result should be after the DST transition
478+
let next = result.unwrap();
479+
// Should skip the non-existent 2:30 AM and go to the next day
480+
assert!(next.day() >= 10);
481+
}
482+
483+
// Test with fall back DST - ambiguous time
484+
#[test]
485+
fn test_dst_fall_back_ambiguous_time() {
486+
// November 3, 2024: 1:00 AM happens twice (fall back)
487+
// Create a time during the ambiguous period
488+
match Pacific.with_ymd_and_hms(2024, 11, 3, 1, 30, 0) {
489+
chrono::LocalResult::Ambiguous(earlier, _later) => {
490+
// Use the earlier (PDT) time
491+
let result = parse("*/15 * * * *", &earlier);
492+
assert!(result.is_ok());
493+
}
494+
chrono::LocalResult::Single(dt) => {
495+
// If single, just test it works
496+
let result = parse("*/15 * * * *", &dt);
497+
assert!(result.is_ok());
498+
}
499+
chrono::LocalResult::None => {
500+
panic!("1:30 AM on Nov 3 should exist");
501+
}
502+
}
503+
}
504+
505+
// Test with extremely restrictive cron that takes long to find next match
506+
#[test]
507+
fn test_very_restrictive_cron() {
508+
// Feb 29 only on leap years that fall on Friday
509+
let now = Utc.timestamp_opt(1_577_836_800, 0).unwrap(); // 2020-01-01
510+
511+
// This should work as 2020-02-29 is on Saturday (day 6)
512+
// But if we look for Sunday (day 0), it won't match in 4 years
513+
let result = parse("0 0 29 2 0", &now);
514+
// Feb 29 on Sunday doesn't occur in the next 4 years from 2020
515+
assert!(result.is_err());
516+
}
517+
439518
// 1541322900 -> 1_541_322_900
440519
// vim :%s/\(\d\)\(\(\d\d\d\)\+\d\@!\)\@=/\1_/g

0 commit comments

Comments
 (0)