Skip to main content

ocpi_kit/testkit/
mod.rs

1//! Building blocks for testing an OCPI integration: sample objects and in-memory stores.
2//!
3//! Everything here is deliberately dependency-light — no HTTP mocking framework, no test runner —
4//! so it can be used from a unit test, an integration test, a fuzz target or a demo binary
5//! without dragging a stack along.
6//!
7//! ```
8//! use ocpi_kit::testkit::{sample, InMemoryLocations};
9//! use ocpi_kit::types::Validate;
10//!
11//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
12//! let location = sample::location("LOC1")?;
13//! location.validate()?;
14//!
15//! let store = InMemoryLocations::new();
16//! store.put(location.clone());
17//! assert_eq!(store.get("loc1").unwrap().id, location.id);
18//! # Ok(())
19//! # }
20//! ```
21
22#[cfg(feature = "server")]
23mod peer;
24pub mod sample;
25pub mod stores;
26
27#[cfg(feature = "server")]
28#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
29pub use peer::{MockPeer, MockPeerStores};
30pub use stores::{InMemoryCdrs, InMemoryLocations, InMemorySessions, InMemoryTariffs, InMemoryTokens};
31
32#[cfg(feature = "server")]
33use crate::VersionNumber;
34use crate::transport::CredentialsToken;
35use crate::types::PartyRef;
36
37/// A credentials token with a fixed, obviously-fake value.
38///
39/// Using a recognisable constant in tests makes an accidental leak into a log or a fixture
40/// obvious, and makes failures reproducible.
41#[must_use]
42pub fn test_token(label: &str) -> CredentialsToken {
43    CredentialsToken::new(format!("test-token-{label}"))
44        .unwrap_or_else(|e| panic!("test token label {label:?} is not usable: {e}"))
45}
46
47/// The party a test CPO speaks as.
48///
49/// `NL/TNM` is the pair the specification's own examples use.
50#[must_use]
51pub fn test_cpo() -> PartyRef {
52    PartyRef::new("NL", "TNM").expect("NL/TNM is a valid party reference")
53}
54
55/// The party a test eMSP speaks as.
56#[must_use]
57pub fn test_msp() -> PartyRef {
58    PartyRef::new("DE", "ABC").expect("DE/ABC is a valid party reference")
59}
60
61/// The party a test hub speaks as.
62#[must_use]
63pub fn test_hub() -> PartyRef {
64    PartyRef::new("NL", "HUB").expect("NL/HUB is a valid party reference")
65}
66
67/// An [`AuthenticatedPeer`](crate::server::AuthenticatedPeer) for a fully registered platform.
68#[cfg(feature = "server")]
69#[must_use]
70pub fn registered_peer(peer_id: &str, parties: Vec<PartyRef>) -> crate::server::AuthenticatedPeer {
71    crate::server::AuthenticatedPeer {
72        peer_id: peer_id.to_owned(),
73        role: crate::transport::TokenRole::C,
74        parties,
75        version: VersionNumber::V2_3_0,
76    }
77}
78
79/// An [`AuthenticatedPeer`](crate::server::AuthenticatedPeer) that still holds
80/// `CREDENTIALS_TOKEN_A`, for testing the scope rule.
81///
82/// Takes the parties for the same reason [`registered_peer`] does: the ownership check compares
83/// the URL's owner against them, so a helper that guessed would make every ownership test a test
84/// of the guess.
85#[cfg(feature = "server")]
86#[must_use]
87pub fn bootstrap_peer(peer_id: &str, parties: Vec<PartyRef>) -> crate::server::AuthenticatedPeer {
88    crate::server::AuthenticatedPeer {
89        peer_id: peer_id.to_owned(),
90        role: crate::transport::TokenRole::A,
91        parties,
92        version: VersionNumber::V2_3_0,
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn the_test_parties_are_the_ones_the_spec_examples_use() {
102        assert_eq!(test_cpo().to_string(), "NL/TNM");
103        assert_eq!(test_msp().to_string(), "DE/ABC");
104        assert!(test_token("a").expose_secret().starts_with("test-token-"));
105    }
106}