Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Add integration tests #59

Merged
merged 2 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/network/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl std::fmt::Display for ServiceAddressProtocol {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ServiceAddress {
host: String,
port: Option<u16>,
Expand Down Expand Up @@ -130,7 +130,9 @@ impl ServerClientImpl {
.send();

match r {
Ok(response) => response.json().map_err(Error::ReqwestError),
Ok(response) => response.json().map_err(|_| {
Error::ProtocolError("Failed to deserialize JSON from server response".to_string())
}),
Err(e) => {
// TODO: Identify if token expired, get new one and retry
if false {
Expand Down
122 changes: 122 additions & 0 deletions tests/test_initial_configuration_fails.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use std::io::{BufRead, BufReader, Write};
use std::net::TcpListener;
use std::sync::mpsc::channel;
use std::thread::spawn;

use appconfiguration::{
AppConfigurationClientHttp, ConfigurationId, Error, ServiceAddress, TokenProvider,
};

fn handle_config_request_error(server: &TcpListener) {
let (mut stream, _) = server.accept().unwrap();

let buf_reader = BufReader::new(&stream);
let http_request: Vec<_> = buf_reader
.lines()
.map(|result| result.unwrap())
.take_while(|line| !line.is_empty())
.collect();
assert_eq!(http_request[0], "GET /test/feature/v1/instances/guid/config?action=sdkConfig&environment_id=dev&collection_id=collection_id HTTP/1.1");
stream
.write_all(b"HTTP/1.1 400\r\nContent-Length: 0")
.unwrap();
}

fn handle_config_request_invalid_json(server: &TcpListener) {
let json_payload = serde_json::json!({
"environments": [
{
"name": "Dev",
"environment_id": "dev",
"features": ["models deserialization mismatch"],
"properties": []
}
],
"segments": []
})
.to_string();

let (mut stream, _) = server.accept().unwrap();

let buf_reader = BufReader::new(&stream);
let http_request: Vec<_> = buf_reader
.lines()
.map(|result| result.unwrap())
.take_while(|line| !line.is_empty())
.collect();
assert_eq!(http_request[0], "GET /test/feature/v1/instances/guid/config?action=sdkConfig&environment_id=dev&collection_id=collection_id HTTP/1.1");
stream
.write_all(
format!(
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
json_payload.len(),
json_payload
)
.as_bytes(),
)
.unwrap();
}

struct ServerHandle {
_terminator: std::sync::mpsc::Sender<()>,
port: u16,
}
fn server_thread() -> ServerHandle {
let (terminator, receiver) = channel();

let server = TcpListener::bind(("127.0.0.1", 0)).expect("Failed to bind");
let port = server.local_addr().unwrap().port();
spawn(move || {
handle_config_request_error(&server);

handle_config_request_invalid_json(&server);

let _ = receiver.recv();
});
ServerHandle {
_terminator: terminator,
port,
}
}

#[derive(Debug)]
struct MockTokenProvider {}

impl TokenProvider for MockTokenProvider {
fn get_access_token(&self) -> appconfiguration::Result<String> {
Ok("mock_token".into())
}
}

#[test]
fn main() {
let server = server_thread();

let address = ServiceAddress::new_without_ssl(
"127.0.0.1".to_string(),
Some(server.port),
Some("test".to_string()),
);
let config_id = ConfigurationId::new(
"guid".to_string(),
"dev".to_string(),
"collection_id".to_string(),
);

// Test response error code 400
let client = AppConfigurationClientHttp::new(
address.clone(),
Box::new(MockTokenProvider {}),
config_id.clone(),
);

assert!(client.is_err());
assert!(matches!(client.unwrap_err(), Error::ReqwestError(_)));

// Test response is successful (200) but configuration JSON is invalid
let client =
AppConfigurationClientHttp::new(address, Box::new(MockTokenProvider {}), config_id);

assert!(client.is_err());
assert!(matches!(client.unwrap_err(), Error::ProtocolError(_)));
}
17 changes: 6 additions & 11 deletions tests/test_initial_configuration_from_server.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
use std::{
io::{BufRead, Write},
net::{TcpListener, TcpStream},
thread::{sleep, spawn},
time::Duration,
};

use appconfiguration::{
AppConfigurationClient, AppConfigurationClientHttp, ConfigurationId, ServiceAddress,
TokenProvider,
};

use std::io::BufReader;
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::{fs, path::PathBuf};
use std::thread::{sleep, spawn};
use std::time::Duration;
use tungstenite::WebSocket;

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

handle_config_request(server, json_payload);
}
Expand Down Expand Up @@ -114,7 +110,6 @@ impl TokenProvider for MockTokenProvider {
fn main() {
let server = server_thread();

sleep(Duration::from_secs(1));
let address = ServiceAddress::new_without_ssl(
"127.0.0.1".to_string(),
Some(server.port),
Expand Down
94 changes: 94 additions & 0 deletions tests/test_ws_connection_fails.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use appconfiguration::{
AppConfigurationClientHttp, ConfigurationId, Error, ServiceAddress, TokenProvider,
};

use std::io::{BufRead, BufReader, Write};
use std::net::TcpListener;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::thread::spawn;

fn handle_config_request_enterprise_example(server: &TcpListener) {
let mut mocked_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
mocked_data.push("data/data-dump-enterprise-plan-sdk-testing.json");
let json_payload = std::fs::read_to_string(mocked_data).unwrap();

handle_config_request(server, json_payload);
}

fn handle_config_request(server: &TcpListener, json_payload: String) {
let (mut stream, _) = server.accept().unwrap();

let buf_reader = BufReader::new(&stream);
let http_request: Vec<_> = buf_reader
.lines()
.map(|result| result.unwrap())
.take_while(|line| !line.is_empty())
.collect();
assert_eq!(http_request[0], "GET /test/feature/v1/instances/guid/config?action=sdkConfig&environment_id=dev&collection_id=collection_id HTTP/1.1");

let response = format!(
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
json_payload.len(),
json_payload
);
stream.write_all(response.as_bytes()).unwrap();
}

fn handle_websocket_error(server: &TcpListener) {
let (mut stream, _) = server.accept().unwrap();
stream.write_all(b"not today").unwrap();
}

struct ServerHandle {
_terminator: std::sync::mpsc::Sender<()>,
port: u16,
}
fn server_thread() -> ServerHandle {
let (terminator, receiver) = channel();

let server = TcpListener::bind(("127.0.0.1", 0)).expect("Failed to bind");
let port = server.local_addr().unwrap().port();
spawn(move || {
handle_config_request_enterprise_example(&server);

// notify client that config changed
handle_websocket_error(&server);

let _ = receiver.recv();
});
ServerHandle {
_terminator: terminator,
port,
}
}

#[derive(Debug)]
struct MockTokenProvider {}

impl TokenProvider for MockTokenProvider {
fn get_access_token(&self) -> appconfiguration::Result<String> {
Ok("mock_token".into())
}
}

#[test]
fn main() {
let server = server_thread();

let address = ServiceAddress::new_without_ssl(
"127.0.0.1".to_string(),
Some(server.port),
Some("test".to_string()),
);
let config_id = ConfigurationId::new(
"guid".to_string(),
"dev".to_string(),
"collection_id".to_string(),
);
let client =
AppConfigurationClientHttp::new(address, Box::new(MockTokenProvider {}), config_id);

assert!(client.is_err());
assert!(matches!(client.unwrap_err(), Error::TungsteniteError(_)));
}