klauthed_testing/
context.rs1use klauthed_core::context::{RequestContext, RequestId};
11use klauthed_core::time::Timestamp;
12
13use crate::ids::seeded_id;
14
15const DEFAULT_REQUEST_SEED: u64 = 1;
17
18const DEFAULT_RECEIVED_AT_MILLIS: i64 = 1_700_000_000_000;
21
22pub fn test_context() -> RequestContext {
38 TestContextBuilder::new().build()
39}
40
41#[derive(Debug, Clone)]
63pub struct TestContextBuilder {
64 seed: u64,
65 received_at: Timestamp,
66 tenant: Option<String>,
67 principal: Option<String>,
68 locale: Option<String>,
69 correlation_id: Option<String>,
70 deadline: Option<Timestamp>,
71 metadata: Vec<(String, String)>,
72}
73
74impl TestContextBuilder {
75 pub fn new() -> Self {
77 Self {
78 seed: DEFAULT_REQUEST_SEED,
79 received_at: Timestamp::from_unix_millis(DEFAULT_RECEIVED_AT_MILLIS),
80 tenant: None,
81 principal: None,
82 locale: None,
83 correlation_id: None,
84 deadline: None,
85 metadata: Vec::new(),
86 }
87 }
88
89 #[must_use]
91 pub fn seed(mut self, seed: u64) -> Self {
92 self.seed = seed;
93 self
94 }
95
96 #[must_use]
98 pub fn received_at(mut self, at: Timestamp) -> Self {
99 self.received_at = at;
100 self
101 }
102
103 #[must_use]
105 pub fn deadline(mut self, deadline: Timestamp) -> Self {
106 self.deadline = Some(deadline);
107 self
108 }
109
110 #[must_use]
112 pub fn tenant(mut self, tenant: impl Into<String>) -> Self {
113 self.tenant = Some(tenant.into());
114 self
115 }
116
117 #[must_use]
119 pub fn principal(mut self, principal: impl Into<String>) -> Self {
120 self.principal = Some(principal.into());
121 self
122 }
123
124 #[must_use]
126 pub fn locale(mut self, locale: impl Into<String>) -> Self {
127 self.locale = Some(locale.into());
128 self
129 }
130
131 #[must_use]
133 pub fn correlation_id(mut self, id: impl Into<String>) -> Self {
134 self.correlation_id = Some(id.into());
135 self
136 }
137
138 #[must_use]
140 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
141 self.metadata.push((key.into(), value.into()));
142 self
143 }
144
145 pub fn request_id(&self) -> RequestId {
147 seeded_id(self.seed)
148 }
149
150 pub fn build(self) -> RequestContext {
152 let mut ctx = RequestContext::new()
153 .with_request_id(seeded_id(self.seed))
154 .with_received_at(self.received_at);
155 if let Some(tenant) = self.tenant {
156 ctx = ctx.with_tenant(tenant);
157 }
158 if let Some(principal) = self.principal {
159 ctx = ctx.with_principal(principal);
160 }
161 if let Some(locale) = self.locale {
162 ctx = ctx.with_locale(locale);
163 }
164 if let Some(correlation_id) = self.correlation_id {
165 ctx = ctx.with_correlation_id(correlation_id);
166 }
167 if let Some(deadline) = self.deadline {
168 ctx = ctx.with_deadline(deadline);
169 }
170 for (key, value) in self.metadata {
171 ctx = ctx.with_metadata(key, value);
172 }
173 ctx
174 }
175}
176
177impl Default for TestContextBuilder {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use crate::ids::seeded_id;
187
188 #[test]
189 fn test_context_is_deterministic() {
190 let a = test_context();
191 let b = test_context();
192 assert_eq!(a.request_id(), b.request_id());
193 assert_eq!(a.received_at(), b.received_at());
194 assert_eq!(a.request_id(), seeded_id(DEFAULT_REQUEST_SEED));
195 }
196
197 #[test]
198 fn builder_sets_fields() {
199 let ctx = TestContextBuilder::new()
200 .seed(9)
201 .tenant("acme")
202 .principal("user-1")
203 .locale("de-DE")
204 .correlation_id("corr-9")
205 .metadata("k", "v")
206 .build();
207
208 assert_eq!(ctx.request_id(), seeded_id::<_>(9));
209 assert_eq!(ctx.tenant(), Some("acme"));
210 assert_eq!(ctx.principal(), Some("user-1"));
211 assert_eq!(ctx.locale(), Some("de-DE"));
212 assert_eq!(ctx.correlation_id(), Some("corr-9"));
213 assert_eq!(ctx.metadata_get("k"), Some("v"));
214 }
215
216 #[test]
217 fn deadline_and_received_at_pinning() {
218 let received = Timestamp::from_unix_millis(10_000);
219 let deadline = Timestamp::from_unix_millis(15_000);
220 let ctx = TestContextBuilder::new().received_at(received).deadline(deadline).build();
221 assert_eq!(ctx.received_at(), received);
222 assert_eq!(ctx.deadline(), Some(deadline));
223 }
224
225 #[test]
226 fn request_id_accessor_matches_built_context() {
227 let builder = TestContextBuilder::new().seed(123);
228 let id = builder.request_id();
229 assert_eq!(builder.build().request_id(), id);
230 }
231}