Skip to content

Commit 91d8c6e

Browse files
committed
Tests for commander.
1 parent baf62f7 commit 91d8c6e

File tree

2 files changed

+269
-0
lines changed

2 files changed

+269
-0
lines changed

server/swimos_agent/src/commander/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#[cfg(test)]
16+
mod tests;
17+
1518
use std::{
1619
hash::{Hash, Hasher},
1720
marker::PhantomData,
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
// Copyright 2015-2024 Swim Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use std::{cell::RefCell, collections::HashMap};
16+
17+
use bytes::BytesMut;
18+
use swimos_agent_protocol::{encoding::ad_hoc::CommandMessageDecoder, CommandMessage};
19+
use swimos_api::{
20+
address::Address,
21+
agent::{AgentConfig, WarpLaneKind},
22+
error::{CommanderRegistrationError, DynamicRegistrationError},
23+
};
24+
use swimos_model::{Text, Value};
25+
use swimos_utilities::routing::RouteUri;
26+
use tokio_util::codec::Decoder;
27+
28+
use crate::{
29+
agent_model::downlink::BoxDownlinkChannelFactory,
30+
commander::Commander,
31+
event_handler::{
32+
ActionContext, DownlinkSpawnOnDone, EventHandlerError, HandlerAction, HandlerFuture,
33+
LaneSpawnOnDone, LaneSpawner, LinkSpawner, Spawner, StepResult,
34+
},
35+
AgentMetadata,
36+
};
37+
38+
use super::RegisterCommander;
39+
40+
struct FakeAgent;
41+
42+
struct TestSpawner {
43+
inner: RefCell<HashMap<Address<Text>, Result<u16, CommanderRegistrationError>>>,
44+
}
45+
46+
impl TestSpawner {
47+
pub fn new(address: Address<Text>, result: Result<u16, CommanderRegistrationError>) -> Self {
48+
TestSpawner {
49+
inner: RefCell::new([(address, result)].into_iter().collect()),
50+
}
51+
}
52+
}
53+
54+
impl LinkSpawner<FakeAgent> for TestSpawner {
55+
fn spawn_downlink(
56+
&self,
57+
_path: Address<Text>,
58+
_make_channel: BoxDownlinkChannelFactory<FakeAgent>,
59+
_on_done: DownlinkSpawnOnDone<FakeAgent>,
60+
) {
61+
panic!("Downlinks not supported.");
62+
}
63+
64+
fn register_commander(&self, path: Address<Text>) -> Result<u16, CommanderRegistrationError> {
65+
self.inner
66+
.borrow_mut()
67+
.remove(&path)
68+
.unwrap_or(Err(CommanderRegistrationError::CommanderIdOverflow))
69+
}
70+
}
71+
72+
impl LaneSpawner<FakeAgent> for TestSpawner {
73+
fn spawn_warp_lane(
74+
&self,
75+
_name: &str,
76+
_kind: WarpLaneKind,
77+
_on_done: LaneSpawnOnDone<FakeAgent>,
78+
) -> Result<(), DynamicRegistrationError> {
79+
panic!("Dynamic lanes not supported.");
80+
}
81+
}
82+
83+
impl Spawner<FakeAgent> for TestSpawner {
84+
fn spawn_suspend(&self, _fut: HandlerFuture<FakeAgent>) {
85+
panic!("Spawning futures not supported.");
86+
}
87+
}
88+
89+
const CONFIG: AgentConfig = AgentConfig::DEFAULT;
90+
const NODE_URI: &str = "/node";
91+
92+
fn make_uri() -> RouteUri {
93+
RouteUri::try_from(NODE_URI).expect("Bad URI.")
94+
}
95+
96+
fn make_meta<'a>(
97+
uri: &'a RouteUri,
98+
route_params: &'a HashMap<String, String>,
99+
) -> AgentMetadata<'a> {
100+
AgentMetadata::new(uri, route_params, &CONFIG)
101+
}
102+
103+
fn run_handler<H>(
104+
mut handler: H,
105+
spawner: &TestSpawner,
106+
buffer: &mut BytesMut,
107+
agent: &FakeAgent,
108+
meta: AgentMetadata<'_>,
109+
) -> Result<H::Completion, EventHandlerError>
110+
where
111+
H: HandlerAction<FakeAgent>,
112+
{
113+
let mut join_lane_init = HashMap::new();
114+
loop {
115+
let mut action_context =
116+
ActionContext::new(spawner, spawner, spawner, &mut join_lane_init, buffer);
117+
match handler.step(&mut action_context, meta, agent) {
118+
StepResult::Continue { modified_item } => {
119+
assert!(modified_item.is_none());
120+
}
121+
StepResult::Fail(err) => {
122+
assert!(join_lane_init.is_empty());
123+
break Err(err);
124+
}
125+
StepResult::Complete {
126+
modified_item,
127+
result,
128+
} => {
129+
assert!(modified_item.is_none());
130+
assert!(join_lane_init.is_empty());
131+
break Ok(result);
132+
}
133+
}
134+
}
135+
}
136+
137+
#[test]
138+
fn create_commander() {
139+
let address = Address::new(
140+
Some(Text::new("ws://remote:8080")),
141+
Text::new("/target"),
142+
Text::new("lane"),
143+
);
144+
let spawner = TestSpawner::new(address.clone(), Ok(7));
145+
let handler = RegisterCommander::new(address.clone());
146+
let mut buffer = BytesMut::new();
147+
let agent = FakeAgent;
148+
let uri = make_uri();
149+
let route_params = HashMap::new();
150+
let meta = make_meta(&uri, &route_params);
151+
152+
let commander =
153+
run_handler(handler, &spawner, &mut buffer, &agent, meta).expect("Failed unexpectedly.");
154+
155+
assert_eq!(commander.id, 7);
156+
157+
let mut decoder = CommandMessageDecoder::<Text, String>::default();
158+
159+
let message = decoder
160+
.decode_eof(&mut buffer)
161+
.expect("Invalid buffer contents.")
162+
.expect("No message.");
163+
164+
assert_eq!(message, CommandMessage::Register { address, id: 7 });
165+
}
166+
167+
#[test]
168+
fn fail_create_commander() {
169+
let address = Address::new(
170+
Some(Text::new("ws://remote:8080")),
171+
Text::new("/target"),
172+
Text::new("lane"),
173+
);
174+
let spawner = TestSpawner::new(
175+
address.clone(),
176+
Err(CommanderRegistrationError::CommanderIdOverflow),
177+
);
178+
let handler = RegisterCommander::new(address.clone());
179+
let mut buffer = BytesMut::new();
180+
let agent = FakeAgent;
181+
let uri = make_uri();
182+
let route_params = HashMap::new();
183+
let meta = make_meta(&uri, &route_params);
184+
185+
let error =
186+
run_handler(handler, &spawner, &mut buffer, &agent, meta).expect_err("Should fail.");
187+
188+
assert!(matches!(
189+
error,
190+
EventHandlerError::FailedCommanderRegistration(
191+
CommanderRegistrationError::CommanderIdOverflow
192+
)
193+
));
194+
195+
assert!(buffer.is_empty());
196+
}
197+
198+
#[test]
199+
fn send_registered_command_with_overwrite() {
200+
let address = Address::new(
201+
Some(Text::new("ws://remote:8080")),
202+
Text::new("/target"),
203+
Text::new("lane"),
204+
);
205+
let spawner = TestSpawner::new(address.clone(), Ok(7));
206+
let mut buffer = BytesMut::new();
207+
let agent = FakeAgent;
208+
let uri = make_uri();
209+
let route_params = HashMap::new();
210+
let meta = make_meta(&uri, &route_params);
211+
212+
let commander: Commander<FakeAgent> = Commander::new(4);
213+
let handler = commander.send("hello");
214+
run_handler(handler, &spawner, &mut buffer, &agent, meta).expect("Failed unexpectedly.");
215+
216+
let mut decoder = CommandMessageDecoder::<Text, Value>::default();
217+
218+
let message = decoder
219+
.decode_eof(&mut buffer)
220+
.expect("Invalid buffer contents.")
221+
.expect("No message.");
222+
223+
assert_eq!(
224+
message,
225+
CommandMessage::<Text, Value>::Registered {
226+
target: 4,
227+
command: Value::text("hello"),
228+
overwrite_permitted: true
229+
}
230+
);
231+
}
232+
233+
#[test]
234+
fn send_registered_command_without_overwrite() {
235+
let address = Address::new(
236+
Some(Text::new("ws://remote:8080")),
237+
Text::new("/target"),
238+
Text::new("lane"),
239+
);
240+
let spawner = TestSpawner::new(address.clone(), Ok(7));
241+
let mut buffer = BytesMut::new();
242+
let agent = FakeAgent;
243+
let uri = make_uri();
244+
let route_params = HashMap::new();
245+
let meta = make_meta(&uri, &route_params);
246+
247+
let commander: Commander<FakeAgent> = Commander::new(4);
248+
let handler = commander.send_queued("hello");
249+
run_handler(handler, &spawner, &mut buffer, &agent, meta).expect("Failed unexpectedly.");
250+
251+
let mut decoder = CommandMessageDecoder::<Text, Value>::default();
252+
253+
let message = decoder
254+
.decode_eof(&mut buffer)
255+
.expect("Invalid buffer contents.")
256+
.expect("No message.");
257+
258+
assert_eq!(
259+
message,
260+
CommandMessage::<Text, Value>::Registered {
261+
target: 4,
262+
command: Value::text("hello"),
263+
overwrite_permitted: false
264+
}
265+
);
266+
}

0 commit comments

Comments
 (0)