Skip to content

Commit 2ef3c5e

Browse files
authored
Merge pull request #9 from silicon-heaven/api-tests
Add more API tests
2 parents 5e54e8c + 62e1014 commit 2ef3c5e

File tree

4 files changed

+90
-35
lines changed

4 files changed

+90
-35
lines changed

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
runs-on: ubuntu-latest
3232
steps:
3333
- name: Generate coverage
34-
uses: syyyr/rust-pycobertura-action@v1.1.1
34+
uses: syyyr/rust-pycobertura-action@v1.2.0
3535
with:
3636
project_name: shv-http-gateway
3737
cargo_test_arguments: ""

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ name = "shv-http-gateway"
33
version = "0.1.2"
44
edition = "2021"
55

6+
[lints.rust]
7+
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] }
8+
69
[dev-dependencies]
710
shvbroker = { git = "https://github.com/silicon-heaven/shvbroker-rs", branch = "master" }
811
sse-codec = "0.3.2"

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![cfg_attr(coverage, feature(coverage_attribute))]
2+
13
use std::collections::HashMap;
24
use std::sync::Arc;
35

src/tests.rs

Lines changed: 84 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
#![cfg_attr(coverage, coverage(off))]
2+
13
use std::future::Future;
2-
use std::sync::LazyLock;
4+
use std::sync::{Arc, LazyLock};
35
use std::time::Duration;
46

57
use const_format::formatcp;
68
use log::{error, info, warn};
9+
use rocket::futures::future::join_all;
710
use rocket::futures::stream::FuturesUnordered;
811
use rocket::futures::StreamExt;
912
use rocket::http::ContentType;
@@ -232,6 +235,29 @@ fn api_login_fails() {
232235
});
233236
}
234237

238+
#[test]
239+
fn api_login_and_logout() {
240+
shared_rt_test(async {
241+
let client = RocketClient::untracked(build_rocket(program_config())).await.unwrap();
242+
243+
let resp = client
244+
.post("/api/login")
245+
.header(ContentType::JSON)
246+
.body(r#"{"username": "admin", "password": "admin"}"#)
247+
.dispatch()
248+
.await;
249+
assert_eq!(resp.status(), Status::Ok);
250+
let session_id = resp.into_json::<LoginResponse>().await.unwrap().session_id;
251+
252+
let resp = client
253+
.post("/api/logout")
254+
.header(rocket::http::Header::new("Authorization", session_id))
255+
.dispatch()
256+
.await;
257+
assert_eq!(resp.status(), Status::Ok);
258+
});
259+
}
260+
235261
// NOTE: This test works only if the user used here is not shared with other tests!
236262
#[test]
237263
fn api_login_max_sessions_exceeds() {
@@ -290,6 +316,19 @@ fn api_rpc_calls() {
290316
}
291317
}
292318

319+
// Wrong session token
320+
{
321+
let resp = client
322+
.post("/api/rpc")
323+
.header(rocket::http::Header::new("Authorization", "wrong_token"))
324+
.header(ContentType::JSON)
325+
.body(r#"{"path": ".broker", "method": "anything"}"#)
326+
.dispatch()
327+
.await;
328+
assert_eq!(resp.status(), Status::Unauthorized);
329+
}
330+
331+
// Wrong body format
293332
{
294333
let resp = client
295334
.post("/api/rpc")
@@ -300,6 +339,8 @@ fn api_rpc_calls() {
300339
.await;
301340
assert_eq!(resp.status(), Status::UnprocessableEntity);
302341
}
342+
343+
// Regular calls
303344
let rpc_call_dispatcher = RpcCallDispatcher { client, session_id };
304345
{
305346
let resp = rpc_call_dispatcher.call(".broker", "ls", None::<&str>).await;
@@ -324,7 +365,7 @@ fn api_rpc_calls() {
324365
#[test]
325366
fn api_subscribe() {
326367
shared_rt_test(async {
327-
let client = RocketClient::untracked(build_rocket(program_config())).await.unwrap();
368+
let client = Arc::new(RocketClient::untracked(build_rocket(program_config())).await.unwrap());
328369

329370
let resp = client
330371
.post("/api/login")
@@ -334,37 +375,45 @@ fn api_subscribe() {
334375
.await;
335376
let session_id = resp.into_json::<LoginResponse>().await.unwrap().session_id;
336377

337-
let resp = client
338-
.post("/api/subscribe")
339-
.header(ContentType::JSON)
340-
.header(rocket::http::Header::new("Authorization", session_id.clone()))
341-
.body(r#"{"shv_ri": "test/device/value:*:*"}"#)
342-
.dispatch()
343-
.await;
378+
let mut tasks = vec![];
379+
for task_id in 0..10 {
380+
let client = client.clone();
381+
let session_id = session_id.clone();
382+
let task = tokio::spawn(async move {
383+
let req = client
384+
.post("/api/subscribe")
385+
.header(ContentType::JSON)
386+
.header(rocket::http::Header::new("Authorization", session_id))
387+
.body(format!(r#"{{"shv_ri": "{}:*:*"}}"#,
388+
if task_id < 5 { "test/device/value" } else { "test/*" }
389+
))
390+
.dispatch();
391+
let resp = req.await;
392+
393+
assert!(resp.content_type().unwrap().is_event_stream());
344394

345-
assert!(resp.content_type().unwrap().is_event_stream());
346-
347-
// let mut reader = tokio::io::BufReader::new(resp).lines();
348-
// for i in 0..5 {
349-
// warn!("receiving event {i}");
350-
// let event = reader
351-
// .next_line()
352-
// .await
353-
// .expect("Read line error")
354-
// .expect("Unexpected end of stream");
355-
// warn!("{event}");
356-
// }
357-
358-
let mut reader = sse_codec::decode_stream(tokio::io::BufReader::new(resp).compat());
359-
for i in 0..10 {
360-
info!("receiving event {i}");
361-
let event = reader
362-
.next()
363-
.await
364-
.expect("Unexpected end of stream")
365-
.unwrap_or_else(|e| panic!("Unexpected error in event stream: {e}"));
366-
match event {
367-
sse_codec::Event::Message{ id, event, data, ..} => {
395+
// let mut reader = tokio::io::BufReader::new(resp).lines();
396+
// for i in 0..5 {
397+
// warn!("receiving event {i}");
398+
// let event = reader
399+
// .next_line()
400+
// .await
401+
// .expect("Read line error")
402+
// .expect("Unexpected end of stream");
403+
// warn!("{event}");
404+
// }
405+
406+
let mut reader = sse_codec::decode_stream(tokio::io::BufReader::new(resp).compat());
407+
for i in 0..10 {
408+
info!("task {task_id}, receiving event {i}");
409+
let event = reader
410+
.next()
411+
.await
412+
.expect("Unexpected end of stream")
413+
.unwrap_or_else(|e| panic!("Unexpected error in event stream: {e}"));
414+
let sse_codec::Event::Message{ id, event, data} = event else {
415+
panic!("Unexpected event");
416+
};
368417
info!("{data}");
369418
assert!(id.is_none());
370419
assert_eq!(event, "message");
@@ -373,8 +422,9 @@ fn api_subscribe() {
373422
assert_eq!(parsed_data.signal, Some("event".into()));
374423
assert_eq!(parsed_data.param, Some(RpcValue::from(42).to_cpon()));
375424
}
376-
_ => panic!("Unexpected event"),
377-
}
425+
});
426+
tasks.push(task);
378427
}
428+
join_all(tasks).await;
379429
});
380430
}

0 commit comments

Comments
 (0)