test: harden panel e2e harness
This commit is contained in:
@@ -4,6 +4,10 @@ version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
e2e-test = []
|
||||
|
||||
[dependencies]
|
||||
client = { workspace = true }
|
||||
protocol = { workspace = true }
|
||||
|
||||
@@ -1,57 +1,89 @@
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
#[cfg(feature = "e2e-test")]
|
||||
mod imp {
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use serde::Serialize;
|
||||
use serde::Serialize;
|
||||
|
||||
const EVENT_PATH_ENV: &str = "YOI_TUI_TEST_EVENTS";
|
||||
const EVENT_PATH_ENV: &str = "YOI_TUI_TEST_EVENTS";
|
||||
const HOLD_BACKGROUND_TASK_ENV: &str = "YOI_TUI_TEST_HOLD_BACKGROUND_TASK";
|
||||
|
||||
static EVENT_WRITER: OnceLock<Option<Mutex<File>>> = OnceLock::new();
|
||||
static EVENT_WRITER: OnceLock<Option<Mutex<File>>> = OnceLock::new();
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct EventEnvelope<'a, T> {
|
||||
ts_ms: u128,
|
||||
surface: &'a str,
|
||||
event: &'a str,
|
||||
data: T,
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
struct EventEnvelope<'a, T> {
|
||||
ts_ms: u128,
|
||||
surface: &'a str,
|
||||
event: &'a str,
|
||||
data: T,
|
||||
}
|
||||
|
||||
pub(crate) fn emit<T>(surface: &'static str, event: &'static str, data: T)
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let Some(writer) = EVENT_WRITER.get_or_init(open_event_writer).as_ref() else {
|
||||
return;
|
||||
};
|
||||
let Ok(mut writer) = writer.lock() else {
|
||||
return;
|
||||
};
|
||||
let envelope = EventEnvelope {
|
||||
ts_ms: SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_millis())
|
||||
.unwrap_or_default(),
|
||||
surface,
|
||||
event,
|
||||
data,
|
||||
};
|
||||
if serde_json::to_writer(&mut *writer, &envelope).is_ok() {
|
||||
let _ = writer.write_all(b"\n");
|
||||
let _ = writer.flush();
|
||||
pub(crate) fn emit<T>(surface: &'static str, event: &'static str, data: T)
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let Some(writer) = EVENT_WRITER.get_or_init(open_event_writer).as_ref() else {
|
||||
return;
|
||||
};
|
||||
let Ok(mut writer) = writer.lock() else {
|
||||
return;
|
||||
};
|
||||
let envelope = EventEnvelope {
|
||||
ts_ms: SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_millis())
|
||||
.unwrap_or_default(),
|
||||
surface,
|
||||
event,
|
||||
data,
|
||||
};
|
||||
if serde_json::to_writer(&mut *writer, &envelope).is_ok() {
|
||||
let _ = writer.write_all(b"\n");
|
||||
let _ = writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn hold_background_task_if_requested(task: &'static str) {
|
||||
let requested = std::env::var(HOLD_BACKGROUND_TASK_ENV).unwrap_or_default();
|
||||
if !requested
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.any(|requested| requested == task)
|
||||
{
|
||||
return;
|
||||
}
|
||||
emit(
|
||||
"panel",
|
||||
"background_task_hold_started",
|
||||
serde_json::json!({ "task": task }),
|
||||
);
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_millis(25)).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn open_event_writer() -> Option<Mutex<File>> {
|
||||
let path = std::env::var_os(EVENT_PATH_ENV).map(PathBuf::from)?;
|
||||
if let Some(parent) = path.parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
}
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(path)
|
||||
.ok()
|
||||
.map(Mutex::new)
|
||||
}
|
||||
}
|
||||
|
||||
fn open_event_writer() -> Option<Mutex<File>> {
|
||||
let path = std::env::var_os(EVENT_PATH_ENV).map(PathBuf::from)?;
|
||||
if let Some(parent) = path.parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
}
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(path)
|
||||
.ok()
|
||||
.map(Mutex::new)
|
||||
}
|
||||
#[cfg(feature = "e2e-test")]
|
||||
pub(crate) use imp::{emit, hold_background_task_if_requested};
|
||||
|
||||
#[cfg(not(feature = "e2e-test"))]
|
||||
pub(crate) fn emit<T>(_surface: &'static str, _event: &'static str, _data: T) {}
|
||||
|
||||
#[cfg(not(feature = "e2e-test"))]
|
||||
pub(crate) async fn hold_background_task_if_requested(_task: &'static str) {}
|
||||
|
||||
@@ -264,6 +264,7 @@ impl PendingReload {
|
||||
}),
|
||||
);
|
||||
self.handle = Some(tokio::spawn(async move {
|
||||
crate::e2e_observer::hold_background_task_if_requested("reload").await;
|
||||
load_multi_pod_snapshot(None, lifecycle_mode).await
|
||||
}));
|
||||
true
|
||||
|
||||
@@ -4,6 +4,10 @@ version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
e2e-test = ["tui/e2e-test"]
|
||||
|
||||
[dependencies]
|
||||
project-record = { workspace = true }
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
||||
|
||||
Reference in New Issue
Block a user