summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2025-11-03 17:22:49 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2025-11-04 14:57:53 +0100
commit30c2e6d12a0189feebfff12d9c86a1b579c5c13c (patch)
tree2b8bf0f285222c7ad51595fbc1e0bcab38fbe3ac
parented38f658e7dcbf7d274ece9141cb9b8de885f50d (diff)
downloadmullvadvpn-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.rs36
-rw-r--r--mullvad-update/src/version/mod.rs26
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(&params, response.signed);