diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-11-03 17:22:49 +0100 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-11-04 14:57:53 +0100 |
| commit | 30c2e6d12a0189feebfff12d9c86a1b579c5c13c (patch) | |
| tree | 2b8bf0f285222c7ad51595fbc1e0bcab38fbe3ac | |
| parent | ed38f658e7dcbf7d274ece9141cb9b8de885f50d (diff) | |
| download | mullvadvpn-proptest-some-more.tar.xz mullvadvpn-proptest-some-more.zip | |
WIP Test rollout properties, starting with the 0-rollout.proptest-some-more
| -rw-r--r-- | mullvad-update/src/format/mod.rs | 36 | ||||
| -rw-r--r-- | mullvad-update/src/version/mod.rs | 26 |
2 files changed, 60 insertions, 2 deletions
diff --git a/mullvad-update/src/format/mod.rs b/mullvad-update/src/format/mod.rs index b4fbf36d6a..e915660523 100644 --- a/mullvad-update/src/format/mod.rs +++ b/mullvad-update/src/format/mod.rs @@ -65,6 +65,8 @@ pub struct Response { } /// App release +/// +/// TODO: I need to be able to generate arbitrary releases. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct Release { /// Mullvad app version @@ -166,6 +168,40 @@ pub enum ResponseSignature { } #[cfg(test)] +pub mod arbitrary { + use super::*; + + use prop::collection::vec; + use proptest::prelude::*; + + prop_compose! { + /// Generate an arbitrary [Release] with `rollout`. + pub fn arb_release(rollout: Rollout) + (version in mullvad_version::arbitrary::arb_version(), + installers in vec(arb_installer(), 5), // TODO: 5 is arbitrary + ) + -> Release { + let changelog = String::new(); // TODO + Release { version, changelog, installers, rollout } } + } + + /// Generate an arbitrary x86 insaller + // + // TODO + fn arb_installer() -> impl Strategy<Value = Installer> { + let urls = vec![]; + let size = 0; + let sha256 = String::from_utf8_lossy(&[0; 32]).into_owned(); + Just(Installer { + architecture: Architecture::X86, + urls, + size, + sha256, + }) + } +} + +#[cfg(test)] mod test { use super::*; diff --git a/mullvad-update/src/version/mod.rs b/mullvad-update/src/version/mod.rs index 9058ec5b75..ffdc606a42 100644 --- a/mullvad-update/src/version/mod.rs +++ b/mullvad-update/src/version/mod.rs @@ -121,12 +121,15 @@ impl VersionInfo { /// Convert signed response data to public version type /// NOTE: `response` is assumed to be verified and untampered. It is not verified. // - // TODO: Decompose + // NOTE: It should be `get_version_info` doing the bulk of the work. This function is merely a + // wrapper around `get_version_info` with some added validation on top. For testing purposes, + // we don't really care about this extra validation. pub fn try_from_response( params: &VersionParameters, response: format::Response, ) -> anyhow::Result<Self> { // Check this before anything else so that it's rejected independently of `params`. + // a lil bit of defensive programming.. shotgun parsing style. Self::validate_releases(&response.releases)?; let version_info = get_version_info( @@ -148,10 +151,11 @@ impl VersionInfo { } } +/// TODO: Rename mee /// TODO: Document this function *very well*. /// Input: TODO /// Output: TODO -fn get_version_info( +pub fn get_version_info( releases: Vec<Release>, rollout: Rollout, allow_empty: bool, @@ -338,12 +342,30 @@ mod test { use std::str::FromStr; use insta::assert_yaml_snapshot; + use prop::collection::vec; use proptest::prelude::*; + use format::arbitrary::arb_release; + const TEST_RESPONSE: &[u8] = include_bytes!("../../test-version-response.json"); proptest! { #[test] + /// For any app version with 0 rollout, no client should _ever_ be prompted to update to this app version. + // + // `arb_version_parameters` get to represnt an arbitrary client, eagerly waiting for an update to be released. + // + // Generate a list of arbitrary releases!! + // These should all have a rollout rate of 0 / rollout::IGNORE. + fn no_rollout_no_update(client in arbitrary::arb_version_parameters(true), releases in vec(arb_release(rollout::IGNORE), 5)) { // TODO: 5 is arbitrary. + // This could be either a Linux or Windows/macOS client. The property generalizes well. + let suggested_update = super::get_version_info(releases, client.rollout, client.allow_empty, client.architecture); + // There should be no suggested update! I.e. `suggested_update` should be an error / + // empty. + prop_assert!(suggested_update.is_err()) + } + + #[test] fn test_allow_empty_installers(params in arbitrary::arb_version_parameters(true)) { let response = format::SignedResponse::deserialize_insecure(TEST_RESPONSE).unwrap(); let info = VersionInfo::try_from_response(¶ms, response.signed); |
