Files
alktty/tests/negotiation.rs
T
glm-5.3-flash a701247ca6 chore: bump to 0.8.2, changelog for the PTY exit-signal fidelity fix
- 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.
2026-11-02 08:27:32 +00:00

117 lines
4.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#![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;
}