database_migration/
proptest_support.rs

1use crate::checksum::Checksum;
2use crate::config::{DEFAULT_MIGRATIONS_FOLDER, MIGRATION_KEY_FORMAT_STR};
3use crate::definition::{
4    DOWN_SCRIPT_FILE_EXTENSION, SCRIPT_FILE_EXTENSION, UP_SCRIPT_FILE_EXTENSION,
5};
6use crate::migration::{Migration, MigrationKind};
7use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
8use proptest::prelude::*;
9use proptest::string::string_regex;
10use std::path::PathBuf;
11
12pub fn any_checksum() -> impl Strategy<Value = Checksum> {
13    (0..=0x_FFFF_FFFF_u32).prop_map(Checksum)
14}
15
16#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
17fn days_in_month(year: i32, month: u32) -> u32 {
18    let current_month =
19        NaiveDate::from_ymd_opt(year, month, 1).expect("year or month out of range");
20    let (next_year, next_month) = match month {
21        12 => (year + 1, 1),
22        _ => (year, month + 1),
23    };
24    let next_month = NaiveDate::from_ymd_opt(next_year, next_month, 1)
25        .expect("next_year or next_month out of range");
26    next_month.signed_duration_since(current_month).num_days() as u32
27}
28
29pub fn any_key() -> impl Strategy<Value = NaiveDateTime> {
30    (1970..=9999, 1..=12_u32)
31        .prop_flat_map(|(year, month)| {
32            (
33                Just(year),
34                Just(month),
35                1..=days_in_month(year, month),
36                1..=23_u32,
37                1..=59_u32,
38                1..=59_u32,
39            )
40        })
41        .prop_map(|(year, month, day, hour, minute, second)| {
42            NaiveDateTime::new(
43                NaiveDate::from_ymd_opt(year, month, day).expect("year, month or day out of range"),
44                NaiveTime::from_hms_opt(hour, minute, second)
45                    .expect("hour, minute or second out of range"),
46            )
47        })
48}
49
50pub fn any_title() -> impl Strategy<Value = String> {
51    string_regex(r"([\w][\w\-_ ]{0,200})?").expect("invalid regex for title")
52}
53
54pub fn any_migration_kind() -> impl Strategy<Value = MigrationKind> {
55    prop_oneof![
56        Just(MigrationKind::Up),
57        Just(MigrationKind::Down),
58        Just(MigrationKind::Baseline)
59    ]
60}
61
62pub fn any_direction() -> impl Strategy<Value = MigrationKind> {
63    prop_oneof![Just(MigrationKind::Up), Just(MigrationKind::Down),]
64}
65
66pub fn any_filename() -> impl Strategy<Value = String> {
67    (any_key(), any_title(), any_direction(), any::<bool>()).prop_map(
68        |(key, title, direction, include_direction)| {
69            let mut filename = key.format(MIGRATION_KEY_FORMAT_STR).to_string();
70            filename.push('_');
71            filename.push_str(&title);
72            match (include_direction, direction) {
73                (true, MigrationKind::Down) => filename.push_str(DOWN_SCRIPT_FILE_EXTENSION),
74                (true, _) => filename.push_str(UP_SCRIPT_FILE_EXTENSION),
75                (false, _) => filename.push_str(SCRIPT_FILE_EXTENSION),
76            }
77            filename
78        },
79    )
80}
81
82pub fn any_script_path() -> impl Strategy<Value = PathBuf> {
83    (
84        string_regex(r"/?[\w][\w\-_ ]{1,50}((/[\w])?[\w\-_ ]{1,50}){1,3}")
85            .expect("invalid regex for workdir path"),
86        any_filename(),
87        any::<bool>(),
88    )
89        .prop_map(|(workdir, filename, default_migrations_folder)| {
90            let mut path = PathBuf::from(workdir);
91            if default_migrations_folder {
92                path.push(DEFAULT_MIGRATIONS_FOLDER);
93            }
94            path.push(filename);
95            path
96        })
97}
98
99pub fn any_script_content() -> impl Strategy<Value = String> {
100    string_regex(r#"(([\w][\w\-_ \(\)\[\]\{\}#'"]){1,100};\n){1,12}"#)
101        .expect("invalid regex for script content")
102}
103
104pub fn any_migration() -> impl Strategy<Value = Migration> {
105    (any_key(), any_title(), any_direction(), any_script_path()).prop_map(
106        |(key, title, kind, script_path)| Migration {
107            key,
108            title,
109            kind,
110            script_path,
111        },
112    )
113}