#![cfg(feature = "http")] //! Wire budgets are adapter-only and failed encoding must not acquire state. use std::collections::BTreeMap; use wip_client::{Client, ClientErrorKind, ClientLimits, ObservationState, SecurityContext}; use wip_http::{Limits, encode_fetch_interface_response, encode_observe_response}; use wip_protocol::{ FetchInterfaceRequest, FetchInterfaceResponse, INTERFACE_FORMAT_V1, InterfaceDescriptor, InterfaceReference, Object, ObjectObservation, ObserveRequest, OperationDeclaration, ReturnDeclaration, TypeExpr, }; fn state_limits() -> ClientLimits { ClientLimits::new(2, 8, 8, 8, 8).unwrap() } #[test] fn request_encoding_failure_leaves_retrieval_cache_and_inflight_unchanged() { let mut client = Client::new(state_limits(), Limits::new(1, 4096, 32).unwrap()); let session = client .open_session("https://example.test/wip", SecurityContext::new("alice")) .unwrap(); assert_eq!( client.refresh_object(&session, "/item").unwrap_err().kind, ClientErrorKind::InvalidRequest ); let reference = InterfaceReference { scope: "/".into(), name: "test".into(), }; assert_eq!( client .prepare_interface(&session, reference.clone()) .unwrap_err() .kind, ClientErrorKind::InvalidRequest ); assert!(client.object(&session, "/item").is_none()); assert!(client.interface(&session, &reference).is_none()); assert_eq!(client.in_flight_len(), 0); } #[test] fn call_encoding_failure_keeps_fresh_observations_and_no_pending_call() { let limits = Limits::new(80, 4096, 32).unwrap(); let mut client = Client::new(state_limits(), limits); let session = client .open_session("https://example.test/wip", SecurityContext::new("alice")) .unwrap(); let reference = InterfaceReference { scope: "/".into(), name: "test".into(), }; let observe = client.refresh_object(&session, "/item").unwrap(); client .complete( observe.id, encode_observe_response( &ObserveRequest { path: "/item".into(), depth: 0, }, &ObjectObservation { object: Object { name: "item".into(), description: None, interfaces: vec![reference.clone()], r#ref: None, validator: None, }, children: None, }, limits, ) .unwrap(), ) .unwrap(); let fetch = client .prepare_interface(&session, reference.clone()) .unwrap(); client .complete( fetch.id, encode_fetch_interface_response( &FetchInterfaceRequest { interface: reference.clone(), }, &FetchInterfaceResponse { interface: reference.clone(), scope_ref: None, validator: None, descriptor: InterfaceDescriptor { format: INTERFACE_FORMAT_V1.into(), documentation: None, types: vec![], operations: vec![OperationDeclaration { name: "run".into(), documentation: None, parameters: vec![], returns: ReturnDeclaration { documentation: None, r#type: TypeExpr::Integer, }, }], }, }, limits, ) .unwrap(), ) .unwrap(); assert_eq!( client .prepare_call(&session, "/item", &reference, "run", BTreeMap::new()) .unwrap_err() .kind, ClientErrorKind::InvalidRequest ); assert_eq!( client.object(&session, "/item").unwrap().state, ObservationState::Fresh ); assert_eq!( client.interface(&session, &reference).unwrap().state, ObservationState::Fresh ); assert!(client.call_history(&session).unwrap().is_empty()); assert_eq!(client.in_flight_len(), 0); }