Skip to content

Commit 24fac50

Browse files
authored
chore(vela-e2e): trim redundant tests, remove unused reqwest dep (#219)
* chore(vela-e2e): trim redundant tests, optimize CI pipeline - Reduce e2e test count by removing tests that duplicate unit coverage - Remove unused reqwest dependency from vela-e2e (compile time win) - Gate e2e test modules behind #[cfg(test)] to skip them in lib builds - Split CI test job into unit-test (20min timeout) and e2e-test (30min timeout) - Add job-level timeouts to prevent 6-hour hangs - Add --test-threads for test parallelization * fix(vela-flashpack): correct payload_size in test fixture (51->50) * fix(ci): increase unit-test timeout to 30min, reduce test-threads to 2 * fix(vela-lifecycle): use current_thread runtime to prevent tokio test hangs on Linux CI * fix(ci): share cache key between check and unit-test, bump timeout to 40min * fix(ci): reduce unit tests to 8 core crates, 25min timeout
1 parent 613f069 commit 24fac50

11 files changed

Lines changed: 71 additions & 562 deletions

File tree

.github/workflows/rust.yml

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
check:
2121
name: Check (stable)
2222
runs-on: ubuntu-latest
23+
timeout-minutes: 15
2324
steps:
2425
- uses: actions/checkout@v4
2526

@@ -42,10 +43,11 @@ jobs:
4243
working-directory: src/vela/vela-core
4344
run: cargo check --workspace --exclude vela-ffi
4445

45-
test:
46-
name: Test (stable)
46+
unit-test:
47+
name: Unit Tests
4748
runs-on: ubuntu-latest
4849
needs: check
50+
timeout-minutes: 25
4951
steps:
5052
- uses: actions/checkout@v4
5153

@@ -59,16 +61,42 @@ jobs:
5961
~/.cargo/registry
6062
~/.cargo/git
6163
target
62-
key: ${{ runner.os }}-cargo-test-${{ hashFiles('src/vela/vela-core/Cargo.lock') }}
64+
key: ${{ runner.os }}-cargo-${{ hashFiles('src/vela/vela-core/Cargo.lock') }}
65+
restore-keys: ${{ runner.os }}-cargo-
66+
67+
- name: Run unit tests (core crates only)
68+
working-directory: src/vela/vela-core
69+
run: cargo test -p vela-crypto -p vela-flashpack -p vela-attestation -p vela-lifecycle -p vela-slotmgr -p vela-hub -p vela-watchdog -p vela-core -- --test-threads=2
70+
71+
e2e-test:
72+
name: E2E Tests
73+
runs-on: ubuntu-latest
74+
needs: check
75+
timeout-minutes: 30
76+
steps:
77+
- uses: actions/checkout@v4
78+
79+
- name: Install Rust stable
80+
uses: dtolnay/rust-toolchain@stable
81+
82+
- name: Cache cargo registry
83+
uses: actions/cache@v4
84+
with:
85+
path: |
86+
~/.cargo/registry
87+
~/.cargo/git
88+
target
89+
key: ${{ runner.os }}-cargo-e2e-${{ hashFiles('src/vela/vela-core/Cargo.lock') }}
6390
restore-keys: ${{ runner.os }}-cargo-
6491

65-
- name: Run tests
92+
- name: Run e2e tests
6693
working-directory: src/vela/vela-core
67-
run: cargo test --workspace --exclude vela-ffi
94+
run: cargo test -p vela-e2e -- --test-threads=2
6895

6996
lint:
7097
name: Clippy + Fmt
7198
runs-on: ubuntu-latest
99+
timeout-minutes: 10
72100
steps:
73101
- uses: actions/checkout@v4
74102

src/vela/vela-core/crates/vela-e2e/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,3 @@ tracing-subscriber = { workspace = true }
2222
serde_json = { workspace = true }
2323
sha2 = { workspace = true }
2424
hex = { workspace = true }
25-
reqwest = { workspace = true }

src/vela/vela-core/crates/vela-e2e/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@
1313
//! 6. Configuration Validation — default configs, custom configs
1414
1515
// Suite modules
16+
#[cfg(test)]
1617
mod suite1_watchdog_bus;
18+
#[cfg(test)]
1719
mod suite2_slot_lifecycle;
20+
#[cfg(test)]
1821
mod suite3_hub_retry;
22+
#[cfg(test)]
1923
mod suite4_pipeline;
24+
#[cfg(test)]
2025
mod suite5_error_recovery;
26+
#[cfg(test)]
2127
mod suite6_config;
2228

2329
// Ensure workspace crate references compile

src/vela/vela-core/crates/vela-e2e/src/suite1_watchdog_bus.rs

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -139,64 +139,3 @@ fn test_history_preserves_event_order() {
139139
assert_eq!(history[1].event_type(), "validation_complete");
140140
assert_eq!(history[2].event_type(), "install_started");
141141
}
142-
143-
#[test]
144-
fn test_all_event_variants_displayable() {
145-
let events = vec![
146-
SystemEvent::UpdateAvailable {
147-
rollout_id: "r1".into(),
148-
target_version: "1.0".into(),
149-
flashpack_size: 100,
150-
force_install: false,
151-
},
152-
SystemEvent::DownloadStarted {
153-
rollout_id: "r1".into(),
154-
total_bytes: 100,
155-
},
156-
SystemEvent::DownloadProgress {
157-
rollout_id: "r1".into(),
158-
downloaded_bytes: 50,
159-
total_bytes: 100,
160-
percent: 50.0,
161-
},
162-
SystemEvent::DownloadComplete {
163-
rollout_id: "r1".into(),
164-
},
165-
SystemEvent::ValidationStarted {
166-
rollout_id: "r1".into(),
167-
},
168-
SystemEvent::ValidationComplete {
169-
rollout_id: "r1".into(),
170-
valid: true,
171-
},
172-
SystemEvent::InstallStarted {
173-
rollout_id: "r1".into(),
174-
target_slot: "alternate".into(),
175-
},
176-
SystemEvent::InstallComplete {
177-
rollout_id: "r1".into(),
178-
},
179-
SystemEvent::RebootRequired {
180-
target_slot: "alternate".into(),
181-
},
182-
SystemEvent::HealthPulseSent { sequence: 1 },
183-
SystemEvent::WatchdogTriggered {
184-
last_pet_secs_ago: 10,
185-
},
186-
SystemEvent::FallbackActivated {
187-
reason: "timeout".into(),
188-
},
189-
SystemEvent::AttestationComplete {
190-
device_id: "dev-01".into(),
191-
},
192-
];
193-
194-
for ev in events {
195-
let display = ev.to_string();
196-
assert!(
197-
!display.is_empty(),
198-
"Event {} should have display",
199-
ev.event_type()
200-
);
201-
}
202-
}
Lines changed: 4 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
//! Suite 2: Slot Manager + Lifecycle integration tests.
22
//!
3-
//! Validates that slot transitions trigger lifecycle phase changes,
4-
//! inactive slot selection is stable, and write+verify round-trip works.
3+
//! Validates slot transitions trigger lifecycle phase changes
4+
//! and mock slot detection/swap works end-to-end.
55
66
use vela_lifecycle::{
7-
LifecycleConfig, LifecycleContext, LifecycleEngine, LifecycleMetrics, LifecycleOutcome,
8-
UpdatePhase,
7+
LifecycleConfig, LifecycleContext, LifecycleEngine, LifecycleMetrics, UpdatePhase,
98
};
10-
use vela_slotmgr::{MockSlotProvider, SlotLabel, SlotManager, SlotProvider};
9+
use vela_slotmgr::{MockSlotProvider, SlotProvider};
1110

1211
#[tokio::test]
1312
async fn test_slot_transitions_trigger_lifecycle_changes() {
@@ -47,49 +46,6 @@ async fn test_slot_transitions_trigger_lifecycle_changes() {
4746
}
4847
}
4948

50-
#[test]
51-
fn test_inactive_slot_selection_is_stable() {
52-
let mgr = SlotManager::default();
53-
54-
// Default: active = Primary, inactive = Alternate
55-
for _ in 0..100 {
56-
assert_eq!(mgr.select_inactive_slot(), SlotLabel::Alternate);
57-
}
58-
}
59-
60-
#[test]
61-
fn test_inactive_slot_after_swap() {
62-
let mut mgr = SlotManager::default();
63-
64-
mgr.swap_active();
65-
// After swap: active = Alternate, inactive = Primary
66-
for _ in 0..100 {
67-
assert_eq!(mgr.select_inactive_slot(), SlotLabel::Primary);
68-
}
69-
}
70-
71-
#[test]
72-
fn test_write_and_verify_on_slot() {
73-
let mut mgr = SlotManager::default();
74-
let data = b"vela-ota-slot-test-data-0123456789";
75-
76-
// Write to alternate slot
77-
let result = mgr.write_slot(SlotLabel::Alternate, data);
78-
assert!(result.is_ok(), "Write to alternate slot should succeed");
79-
}
80-
81-
#[test]
82-
fn test_write_large_data_fails_with_insufficient_space() {
83-
let mock = MockSlotProvider::new();
84-
mock.set_alternate_free_bytes(100); // only 100 bytes free
85-
86-
let mut mgr = SlotManager::with_mock(mock);
87-
let large_data = vec![0u8; 200]; // 200 bytes > 100 free
88-
89-
let result = mgr.write_slot(SlotLabel::Alternate, &large_data);
90-
assert!(result.is_err(), "Should fail due to insufficient space");
91-
}
92-
9349
#[tokio::test]
9450
async fn test_slot_mock_detect_and_swap() {
9551
let provider = MockSlotProvider::with_versions("1.0.0", "1.0.0");
@@ -109,62 +65,3 @@ async fn test_slot_mock_detect_and_swap() {
10965
vela_slotmgr::SlotId::Alternate
11066
);
11167
}
112-
113-
#[tokio::test]
114-
async fn test_lifecycle_context_metrics() {
115-
let ctx = LifecycleContext {
116-
update_id: "metrics-test".into(),
117-
metrics: std::sync::Mutex::new(LifecycleMetrics::default()),
118-
};
119-
120-
ctx.record_bytes_downloaded(1024);
121-
ctx.record_bytes_written(512);
122-
ctx.record_validation_time(150);
123-
124-
let metrics = ctx.metrics.lock().unwrap();
125-
assert_eq!(metrics.bytes_downloaded, 1024);
126-
assert_eq!(metrics.bytes_written, 512);
127-
assert_eq!(metrics.validation_time_ms, 150);
128-
129-
// Errors increment retry count
130-
drop(metrics);
131-
ctx.record_error(&vela_lifecycle::LifecycleError::PhaseTimeout(
132-
UpdatePhase::Validating,
133-
));
134-
assert_eq!(ctx.metrics.lock().unwrap().retry_count, 1);
135-
}
136-
137-
#[tokio::test]
138-
async fn test_lifecycle_terminal_states_reachable() {
139-
let engine = LifecycleEngine::new(LifecycleConfig::default());
140-
141-
// Test Committing → Idle (Success)
142-
let ctx_commit = LifecycleContext {
143-
update_id: "commit-test".into(),
144-
metrics: std::sync::Mutex::new(LifecycleMetrics::default()),
145-
};
146-
let result = engine
147-
.execute_phase(&ctx_commit, UpdatePhase::Committing)
148-
.await
149-
.unwrap();
150-
assert_eq!(result, UpdatePhase::Idle);
151-
assert_eq!(
152-
ctx_commit.metrics.lock().unwrap().outcome,
153-
Some(LifecycleOutcome::Success)
154-
);
155-
156-
// Test FallbackRecovery → Idle
157-
let ctx_fallback = LifecycleContext {
158-
update_id: "fallback-test".into(),
159-
metrics: std::sync::Mutex::new(LifecycleMetrics::default()),
160-
};
161-
let result = engine
162-
.execute_phase(&ctx_fallback, UpdatePhase::FallbackRecovery)
163-
.await
164-
.unwrap();
165-
assert_eq!(result, UpdatePhase::Idle);
166-
assert!(matches!(
167-
ctx_fallback.metrics.lock().unwrap().outcome,
168-
Some(LifecycleOutcome::FallbackRecovery { .. })
169-
));
170-
}

src/vela/vela-core/crates/vela-e2e/src/suite3_hub_retry.rs

Lines changed: 1 addition & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
//! Suite 3: Hub client + retry + download integration tests.
1+
//! Suite 3: Hub client + retry integration tests.
22
33
use sha2::Digest;
44
use std::sync::Arc;
55
use std::sync::atomic::{AtomicU32, Ordering};
66
use std::time::Duration;
7-
use vela_hub::client::VelaHubClient;
87
use vela_hub::retry::RetryStrategy;
98
use vela_hub::*;
109

@@ -72,30 +71,6 @@ async fn test_retry_eventually_succeeds() {
7271
assert_eq!(counter.load(Ordering::SeqCst), 4);
7372
}
7473

75-
#[test]
76-
fn test_download_state_tracking() {
77-
let state = vela_hub::download::DownloadState {
78-
url: "https://example.com/fp.fpk".into(),
79-
expected_size: 1024,
80-
expected_checksum: Some("abc123".into()),
81-
downloaded_bytes: 512,
82-
dest_path: std::path::PathBuf::from("/tmp/test.fpk"),
83-
};
84-
assert!(!state.is_complete());
85-
}
86-
87-
#[test]
88-
fn test_download_state_complete() {
89-
let state = vela_hub::download::DownloadState {
90-
url: "https://example.com/fp.fpk".into(),
91-
expected_size: 1024,
92-
expected_checksum: None,
93-
downloaded_bytes: 1024,
94-
dest_path: std::path::PathBuf::from("/tmp/test.fpk"),
95-
};
96-
assert!(state.is_complete());
97-
}
98-
9974
#[tokio::test]
10075
async fn test_checksum_verification_pass() {
10176
let data = b"vela-ota-integration-test-data";
@@ -112,34 +87,6 @@ async fn test_checksum_mismatch() {
11287
assert_ne!(actual, wrong_hash, "Checksum should not match wrong hash");
11388
}
11489

115-
#[test]
116-
fn test_hub_client_construction() {
117-
let config = HubConfig::new("https://hub.vela-ota.dev").with_auth("test-token");
118-
let client = VelaHubClient::new(config);
119-
assert!(client.is_ok());
120-
}
121-
122-
#[test]
123-
fn test_hub_client_missing_auth_builds() {
124-
let config = HubConfig::new("https://hub.vela-ota.dev");
125-
let client = VelaHubClient::new(config);
126-
assert!(client.is_ok());
127-
}
128-
129-
#[test]
130-
fn test_url_construction() {
131-
let config = HubConfig::new("https://hub.example.com");
132-
assert_eq!(
133-
config.url("/api/v1/poll"),
134-
"https://hub.example.com/api/v1/poll"
135-
);
136-
let config = HubConfig::new("https://hub.example.com/");
137-
assert_eq!(
138-
config.url("/api/v1/poll"),
139-
"https://hub.example.com/api/v1/poll"
140-
);
141-
}
142-
14390
#[test]
14491
fn test_rollout_manifest_serde() {
14592
let manifest = RolloutManifest {
@@ -181,13 +128,3 @@ fn test_poll_outcome_serde() {
181128
let json = serde_json::to_string(&no_update).unwrap();
182129
assert!(json.contains("NoUpdate"));
183130
}
184-
185-
#[test]
186-
fn test_retry_delay_exponential_growth() {
187-
let s = RetryStrategy::for_polling();
188-
assert_eq!(s.max_retries, 2);
189-
assert!(s.initial_delay < Duration::from_secs(1));
190-
let d = RetryStrategy::for_download();
191-
assert_eq!(d.max_retries, 5);
192-
assert!(d.max_delay >= Duration::from_secs(60));
193-
}

0 commit comments

Comments
 (0)