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 pub fn seed(mut self, seed: u64) -> Self {
91 self.seed = seed;
92 self
93 }
94
95 pub fn received_at(mut self, at: Timestamp) -> Self {
97 self.received_at = at;
98 self
99 }
100
101 pub fn deadline(mut self, deadline: Timestamp) -> Self {
103 self.deadline = Some(deadline);
104 self
105 }
106
107 pub fn tenant(mut self, tenant: impl Into<String>) -> Self {
109 self.tenant = Some(tenant.into());
110 self
111 }
112
113 pub fn principal(mut self, principal: impl Into<String>) -> Self {
115 self.principal = Some(principal.into());
116 self
117 }
118
119 pub fn locale(mut self, locale: impl Into<String>) -> Self {
121 self.locale = Some(locale.into());
122 self
123 }
124
125 pub fn correlation_id(mut self, id: impl Into<String>) -> Self {
127 self.correlation_id = Some(id.into());
128 self
129 }
130
131 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
133 self.metadata.push((key.into(), value.into()));
134 self
135 }
136
137 pub fn request_id(&self) -> RequestId {
139 seeded_id(self.seed)
140 }
141
142 pub fn build(self) -> RequestContext {
144 let mut ctx = RequestContext::new()
145 .with_request_id(seeded_id(self.seed))
146 .with_received_at(self.received_at);
147 if let Some(tenant) = self.tenant {
148 ctx = ctx.with_tenant(tenant);
149 }
150 if let Some(principal) = self.principal {
151 ctx = ctx.with_principal(principal);
152 }
153 if let Some(locale) = self.locale {
154 ctx = ctx.with_locale(locale);
155 }
156 if let Some(correlation_id) = self.correlation_id {
157 ctx = ctx.with_correlation_id(correlation_id);
158 }
159 if let Some(deadline) = self.deadline {
160 ctx = ctx.with_deadline(deadline);
161 }
162 for (key, value) in self.metadata {
163 ctx = ctx.with_metadata(key, value);
164 }
165 ctx
166 }
167}
168
169impl Default for TestContextBuilder {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use crate::ids::seeded_id;
179
180 #[test]
181 fn test_context_is_deterministic() {
182 let a = test_context();
183 let b = test_context();
184 assert_eq!(a.request_id(), b.request_id());
185 assert_eq!(a.received_at(), b.received_at());
186 assert_eq!(a.request_id(), seeded_id(DEFAULT_REQUEST_SEED));
187 }
188
189 #[test]
190 fn builder_sets_fields() {
191 let ctx = TestContextBuilder::new()
192 .seed(9)
193 .tenant("acme")
194 .principal("user-1")
195 .locale("de-DE")
196 .correlation_id("corr-9")
197 .metadata("k", "v")
198 .build();
199
200 assert_eq!(ctx.request_id(), seeded_id::<_>(9));
201 assert_eq!(ctx.tenant(), Some("acme"));
202 assert_eq!(ctx.principal(), Some("user-1"));
203 assert_eq!(ctx.locale(), Some("de-DE"));
204 assert_eq!(ctx.correlation_id(), Some("corr-9"));
205 assert_eq!(ctx.metadata_get("k"), Some("v"));
206 }
207
208 #[test]
209 fn deadline_and_received_at_pinning() {
210 let received = Timestamp::from_unix_millis(10_000);
211 let deadline = Timestamp::from_unix_millis(15_000);
212 let ctx = TestContextBuilder::new().received_at(received).deadline(deadline).build();
213 assert_eq!(ctx.received_at(), received);
214 assert_eq!(ctx.deadline(), Some(deadline));
215 }
216
217 #[test]
218 fn request_id_accessor_matches_built_context() {
219 let builder = TestContextBuilder::new().seed(123);
220 let id = builder.request_id();
221 assert_eq!(builder.build().request_id(), id);
222 }
223}