From 3da6c38ef4f5645e7ae9574d2291b2d94578c9ba Mon Sep 17 00:00:00 2001 From: Bryan Oltman Date: Thu, 20 Jun 2024 15:47:59 -0400 Subject: [PATCH] feat: add timestamp to patch events (#179) * feat: add timestamp to patch events * fix test --- Cargo.lock | 7 +++++++ library/Cargo.toml | 1 + library/src/events.rs | 3 +++ library/src/lib.rs | 1 + library/src/network.rs | 7 +++++-- library/src/time.rs | 32 ++++++++++++++++++++++++++++++++ library/src/updater.rs | 6 +++++- 7 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 library/src/time.rs diff --git a/Cargo.lock b/Cargo.lock index 635a4bb..db42dae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -878,6 +878,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mock_instant" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcebb6db83796481097dedc7747809243cc81d9ed83e6a938b76d4ea0b249cf" + [[package]] name = "mockall" version = "0.12.1" @@ -1772,6 +1778,7 @@ dependencies = [ "libc", "log", "log-panics", + "mock_instant", "mockall", "mockito", "once_cell", diff --git a/library/Cargo.toml b/library/Cargo.toml index a45b6f0..5c43946 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -67,6 +67,7 @@ oslog = "0.2.0" [dev-dependencies] mockall = "0.12.1" mockito = "1.2.0" +mock_instant = "0.5.1" # Gives #[serial] attribute for locking all of our shorebird_init # tests to a single thread so they don't conflict with each other. serial_test = "2.0.0" diff --git a/library/src/events.rs b/library/src/events.rs index a562541..95a31ea 100644 --- a/library/src/events.rs +++ b/library/src/events.rs @@ -58,4 +58,7 @@ pub struct PatchEvent { /// The release version from AndroidManifest.xml, Info.plist in the app. pub release_version: String, + + /// When this event occurred as a Unix epoch timestamp in seconds. + pub timestamp: u64, } diff --git a/library/src/lib.rs b/library/src/lib.rs index c63d07d..30bb7d9 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -11,6 +11,7 @@ mod config; mod events; mod logging; mod network; +mod time; mod updater; mod updater_lock; mod yaml; diff --git a/library/src/network.rs b/library/src/network.rs index 5d9748e..b30e00c 100644 --- a/library/src/network.rs +++ b/library/src/network.rs @@ -258,7 +258,7 @@ pub fn download_to_path( #[cfg(test)] mod tests { - use crate::network::PatchCheckResponse; + use crate::{network::PatchCheckResponse, time}; use super::{patches_events_url, PatchEvent}; use crate::events::EventType; @@ -295,12 +295,13 @@ mod tests { platform: "platform".to_string(), release_version: "release_version".to_string(), identifier: EventType::PatchInstallSuccess, + timestamp: 1234, }; let request = super::CreatePatchEventRequest { event }; let json_string = serde_json::to_string(&request).unwrap(); assert_eq!( json_string, - r#"{"event":{"app_id":"app_id","arch":"arch","type":"__patch_install__","patch_number":1,"platform":"platform","release_version":"release_version"}}"# + r#"{"event":{"app_id":"app_id","arch":"arch","type":"__patch_install__","patch_number":1,"platform":"platform","release_version":"release_version","timestamp":1234}}"# ) } @@ -375,6 +376,7 @@ mod tests { platform: "platform".to_string(), release_version: "release_version".to_string(), identifier: EventType::PatchInstallSuccess, + timestamp: time::unix_timestamp(), }; let result = super::report_event_default( // Make the request to a non-existent URL, which will trigger the @@ -402,6 +404,7 @@ mod tests { platform: "platform".to_string(), release_version: "release_version".to_string(), identifier: EventType::PatchInstallSuccess, + timestamp: time::unix_timestamp(), }, }, ); diff --git a/library/src/time.rs b/library/src/time.rs new file mode 100644 index 0000000..e6e4e6c --- /dev/null +++ b/library/src/time.rs @@ -0,0 +1,32 @@ +#[cfg(test)] +use mock_instant::global::SystemTime; + +#[cfg(not(test))] +use std::time::SystemTime; + +/// The number of seconds since the Unix epoch. Returns 0 if the system clock is set before the +/// Unix epoch. +pub(crate) fn unix_timestamp() -> u64 { + match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + Ok(n) => n.as_secs(), + Err(_) => 0, + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use mock_instant::global::MockClock; + + #[test] + fn returns_duration_since_unix_epoch() { + MockClock::set_system_time(Duration::from_secs(123)); + assert_eq!(super::unix_timestamp(), 123); + } + + // Ideally, we'd be able to test the case where `duration_since` returns an error, but it + // seems to only happen when system time is set before the Unix epoch, which is not possible + // with the current implementation of `MockClock` because `set_system_time` expects a duration, + // and a duration cannot be negative. +} diff --git a/library/src/updater.rs b/library/src/updater.rs index 8623166..4ff8cf1 100644 --- a/library/src/updater.rs +++ b/library/src/updater.rs @@ -17,6 +17,7 @@ use crate::network::{ download_to_path, patches_check_url, send_patch_check_request, NetworkHooks, PatchCheckRequest, PatchCheckResponse, }; +use crate::time; use crate::updater_lock::{with_updater_thread_lock, UpdaterLockState}; use crate::yaml::YamlConfig; @@ -477,6 +478,7 @@ pub fn report_launch_failure() -> anyhow::Result<()> { patch_number: patch.number, platform: current_platform().to_string(), release_version: config.release_version.clone(), + timestamp: time::unix_timestamp(), }; // Queue the failure event for later sending since right after this // function returns the Flutter engine is likely to abort(). @@ -524,6 +526,7 @@ pub fn report_launch_success() -> anyhow::Result<()> { platform: current_platform().to_string(), release_version: config_copy.release_version.clone(), identifier: EventType::PatchInstallSuccess, + timestamp: time::unix_timestamp(), }; let report_result = crate::network::send_patch_event(event, &config_copy); if let Err(err) = report_result { @@ -561,7 +564,7 @@ mod tests { use crate::{ config::{testing_reset_config, with_config}, network::{testing_set_network_hooks, NetworkHooks, PatchCheckResponse}, - ExternalFileProvider, + time, ExternalFileProvider, }; #[derive(Debug, Clone)] @@ -867,6 +870,7 @@ mod tests { patch_number: 1, platform: current_platform().to_string(), release_version: config.release_version.clone(), + timestamp: time::unix_timestamp(), }; // Queue 5 events. assert!(state.queue_event(fail_event.clone()).is_ok());