// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. use serde_json::json; use crate::enrollment::*; /// A suite of tests for b/w compat of data storage schema. /// /// We use the `Serialize/`Deserialize` impls on various structs in order to persist them /// into rkv, and it's important that we be able to read previously-persisted data even /// if the struct definitions change over time. /// /// This is a suite of tests specifically to check for backward compatibility with data /// that may have been written to disk by previous versions of the library. /// /// ⚠️ Warning : Do not change the JSON data used by these tests. ⚠️ /// ⚠️ The whole point of the tests is to check things work with that data. ⚠️ /// #[test] // This was the `ExperimentEnrollment` object schema as it initially shipped to Fenix Nightly. // It was missing some fields that have since been added. fn test_experiment_enrollment_schema_initial_release() { // ⚠️ Warning : Do not change the JSON data used by this test. ⚠️ let enroll: ExperimentEnrollment = serde_json::from_value(json!({ "slug": "test", "status": {"Enrolled": { "enrollment_id": "b6d6f532-e219-4b5a-8ddf-66700dd47d68", "reason": "Qualified", "branch": "hello", }} })) .unwrap(); assert!(matches!(enroll.status, EnrollmentStatus::Enrolled { .. })); } // In #96 we added a `feature_id` field to the ExperimentEnrollment schema. // This tests the data as it was after that change. #[test] fn test_experiment_schema_with_feature_ids() { // ⚠️ Warning : Do not change the JSON data used by this test. ⚠️ let enroll: ExperimentEnrollment = serde_json::from_value(json!({ "slug": "secure-gold", "status": {"Enrolled": { "enrollment_id": "b6d6f532-e219-4b5a-8ddf-66700dd47d68", "reason": "Qualified", "branch": "hello", "feature_id": "some_control" }} })) .unwrap(); assert!(matches!(enroll.status, EnrollmentStatus::Enrolled { .. })); } // In SDK-260 we added a FeatureConflict variant to the NotEnrolledReason // schema. #[test] fn test_not_enrolled_reason_schema_with_feature_conflict() { // ⚠️ Warning : Do not change the JSON data used by this test. ⚠️ let non_enrollment: ExperimentEnrollment = serde_json::from_value(json!({ "slug": "secure-gold", "status": {"NotEnrolled": { "reason": "FeatureConflict", }} })) .unwrap(); assert!( matches!(non_enrollment.status, EnrollmentStatus::NotEnrolled{ ref reason, ..} if reason == &NotEnrolledReason::FeatureConflict) ); } // In bug 1997373, we added a `prev_gecko_pref_states` field to the EnrollmentStatus schema. // This test check tht the data deserializes correctly both with and without the new field. #[cfg(feature = "stateful")] #[test] fn test_experiment_schema_with_previous_states() { // ⚠️ Warning : Do not change the JSON data used by this test. ⚠️ let prev_gecko_pref_states_empty: EnrollmentStatus = serde_json::from_value(json!({ "Enrolled": { "reason": "Qualified", "branch": "some_branch", } })) .unwrap(); assert!( matches!(prev_gecko_pref_states_empty, EnrollmentStatus::Enrolled {ref prev_gecko_pref_states, ..} if prev_gecko_pref_states.is_none()) ); let prev_gecko_pref_state_exists: EnrollmentStatus = serde_json::from_value(json!({ "Enrolled": { "reason": "Qualified", "branch": "some_branch", "prev_gecko_pref_states": [ { "original_value": { "pref": "some_pref", "branch": "default", "value": 5 }, "feature_id": "some_control", "variable": "some_variable" }, { "original_value": { "pref": "some_pref_2", "branch": "user", "value": "hello" }, "feature_id": "some_control_2", "variable": "some_variable" }, ] } })) .unwrap(); assert!(matches!( prev_gecko_pref_state_exists, EnrollmentStatus::Enrolled { prev_gecko_pref_states: Some(ref states), .. } if states[0].original_value.pref == "some_pref" && states[0].original_value.value.clone().unwrap() == 5 && states[0].feature_id == "some_control" && states[0].variable == "some_variable" && states[1].original_value.pref == "some_pref_2" && states[1].original_value.value.clone().unwrap() == "hello" )); }