- version 0.8.1 -> 0.8.2 (patch: behavior-only bug fix in the local
feature's PTY waiter from db0b1fc; ADR-004 decision 1's negative-code
convention now holds on the PTY path; no API, wire-format, or
dependency change)
- CHANGELOG entry for 0.8.2 (review #004 resolution: the waitpid reap
mechanism, the signal round-trip notes, the tests, the docs)
- finish the em-dash sweep in prose 58da42a missed: doc comments in
tests/ (negotiation, pipe, pty, common) and fuzz/shared
(control_json, negotiation_frame, session_opseq); prose rewrites
only, no code, JSON literals, or wire shapes touched
- README checked: no version pins, nothing to update
Verification: cargo test; cargo test --all-features; cargo clippy
--all-targets -D warnings (default and --all-features); cargo fmt
--check; cargo doc --no-deps; fuzz/shared corpus replay (9 seeds pass);
cargo check + clippy for wasm32-unknown-unknown -D warnings; cargo
publish --dry-run --allow-dirty (43 files, 194.4 KiB); detached
campaigns over the four targets (chunk_frame 632s, negotiation_frame
602s, control_json 610s, session_opseq 608s) all exiting 0 with
0 crash/oom/timeout findings.
117 lines
4.2 KiB
Rust
117 lines
4.2 KiB
Rust
#![cfg(feature = "local")]
|
||
|
||
//! Negotiation error tests: scenarios 14–18 of
|
||
//! `tasks/tty/integration-test.md`.
|
||
//!
|
||
//! 14. unknown_backend: error response, first byte `0x00`
|
||
//! 15. malformed_negotiation (bad JSON): error response
|
||
//! 16. malformed_negotiation (carriage != raw): error response
|
||
//! 17. malformed_negotiation (empty cmd): error response
|
||
//! 18. allocate_failed (nonexistent binary): error response
|
||
//!
|
||
//! The test identity carries the `tty:open` scope, so the
|
||
//! `unknown_backend` test reaches the backend lookup; the scope-gate
|
||
//! is exercised in the adapter's unit tests. These tests assert the
|
||
//! server sends a length-prefixed JSON error frame (Phase 1 framing,
|
||
//! ADR-052) and closes the stream without entering raw mode.
|
||
|
||
mod common;
|
||
|
||
use std::sync::Arc;
|
||
|
||
use alktty::local::LocalTtyBackend;
|
||
use common::{negotiate_pipe_json, spawn_session};
|
||
|
||
/// 14. unknown_backend: negotiate `{backend:"kubernetes",...}`, assert
|
||
/// the error response `{"error":"unknown_backend","backend":"kubernetes"}`,
|
||
/// the first byte of the error frame is `0x00`, and the stream closes.
|
||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||
async fn unknown_backend_error() {
|
||
let backend = Arc::new(LocalTtyBackend::new());
|
||
let (mut client, server) = spawn_session("local", backend);
|
||
client
|
||
.write_negotiation(
|
||
r#"{"carriage":"raw","backend":"kubernetes","tty":null,"cmd":["echo","hi"]}"#,
|
||
)
|
||
.await;
|
||
|
||
let err = client.read_error_frame().await;
|
||
assert_eq!(err["error"], "unknown_backend");
|
||
assert_eq!(err["backend"], "kubernetes");
|
||
|
||
client.assert_no_more_chunks().await;
|
||
let _ = server.await;
|
||
}
|
||
|
||
/// 15. malformed_negotiation (bad JSON): write garbage bytes as the
|
||
/// negotiation frame, assert `malformed_negotiation` error response.
|
||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||
async fn malformed_negotiation_bad_json() {
|
||
let backend = Arc::new(LocalTtyBackend::new());
|
||
let (mut client, server) = spawn_session("local", backend);
|
||
client.write_negotiation("not valid json").await;
|
||
|
||
let err = client.read_error_frame().await;
|
||
assert_eq!(err["error"], "malformed_negotiation");
|
||
|
||
client.assert_no_more_chunks().await;
|
||
let _ = server.await;
|
||
}
|
||
|
||
/// 16. malformed_negotiation (carriage != raw): negotiate
|
||
/// `{carriage:"json",...}`, assert `malformed_negotiation`.
|
||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||
async fn malformed_negotiation_carriage_not_raw() {
|
||
let backend = Arc::new(LocalTtyBackend::new());
|
||
let (mut client, server) = spawn_session("local", backend);
|
||
client
|
||
.write_negotiation(
|
||
r#"{"carriage":"json","backend":"local","tty":null,"cmd":["echo","hi"]}"#,
|
||
)
|
||
.await;
|
||
|
||
let err = client.read_error_frame().await;
|
||
assert_eq!(err["error"], "malformed_negotiation");
|
||
|
||
client.assert_no_more_chunks().await;
|
||
let _ = server.await;
|
||
}
|
||
|
||
/// 17. malformed_negotiation (empty cmd): negotiate `{cmd:[]}`, assert
|
||
/// `malformed_negotiation`.
|
||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||
async fn malformed_negotiation_empty_cmd() {
|
||
let backend = Arc::new(LocalTtyBackend::new());
|
||
let (mut client, server) = spawn_session("local", backend);
|
||
client
|
||
.write_negotiation(r#"{"carriage":"raw","backend":"local","tty":null,"cmd":[]}"#)
|
||
.await;
|
||
|
||
let err = client.read_error_frame().await;
|
||
assert_eq!(err["error"], "malformed_negotiation");
|
||
|
||
client.assert_no_more_chunks().await;
|
||
let _ = server.await;
|
||
}
|
||
|
||
/// 18. allocate_failed (nonexistent binary): negotiate with a
|
||
/// nonexistent command path, assert `allocate_failed` (the spawn
|
||
/// fails). The adapter sends the error response in negotiation
|
||
/// framing and closes.
|
||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||
async fn allocate_failed_nonexistent_binary() {
|
||
let backend = Arc::new(LocalTtyBackend::new());
|
||
let (mut client, server) = spawn_session("local", backend);
|
||
client
|
||
.write_negotiation(
|
||
negotiate_pipe_json("local", &["/nonexistent/binary/that/does/not/exist"]).as_str(),
|
||
)
|
||
.await;
|
||
|
||
let err = client.read_error_frame().await;
|
||
assert_eq!(err["error"], "allocate_failed");
|
||
|
||
client.assert_no_more_chunks().await;
|
||
let _ = server.await;
|
||
}
|