fix: align tui status and list viewports

This commit is contained in:
2026-09-30 10:51:35 +09:00
parent db2a51c824
commit de82b66de4
4 changed files with 265 additions and 34 deletions
+83 -1
View File
@@ -8,7 +8,7 @@ use wip_fs_server::{ServerConfig, WipFsServer};
use wip_https::encode_protocol_error_response;
use wip_https::http::{Request, Response};
use wip_protocol::{ProtocolError, ProtocolErrorCode, ProtocolInteraction};
use wip_tui::app::{Focus, HistoryKind, InputKey, Modal, ViewState};
use wip_tui::app::{ConnectionState, Focus, HistoryKind, InputKey, Modal, ViewState};
use wip_tui::config::Config;
use wip_tui::runtime::{
Runtime, Transport, TransportError, TransportErrorKind, default_wire_limits,
@@ -20,7 +20,9 @@ struct ScriptedTransport {
delayed_after_startup: bool,
unknown_calls: bool,
fail_call_prepare: bool,
fail_tree_prepare: bool,
panic_calls: bool,
root_tree_protocol_failures: Arc<AtomicUsize>,
}
impl ScriptedTransport {
@@ -31,7 +33,9 @@ impl ScriptedTransport {
delayed_after_startup: false,
unknown_calls: false,
fail_call_prepare: false,
fail_tree_prepare: false,
panic_calls: false,
root_tree_protocol_failures: Arc::new(AtomicUsize::new(0)),
}
}
}
@@ -46,11 +50,35 @@ impl Transport for ScriptedTransport {
detail: "scripted local conversion failure".into(),
});
}
if self.fail_tree_prepare && request.uri().path().ends_with("/v1/fetch_tree") {
return Err(TransportError {
kind: TransportErrorKind::Disconnect,
detail: "scripted tree conversion failure".into(),
});
}
Ok(request)
}
fn execute(&self, request: Self::Prepared) -> Result<Response<Vec<u8>>, TransportError> {
let sequence = self.sequence.fetch_add(1, Ordering::AcqRel);
if request.uri().path().ends_with("/v1/fetch_tree")
&& self
.root_tree_protocol_failures
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |remaining| {
remaining.checked_sub(1)
})
.is_ok()
{
return Ok(encode_protocol_error_response(
ProtocolInteraction::FetchTree,
&ProtocolError {
code: ProtocolErrorCode::Internal,
message: "scripted startup failure".into(),
},
default_wire_limits(),
)
.expect("encode tree failure"));
}
if self.delayed_after_startup && sequence > 0 {
// Each refresh starts object then tree. Make the first pair lose to
// the second pair to exercise exact RequestId ownership.
@@ -123,6 +151,56 @@ fn out_of_order_refresh_and_cancel_keep_request_identity_safe() {
assert!(runtime.app().operation_rows().is_empty());
}
#[test]
fn startup_transport_preparation_failure_enters_failed_state() {
let temporary = tempdir().expect("temporary root");
let server = Arc::new(
WipFsServer::new(ServerConfig::for_root(temporary.path())).expect("filesystem server"),
);
let mut transport = ScriptedTransport::new(server);
transport.fail_tree_prepare = true;
let config = config();
let mut runtime = Runtime::new(&config, transport).expect("runtime");
runtime.connect();
settle(&mut runtime);
assert!(matches!(
runtime.app().connection,
ConnectionState::Failed(ref detail) if detail.contains("scripted tree conversion failure")
));
assert!(runtime.app().status.contains("press R to reconnect"));
assert!(runtime.app().in_flight.is_empty());
}
#[test]
fn startup_protocol_failure_enters_failed_state_and_reconnects() {
let temporary = tempdir().expect("temporary root");
let server = Arc::new(
WipFsServer::new(ServerConfig::for_root(temporary.path())).expect("filesystem server"),
);
let transport = ScriptedTransport::new(server);
transport
.root_tree_protocol_failures
.store(1, Ordering::Release);
let config = config();
let mut runtime = Runtime::new(&config, transport).expect("runtime");
runtime.connect();
settle(&mut runtime);
assert!(matches!(
runtime.app().connection,
ConnectionState::Failed(ref detail) if detail.contains("scripted startup failure")
));
assert!(runtime.app().status.contains("press R to reconnect"));
assert!(runtime.app().history.iter().any(|item| {
item.kind == HistoryKind::DomainFailure && item.detail.contains("scripted startup failure")
}));
runtime.handle_key(InputKey::Reconnect);
settle(&mut runtime);
assert_eq!(runtime.app().connection, ConnectionState::Connected);
}
#[test]
fn local_transport_preparation_failure_is_definitely_not_dispatched() {
let temporary = tempdir().expect("temporary root");
@@ -150,6 +228,10 @@ fn local_transport_preparation_failure_is_definitely_not_dispatched() {
assert!(runtime.app().in_flight.is_empty());
assert_eq!(runtime.app().history[0].kind, HistoryKind::TransportFailure);
assert!(runtime.app().history[0].detail.contains("not dispatched"));
assert_eq!(
runtime.app().status,
"operation not dispatched; not executed"
);
assert_eq!(runtime.app().entries["/file"].state, ViewState::Fresh);
}