Skip to content

Commit c5397f4

Browse files
test: integration tests for core microcrates (#47)
Adds integration test suites for core-cache, core-kid, core-negative, core-sink, and core-x509-spec microcrates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9e86631 commit c5397f4

File tree

1 file changed

+332
-0
lines changed

1 file changed

+332
-0
lines changed
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
use std::time::Duration;
2+
3+
use uselesskey_core_x509_spec::{ChainSpec, KeyUsage, NotBeforeOffset, X509Spec};
4+
5+
// ---------------------------------------------------------------------------
6+
// X509Spec — construction and defaults
7+
// ---------------------------------------------------------------------------
8+
9+
#[test]
10+
fn self_signed_sets_subject_and_issuer() {
11+
let spec = X509Spec::self_signed("myapp.example.com");
12+
assert_eq!(spec.subject_cn, "myapp.example.com");
13+
assert_eq!(spec.issuer_cn, "myapp.example.com");
14+
assert!(!spec.is_ca);
15+
}
16+
17+
#[test]
18+
fn self_signed_ca_sets_ca_flags() {
19+
let ca = X509Spec::self_signed_ca("Test Root CA");
20+
assert!(ca.is_ca);
21+
assert!(ca.key_usage.key_cert_sign);
22+
assert!(ca.key_usage.crl_sign);
23+
assert!(ca.key_usage.digital_signature);
24+
assert!(!ca.key_usage.key_encipherment);
25+
}
26+
27+
#[test]
28+
fn default_spec_values() {
29+
let spec = X509Spec::default();
30+
assert_eq!(spec.validity_days, 3650);
31+
assert_eq!(spec.rsa_bits, 2048);
32+
assert!(!spec.is_ca);
33+
assert!(spec.sans.is_empty());
34+
assert_eq!(spec.not_before_offset, NotBeforeOffset::DaysAgo(1));
35+
}
36+
37+
// ---------------------------------------------------------------------------
38+
// X509Spec — builder chain
39+
// ---------------------------------------------------------------------------
40+
41+
#[test]
42+
fn builder_chain_applies_all_fields() {
43+
let spec = X509Spec::self_signed("builder.test")
44+
.with_validity_days(90)
45+
.with_not_before(NotBeforeOffset::DaysFromNow(7))
46+
.with_rsa_bits(4096)
47+
.with_key_usage(KeyUsage::ca())
48+
.with_is_ca(true)
49+
.with_sans(vec!["a.test".into(), "b.test".into()]);
50+
51+
assert_eq!(spec.validity_days, 90);
52+
assert_eq!(spec.not_before_offset, NotBeforeOffset::DaysFromNow(7));
53+
assert_eq!(spec.rsa_bits, 4096);
54+
assert!(spec.is_ca);
55+
assert!(spec.key_usage.key_cert_sign);
56+
assert_eq!(spec.sans, vec!["a.test", "b.test"]);
57+
}
58+
59+
// ---------------------------------------------------------------------------
60+
// KeyUsage
61+
// ---------------------------------------------------------------------------
62+
63+
#[test]
64+
fn key_usage_leaf_defaults() {
65+
let ku = KeyUsage::leaf();
66+
assert!(!ku.key_cert_sign);
67+
assert!(!ku.crl_sign);
68+
assert!(ku.digital_signature);
69+
assert!(ku.key_encipherment);
70+
}
71+
72+
#[test]
73+
fn key_usage_ca_defaults() {
74+
let ku = KeyUsage::ca();
75+
assert!(ku.key_cert_sign);
76+
assert!(ku.crl_sign);
77+
assert!(ku.digital_signature);
78+
assert!(!ku.key_encipherment);
79+
}
80+
81+
#[test]
82+
fn key_usage_default_is_leaf() {
83+
assert_eq!(KeyUsage::default(), KeyUsage::leaf());
84+
}
85+
86+
#[test]
87+
fn key_usage_stable_bytes_leaf_vs_ca_differ() {
88+
assert_ne!(
89+
KeyUsage::leaf().stable_bytes(),
90+
KeyUsage::ca().stable_bytes()
91+
);
92+
}
93+
94+
// ---------------------------------------------------------------------------
95+
// X509Spec — stable_bytes determinism
96+
// ---------------------------------------------------------------------------
97+
98+
#[test]
99+
fn stable_bytes_deterministic() {
100+
let spec = X509Spec::self_signed("determinism-test");
101+
assert_eq!(spec.stable_bytes(), spec.stable_bytes());
102+
}
103+
104+
#[test]
105+
fn stable_bytes_different_cn_differ() {
106+
let a = X509Spec::self_signed("alpha.test");
107+
let b = X509Spec::self_signed("beta.test");
108+
assert_ne!(a.stable_bytes(), b.stable_bytes());
109+
}
110+
111+
#[test]
112+
fn stable_bytes_san_order_independent() {
113+
let a = X509Spec::self_signed("test").with_sans(vec!["z.test".into(), "a.test".into()]);
114+
let b = X509Spec::self_signed("test").with_sans(vec!["a.test".into(), "z.test".into()]);
115+
assert_eq!(a.stable_bytes(), b.stable_bytes());
116+
}
117+
118+
#[test]
119+
fn stable_bytes_deduplicates_sans() {
120+
let with_dupes = X509Spec::self_signed("test").with_sans(vec![
121+
"a.test".into(),
122+
"a.test".into(),
123+
"b.test".into(),
124+
]);
125+
let without_dupes =
126+
X509Spec::self_signed("test").with_sans(vec!["a.test".into(), "b.test".into()]);
127+
assert_eq!(with_dupes.stable_bytes(), without_dupes.stable_bytes());
128+
}
129+
130+
#[test]
131+
fn stable_bytes_each_field_matters() {
132+
let base = X509Spec::self_signed("test");
133+
let base_bytes = base.stable_bytes();
134+
135+
let changes: Vec<(&str, X509Spec)> = vec![
136+
("validity_days", base.clone().with_validity_days(999)),
137+
("is_ca", base.clone().with_is_ca(true)),
138+
("rsa_bits", base.clone().with_rsa_bits(4096)),
139+
(
140+
"not_before_offset",
141+
base.clone()
142+
.with_not_before(NotBeforeOffset::DaysFromNow(7)),
143+
),
144+
("key_usage", base.clone().with_key_usage(KeyUsage::ca())),
145+
("sans", base.clone().with_sans(vec!["extra.test".into()])),
146+
];
147+
148+
for (field, changed) in changes {
149+
assert_ne!(
150+
changed.stable_bytes(),
151+
base_bytes,
152+
"{field} must affect stable_bytes"
153+
);
154+
}
155+
}
156+
157+
#[test]
158+
fn stable_bytes_issuer_cn_matters() {
159+
let mut spec = X509Spec::self_signed("test");
160+
let base_bytes = spec.stable_bytes();
161+
spec.issuer_cn = "Other Issuer".to_string();
162+
assert_ne!(spec.stable_bytes(), base_bytes);
163+
}
164+
165+
#[test]
166+
fn stable_bytes_not_before_variants_differ() {
167+
let ago = X509Spec::self_signed("test").with_not_before(NotBeforeOffset::DaysAgo(1));
168+
let future = X509Spec::self_signed("test").with_not_before(NotBeforeOffset::DaysFromNow(1));
169+
assert_ne!(ago.stable_bytes(), future.stable_bytes());
170+
}
171+
172+
// ---------------------------------------------------------------------------
173+
// X509Spec — duration helpers
174+
// ---------------------------------------------------------------------------
175+
176+
#[test]
177+
fn not_before_duration_days_ago() {
178+
let spec = X509Spec::self_signed("test").with_not_before(NotBeforeOffset::DaysAgo(3));
179+
assert_eq!(
180+
spec.not_before_duration(),
181+
Duration::from_secs(3 * 24 * 60 * 60)
182+
);
183+
}
184+
185+
#[test]
186+
fn not_before_duration_days_from_now_is_zero() {
187+
let spec = X509Spec::self_signed("test").with_not_before(NotBeforeOffset::DaysFromNow(3));
188+
assert_eq!(spec.not_before_duration(), Duration::ZERO);
189+
}
190+
191+
#[test]
192+
fn not_after_duration_days_ago() {
193+
let spec = X509Spec::self_signed("test")
194+
.with_not_before(NotBeforeOffset::DaysAgo(1))
195+
.with_validity_days(30);
196+
assert_eq!(
197+
spec.not_after_duration(),
198+
Duration::from_secs(30 * 24 * 60 * 60)
199+
);
200+
}
201+
202+
#[test]
203+
fn not_after_duration_days_from_now() {
204+
let spec = X509Spec::self_signed("test")
205+
.with_not_before(NotBeforeOffset::DaysFromNow(5))
206+
.with_validity_days(30);
207+
assert_eq!(
208+
spec.not_after_duration(),
209+
Duration::from_secs((5 + 30) * 24 * 60 * 60)
210+
);
211+
}
212+
213+
// ---------------------------------------------------------------------------
214+
// ChainSpec — construction and defaults
215+
// ---------------------------------------------------------------------------
216+
217+
#[test]
218+
fn chain_spec_defaults() {
219+
let cs = ChainSpec::new("test.example.com");
220+
assert_eq!(cs.leaf_cn, "test.example.com");
221+
assert_eq!(cs.leaf_sans, vec!["test.example.com"]);
222+
assert_eq!(cs.root_cn, "test.example.com Root CA");
223+
assert_eq!(cs.intermediate_cn, "test.example.com Intermediate CA");
224+
assert_eq!(cs.rsa_bits, 2048);
225+
assert_eq!(cs.root_validity_days, 3650);
226+
assert_eq!(cs.intermediate_validity_days, 1825);
227+
assert_eq!(cs.leaf_validity_days, 3650);
228+
assert!(cs.leaf_not_before_offset_days.is_none());
229+
assert!(cs.intermediate_not_before_offset_days.is_none());
230+
}
231+
232+
#[test]
233+
fn chain_spec_builder_chain() {
234+
let cs = ChainSpec::new("example.com")
235+
.with_sans(vec!["example.com".into(), "www.example.com".into()])
236+
.with_root_cn("Custom Root")
237+
.with_intermediate_cn("Custom Int")
238+
.with_rsa_bits(4096)
239+
.with_root_validity_days(7300)
240+
.with_intermediate_validity_days(3650)
241+
.with_leaf_validity_days(90);
242+
243+
assert_eq!(cs.leaf_sans.len(), 2);
244+
assert_eq!(cs.root_cn, "Custom Root");
245+
assert_eq!(cs.intermediate_cn, "Custom Int");
246+
assert_eq!(cs.rsa_bits, 4096);
247+
assert_eq!(cs.root_validity_days, 7300);
248+
assert_eq!(cs.intermediate_validity_days, 3650);
249+
assert_eq!(cs.leaf_validity_days, 90);
250+
}
251+
252+
// ---------------------------------------------------------------------------
253+
// ChainSpec — stable_bytes
254+
// ---------------------------------------------------------------------------
255+
256+
#[test]
257+
fn chain_stable_bytes_deterministic() {
258+
let cs = ChainSpec::new("test.example.com");
259+
assert_eq!(cs.stable_bytes(), cs.stable_bytes());
260+
}
261+
262+
#[test]
263+
fn chain_stable_bytes_different_leaf_cn_differ() {
264+
let a = ChainSpec::new("alpha.test");
265+
let b = ChainSpec::new("beta.test");
266+
assert_ne!(a.stable_bytes(), b.stable_bytes());
267+
}
268+
269+
#[test]
270+
fn chain_stable_bytes_san_order_independent() {
271+
let a = ChainSpec::new("test").with_sans(vec!["z.test".into(), "a.test".into()]);
272+
let b = ChainSpec::new("test").with_sans(vec!["a.test".into(), "z.test".into()]);
273+
assert_eq!(a.stable_bytes(), b.stable_bytes());
274+
}
275+
276+
#[test]
277+
fn chain_stable_bytes_each_field_matters() {
278+
let base = ChainSpec::new("test.example.com");
279+
let base_bytes = base.stable_bytes();
280+
281+
let changes: Vec<(&str, ChainSpec)> = vec![
282+
("rsa_bits", base.clone().with_rsa_bits(4096)),
283+
(
284+
"root_validity_days",
285+
base.clone().with_root_validity_days(999),
286+
),
287+
(
288+
"intermediate_validity_days",
289+
base.clone().with_intermediate_validity_days(999),
290+
),
291+
(
292+
"leaf_validity_days",
293+
base.clone().with_leaf_validity_days(999),
294+
),
295+
("root_cn", base.clone().with_root_cn("Other Root")),
296+
(
297+
"intermediate_cn",
298+
base.clone().with_intermediate_cn("Other Int"),
299+
),
300+
(
301+
"leaf_sans",
302+
base.clone().with_sans(vec!["extra.example.com".into()]),
303+
),
304+
];
305+
306+
for (field, changed) in changes {
307+
assert_ne!(
308+
changed.stable_bytes(),
309+
base_bytes,
310+
"{field} must affect stable_bytes"
311+
);
312+
}
313+
}
314+
315+
#[test]
316+
fn chain_stable_bytes_optional_offsets_matter() {
317+
let base = ChainSpec::new("test.example.com");
318+
let base_bytes = base.stable_bytes();
319+
320+
let mut with_leaf_offset = base.clone();
321+
with_leaf_offset.leaf_not_before_offset_days = Some(100);
322+
assert_ne!(with_leaf_offset.stable_bytes(), base_bytes);
323+
324+
let mut with_int_offset = base.clone();
325+
with_int_offset.intermediate_not_before_offset_days = Some(100);
326+
assert_ne!(with_int_offset.stable_bytes(), base_bytes);
327+
328+
// Different offset values differ from each other
329+
let mut offset_200 = base.clone();
330+
offset_200.leaf_not_before_offset_days = Some(200);
331+
assert_ne!(with_leaf_offset.stable_bytes(), offset_200.stable_bytes());
332+
}

0 commit comments

Comments
 (0)