Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4398,6 +4398,151 @@ mod tests {
.expect_err("writes are refused during maintenance");
}

/// Enter a single-flight install already under store/PTY maintenance.
async fn begin_install_under_maintenance<'a>(
state: &'a crate::update_cancel::UpdateCancel,
pty: &PtyManager,
store: &super::Store,
Comment on lines +4401 to +4405

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Halte main.rs unter der Nahtstellen-Baseline

Auf diesem Commit ergibt git show 70c914c:src-tauri/src/main.rs | wc -l 5.137 Zeilen. Das Z3-Abnahmekriterium in docs/PLAN.md:260-263 verlangt für diese Naht ausdrücklich < 5.133; der Testnachtrag überschreitet die Grenze somit um fünf Zeilen und verhindert, dass Z3 trotz grüner Tests als fertig gelten kann. Bitte mindestens fünf Zeilen deduplizieren oder kompaktieren.

AGENTS.md reference: AGENTS.md:L31-L32

Useful? React with 👍 / 👎.

) -> crate::update_cancel::Install<'a> {
crate::update_cancel::enter_or_abort(
state.begin().expect("begin install"),
super::enter_database_maintenance(pty, store, Duration::from_secs(1)),
)
.await
.expect("enter maintenance through cancel glue")
}

/// R821-M2: TooLate must keep the real store/PTY freeze, not only the
/// single-flight slot (`cancel_after_installer_started_is_too_late_and_keeps_freeze`).
#[tokio::test]
async fn too_late_after_installer_started_keeps_maintenance_freeze() {
use crate::update_cancel::{self, CancelResult};
use std::future::ready;

let (_dir, store) = seam_fixture("too-late-real-freeze").await;
let pty = PtyManager::default();
let state = update_cancel::UpdateCancel::default();
let install = begin_install_under_maintenance(&state, &pty, &store).await;
install
.download(ready(Ok(())))
.await
.expect("download marks Installing");
assert_eq!(
state.cancel().await.expect("cancel while Installing"),
CancelResult::TooLate
);
update_cancel::finish_flight(
install,
Ok(()),
true,
super::leave_database_maintenance(&pty, &store),
)
.await
.expect("installer-started finish keeps the freeze");
assert!(
store.is_maintenance_active(),
"store freeze must survive TooLate"
);
assert!(
pty.reserve_session().is_err(),
"PTY launches must stay refused after TooLate"
);
assert!(
pty.install_when_idle(|| Ok(())).is_err(),
"install_when_idle must stay refused after TooLate"
);
tokio::time::timeout(Duration::from_secs(8), store.create_project("frozen", "/f"))
.await
.expect("bounded wait")
.expect_err("writes must stay refused after TooLate");
let begin_err = state
.begin()
.map(|_| ())
.expect_err("single-flight slot must stay occupied after TooLate");
assert!(
begin_err.starts_with(crate::errors::ERR_REFUSED),
"{begin_err}"
);
}

/// Negative control for R821-M2: a body error before the installer starts
/// must fully thaw store/PTY maintenance and free the single-flight slot.
#[tokio::test]
async fn body_error_before_installer_started_releases_maintenance_freeze() {
use crate::update_cancel;

let (_dir, store) = seam_fixture("pre-installer-thaw").await;
let pty = PtyManager::default();
let state = update_cancel::UpdateCancel::default();
let install = begin_install_under_maintenance(&state, &pty, &store).await;
assert!(store.is_maintenance_active());
let err = update_cancel::finish_flight(
install,
Err("download failed".into()),
false,
super::leave_database_maintenance(&pty, &store),
)
.await
.expect_err("body error before installer must surface");
assert!(
err.contains("download failed"),
"surfaced error must carry the body text: {err}"
);
assert!(
!store.is_maintenance_active(),
"freeze must release when the installer never started"
);
let id = pty
.reserve_session()
.expect("PTY launches must work again after thaw");
pty.cancel_reservation(&id);
tokio::time::timeout(Duration::from_secs(8), store.create_project("after", "/a"))
.await
.expect("bounded wait")
.expect("writes must work again after thaw");
state
.begin()
.expect("single-flight slot must be free after thaw")
.finish_unstarted()
.expect("release probe install");
}

/// Off-diagonal: body error with installer_started=true keeps freeze and slot
/// (finish_flight only thaws when both body fails and installer never started).
#[tokio::test]
async fn body_error_after_installer_started_keeps_maintenance_freeze() {
use crate::update_cancel;

let (_dir, store) = seam_fixture("post-installer-body-err").await;
let pty = PtyManager::default();
let state = update_cancel::UpdateCancel::default();
let install = begin_install_under_maintenance(&state, &pty, &store).await;
let err = update_cancel::finish_flight(
install,
Err("download failed".into()),
true,
super::leave_database_maintenance(&pty, &store),
)
.await
.expect_err("body error must surface even when installer started");
assert!(
err.contains("download failed"),
"surfaced error must carry the body text: {err}"
);
assert!(
store.is_maintenance_active(),
"freeze must stay when installer_started is true"
);
assert!(
pty.reserve_session().is_err(),
"PTY launches must stay refused after installer-started body error"
);
assert!(
state.begin().is_err(),
"single-flight slot must stay occupied after installer-started body error"
);
}

#[tokio::test]
async fn leaving_maintenance_restores_launches_and_writes() {
let (_dir, store) = seam_fixture("maintenance-leave").await;
Expand Down
Loading