Skip to content

Commit a6615ce

Browse files
jgsogorainerschoe
andauthored
test: Add integration tests (#59)
* Test initial configuration fails * Test WS handshake fails --------- Signed-off-by: Javier G. Sogo <[email protected]> Co-authored-by: Rainer Schoenberger <[email protected]>
1 parent 26d5f9e commit a6615ce

4 files changed

+226
-13
lines changed

src/network/http_client.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl std::fmt::Display for ServiceAddressProtocol {
4242
}
4343
}
4444

45-
#[derive(Debug)]
45+
#[derive(Debug, Clone)]
4646
pub struct ServiceAddress {
4747
host: String,
4848
port: Option<u16>,
@@ -130,7 +130,9 @@ impl ServerClientImpl {
130130
.send();
131131

132132
match r {
133-
Ok(response) => response.json().map_err(Error::ReqwestError),
133+
Ok(response) => response.json().map_err(|_| {
134+
Error::ProtocolError("Failed to deserialize JSON from server response".to_string())
135+
}),
134136
Err(e) => {
135137
// TODO: Identify if token expired, get new one and retry
136138
if false {
+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use std::io::{BufRead, BufReader, Write};
2+
use std::net::TcpListener;
3+
use std::sync::mpsc::channel;
4+
use std::thread::spawn;
5+
6+
use appconfiguration::{
7+
AppConfigurationClientHttp, ConfigurationId, Error, ServiceAddress, TokenProvider,
8+
};
9+
10+
fn handle_config_request_error(server: &TcpListener) {
11+
let (mut stream, _) = server.accept().unwrap();
12+
13+
let buf_reader = BufReader::new(&stream);
14+
let http_request: Vec<_> = buf_reader
15+
.lines()
16+
.map(|result| result.unwrap())
17+
.take_while(|line| !line.is_empty())
18+
.collect();
19+
assert_eq!(http_request[0], "GET /test/feature/v1/instances/guid/config?action=sdkConfig&environment_id=dev&collection_id=collection_id HTTP/1.1");
20+
stream
21+
.write_all(b"HTTP/1.1 400\r\nContent-Length: 0")
22+
.unwrap();
23+
}
24+
25+
fn handle_config_request_invalid_json(server: &TcpListener) {
26+
let json_payload = serde_json::json!({
27+
"environments": [
28+
{
29+
"name": "Dev",
30+
"environment_id": "dev",
31+
"features": ["models deserialization mismatch"],
32+
"properties": []
33+
}
34+
],
35+
"segments": []
36+
})
37+
.to_string();
38+
39+
let (mut stream, _) = server.accept().unwrap();
40+
41+
let buf_reader = BufReader::new(&stream);
42+
let http_request: Vec<_> = buf_reader
43+
.lines()
44+
.map(|result| result.unwrap())
45+
.take_while(|line| !line.is_empty())
46+
.collect();
47+
assert_eq!(http_request[0], "GET /test/feature/v1/instances/guid/config?action=sdkConfig&environment_id=dev&collection_id=collection_id HTTP/1.1");
48+
stream
49+
.write_all(
50+
format!(
51+
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
52+
json_payload.len(),
53+
json_payload
54+
)
55+
.as_bytes(),
56+
)
57+
.unwrap();
58+
}
59+
60+
struct ServerHandle {
61+
_terminator: std::sync::mpsc::Sender<()>,
62+
port: u16,
63+
}
64+
fn server_thread() -> ServerHandle {
65+
let (terminator, receiver) = channel();
66+
67+
let server = TcpListener::bind(("127.0.0.1", 0)).expect("Failed to bind");
68+
let port = server.local_addr().unwrap().port();
69+
spawn(move || {
70+
handle_config_request_error(&server);
71+
72+
handle_config_request_invalid_json(&server);
73+
74+
let _ = receiver.recv();
75+
});
76+
ServerHandle {
77+
_terminator: terminator,
78+
port,
79+
}
80+
}
81+
82+
#[derive(Debug)]
83+
struct MockTokenProvider {}
84+
85+
impl TokenProvider for MockTokenProvider {
86+
fn get_access_token(&self) -> appconfiguration::Result<String> {
87+
Ok("mock_token".into())
88+
}
89+
}
90+
91+
#[test]
92+
fn main() {
93+
let server = server_thread();
94+
95+
let address = ServiceAddress::new_without_ssl(
96+
"127.0.0.1".to_string(),
97+
Some(server.port),
98+
Some("test".to_string()),
99+
);
100+
let config_id = ConfigurationId::new(
101+
"guid".to_string(),
102+
"dev".to_string(),
103+
"collection_id".to_string(),
104+
);
105+
106+
// Test response error code 400
107+
let client = AppConfigurationClientHttp::new(
108+
address.clone(),
109+
Box::new(MockTokenProvider {}),
110+
config_id.clone(),
111+
);
112+
113+
assert!(client.is_err());
114+
assert!(matches!(client.unwrap_err(), Error::ReqwestError(_)));
115+
116+
// Test response is successful (200) but configuration JSON is invalid
117+
let client =
118+
AppConfigurationClientHttp::new(address, Box::new(MockTokenProvider {}), config_id);
119+
120+
assert!(client.is_err());
121+
assert!(matches!(client.unwrap_err(), Error::ProtocolError(_)));
122+
}

tests/test_initial_configuration_from_server.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
1-
use std::{
2-
io::{BufRead, Write},
3-
net::{TcpListener, TcpStream},
4-
thread::{sleep, spawn},
5-
time::Duration,
6-
};
7-
81
use appconfiguration::{
92
AppConfigurationClient, AppConfigurationClientHttp, ConfigurationId, ServiceAddress,
103
TokenProvider,
114
};
125

13-
use std::io::BufReader;
6+
use std::io::{BufRead, BufReader, Write};
7+
use std::net::{TcpListener, TcpStream};
8+
use std::path::PathBuf;
149
use std::sync::mpsc::channel;
15-
use std::{fs, path::PathBuf};
10+
use std::thread::{sleep, spawn};
11+
use std::time::Duration;
1612
use tungstenite::WebSocket;
1713

1814
fn handle_config_request_trivial_config(server: &TcpListener) {
@@ -33,7 +29,7 @@ fn handle_config_request_trivial_config(server: &TcpListener) {
3329
fn handle_config_request_enterprise_example(server: &TcpListener) {
3430
let mut mocked_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3531
mocked_data.push("data/data-dump-enterprise-plan-sdk-testing.json");
36-
let json_payload = fs::read_to_string(mocked_data).unwrap();
32+
let json_payload = std::fs::read_to_string(mocked_data).unwrap();
3733

3834
handle_config_request(server, json_payload);
3935
}
@@ -114,7 +110,6 @@ impl TokenProvider for MockTokenProvider {
114110
fn main() {
115111
let server = server_thread();
116112

117-
sleep(Duration::from_secs(1));
118113
let address = ServiceAddress::new_without_ssl(
119114
"127.0.0.1".to_string(),
120115
Some(server.port),

tests/test_ws_connection_fails.rs

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use appconfiguration::{
2+
AppConfigurationClientHttp, ConfigurationId, Error, ServiceAddress, TokenProvider,
3+
};
4+
5+
use std::io::{BufRead, BufReader, Write};
6+
use std::net::TcpListener;
7+
use std::path::PathBuf;
8+
use std::sync::mpsc::channel;
9+
use std::thread::spawn;
10+
11+
fn handle_config_request_enterprise_example(server: &TcpListener) {
12+
let mut mocked_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
13+
mocked_data.push("data/data-dump-enterprise-plan-sdk-testing.json");
14+
let json_payload = std::fs::read_to_string(mocked_data).unwrap();
15+
16+
handle_config_request(server, json_payload);
17+
}
18+
19+
fn handle_config_request(server: &TcpListener, json_payload: String) {
20+
let (mut stream, _) = server.accept().unwrap();
21+
22+
let buf_reader = BufReader::new(&stream);
23+
let http_request: Vec<_> = buf_reader
24+
.lines()
25+
.map(|result| result.unwrap())
26+
.take_while(|line| !line.is_empty())
27+
.collect();
28+
assert_eq!(http_request[0], "GET /test/feature/v1/instances/guid/config?action=sdkConfig&environment_id=dev&collection_id=collection_id HTTP/1.1");
29+
30+
let response = format!(
31+
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
32+
json_payload.len(),
33+
json_payload
34+
);
35+
stream.write_all(response.as_bytes()).unwrap();
36+
}
37+
38+
fn handle_websocket_error(server: &TcpListener) {
39+
let (mut stream, _) = server.accept().unwrap();
40+
stream.write_all(b"not today").unwrap();
41+
}
42+
43+
struct ServerHandle {
44+
_terminator: std::sync::mpsc::Sender<()>,
45+
port: u16,
46+
}
47+
fn server_thread() -> ServerHandle {
48+
let (terminator, receiver) = channel();
49+
50+
let server = TcpListener::bind(("127.0.0.1", 0)).expect("Failed to bind");
51+
let port = server.local_addr().unwrap().port();
52+
spawn(move || {
53+
handle_config_request_enterprise_example(&server);
54+
55+
// notify client that config changed
56+
handle_websocket_error(&server);
57+
58+
let _ = receiver.recv();
59+
});
60+
ServerHandle {
61+
_terminator: terminator,
62+
port,
63+
}
64+
}
65+
66+
#[derive(Debug)]
67+
struct MockTokenProvider {}
68+
69+
impl TokenProvider for MockTokenProvider {
70+
fn get_access_token(&self) -> appconfiguration::Result<String> {
71+
Ok("mock_token".into())
72+
}
73+
}
74+
75+
#[test]
76+
fn main() {
77+
let server = server_thread();
78+
79+
let address = ServiceAddress::new_without_ssl(
80+
"127.0.0.1".to_string(),
81+
Some(server.port),
82+
Some("test".to_string()),
83+
);
84+
let config_id = ConfigurationId::new(
85+
"guid".to_string(),
86+
"dev".to_string(),
87+
"collection_id".to_string(),
88+
);
89+
let client =
90+
AppConfigurationClientHttp::new(address, Box::new(MockTokenProvider {}), config_id);
91+
92+
assert!(client.is_err());
93+
assert!(matches!(client.unwrap_err(), Error::TungsteniteError(_)));
94+
}

0 commit comments

Comments
 (0)