mod test_store;
use {
ocpi::{
types::{self, CiString, CredentialsToken, CsString},
CredentialsModule,
},
serde_json::json,
wiremock::{matchers, Mock, MockServer, ResponseTemplate},
};
use std::{
str::FromStr,
sync::atomic::{AtomicUsize, Ordering},
};
use ocpi::{NoCommandsHandler, Party};
use once_cell::sync::OnceCell;
pub use test_store::TestStore;
use wiremock::http::HeaderValue;
pub fn create_cpo_and_store() -> (ocpi::Cpo<TestStore, NoCommandsHandler>, TestStore) {
let store = TestStore::default();
(
ocpi::Cpo::new(
"http://localhost:8000".parse().expect("parsing unused url"),
store.clone(),
ocpi::Client::default(),
),
store,
)
}
#[cfg(test)]
#[rustfmt::skip::macros(json)]
async fn create_versions_mock(cpo_token: impl AsRef<str>) -> (types::Url, MockServer) {
let mock = MockServer::start().await;
let mock_url = mock.uri().parse::<types::Url>().expect("Mock URI as URL");
let b64_token = base64::encode(cpo_token.as_ref());
let authorization_header = format!("Token {}", b64_token);
Mock::given(matchers::method("GET"))
.and(matchers::path("/versions"))
.and(matchers::header(
"Authorization",
HeaderValue::from_str(&authorization_header).expect("HeaderValue"),
))
.and(matchers::header_exists("X-Request-Id"))
.and(matchers::header_exists("X-Correlation-Id"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!(
{
"status_code": 1000,
"status_message": "Success",
"timestamp": "2015-06-30T21:59:59Z",
"data":
[
{
"version": "2.1.1",
"url": "http://www.server.com/ocpi/2.1.1/"
},
{
"version": "2.2",
"url": format!("{}/2.2", mock.uri())
}
]
}
)))
.mount(&mock)
.await;
Mock::given(matchers::method("GET"))
.and(matchers::path("/2.2"))
.and(matchers::header(
"Authorization",
HeaderValue::from_str(&format!("Token {}", b64_token)).expect("HeaderValue"),
))
.and(matchers::header_exists("X-Request-Id"))
.and(matchers::header_exists("X-Correlation-Id"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!(
{
"status_code": 1000,
"status_message": "Success",
"timestamp": "2015-06-30T21:59:59Z",
"data": {
"version": "2.2",
"endpoints": [
{
"identifier": "credentials",
"role": "SENDER",
"url": format!("{}/2.2/credentials", mock.uri())
}
]
}
}
)))
.mount(&mock)
.await;
(mock_url, mock)
}
fn ctx(credentials_token: CredentialsToken) -> ocpi::Context {
static COUNTER: OnceCell<AtomicUsize> = OnceCell::new();
let c = COUNTER.get_or_init(|| AtomicUsize::new(1));
let req_id = c.fetch_add(1, Ordering::Relaxed);
let corr_id = c.fetch_add(1, Ordering::Relaxed);
ocpi::Context {
request_id: format!("{req_id}"),
correlation_id: format!("{corr_id}"),
credentials_token,
}
}
#[tokio::test]
async fn test_registration() {
let cpo_token = "<i_really_am_a_dummy_token>"
.parse::<CredentialsToken>()
.expect("Parsing dummy token");
let (mock_url, _mock) = create_versions_mock(cpo_token.as_str()).await;
let (cpo, store) = create_cpo_and_store();
let reg_token = store.create_reg_token(); let emsp_name: CsString<100> = "TestingEMSPParty".parse().expect("emsp_name");
let emsp_party_id: CiString<3> = "WUT".parse().expect("emsp_party_id");
let emsp_country_code: CiString<2> = "se".parse().expect("emsp_country_code");
let roles = vec![ocpi::types::CredentialsRole {
role: ocpi::types::Role::Emsp,
business_details: ocpi::types::BusinessDetails {
name: emsp_name.clone(),
website: None,
logo: None,
},
party_id: emsp_party_id.clone(),
country_code: emsp_country_code.clone(),
}];
let cpo_credentials = cpo
.credentials_post(
ctx(reg_token),
ocpi::types::Credential {
token: cpo_token.clone(),
url: mock_url.join("/versions").expect("Versions url"),
roles,
},
)
.await
.expect("POST credentials");
let emsp_party = store
.by_token_we_use(&cpo_token)
.expect("Expected a party to be created");
assert_ne!(cpo_credentials.token, cpo_token);
assert_eq!(cpo_credentials.token, emsp_party.token_they_use());
assert_eq!(emsp_party.name, emsp_name);
assert_eq!(emsp_party.roles.len(), 1);
assert_eq!(emsp_party.roles[0].business_details.name, emsp_name);
assert_eq!(emsp_party.roles[0].party_id, emsp_party_id);
assert_eq!(emsp_party.roles[0].country_code, emsp_country_code);
}
#[tokio::test]
async fn test_put_registration() {
let cpo_token = "<i_really_am_a_dummy_token>"
.parse::<CredentialsToken>()
.expect("Parsing dummy token");
let (mock_url, _mock) = create_versions_mock(cpo_token.as_str()).await;
let (cpo, store) = create_cpo_and_store();
let reg_token = store.create_reg_token();
let mut roles = vec![ocpi::types::CredentialsRole {
role: ocpi::types::Role::Emsp,
business_details: ocpi::types::BusinessDetails {
name: "TestingEMSPPartyBeforeUpdate".parse().expect("emsp_name"),
website: None,
logo: None,
},
party_id: "WUT".parse().expect("emsp_party_id"),
country_code: "se".parse().expect("emsp_country_code"),
}];
let cpo_credentials = cpo
.credentials_post(
ctx(reg_token),
ocpi::types::Credential {
token: cpo_token.clone(),
url: mock_url.join("/versions").expect("Versions url"),
roles: roles.clone(),
},
)
.await
.expect("POST credentials");
let token_to_send = cpo_credentials.token;
let new_cpo_token = "<Im the new cpo token use in put>"
.parse::<CredentialsToken>()
.expect("Parsing dummy token");
let (new_mock_url, _mock) = create_versions_mock(new_cpo_token.as_str()).await;
let new_business_name: CsString<100> =
"TestingEMSPPartyAfterUpdate".parse().expect("emsp_name");
roles[0].business_details.name = new_business_name.clone();
let cpo_credentials = cpo
.credentials_put(
ctx(token_to_send.clone()),
ocpi::types::Credential {
token: new_cpo_token.clone(),
url: new_mock_url.join("/versions").expect("Versions url"),
roles,
},
)
.await
.expect("PUT credentials");
let party = store.by_token_we_use(&new_cpo_token).unwrap();
assert_ne!(party.token_they_use, token_to_send);
assert_eq!(cpo_credentials.token, party.token_they_use);
assert_eq!(party.token_we_use, new_cpo_token);
}