Skip to content

Commit 39e0ed6

Browse files
committed
test: add more tests and const generics documentation
Fill test coverage gaps identified during code review: - `trybuild`: verify error messages for missing Facet derive, non-Debug type params, and const generics (documenting the limitation) - `insta`: snapshot tests for exact Debug output format verification across structs, enums, tuple structs, and nested types - Bump `facet` dev-dep to 0.44
1 parent 8392ae0 commit 39e0ed6

18 files changed

Lines changed: 348 additions & 1 deletion

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ quote = "1.0"
2727
facet-macros-parse = "0.31"
2828

2929
[dev-dependencies]
30-
facet = { version = "0.43.2", features = ["reflect"] }
30+
facet = { version = "0.44", features = ["reflect"] }
31+
insta = "1"
32+
trybuild = { version = "1", features = ["diff"] }
3133

3234
[[example]]
3335
name = "enum_example"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// Const generics are not supported.
2+
/// Both `Facet` and `SafeDebug` fail on const generic parameters.
3+
/// Facet's derive rejects `const N: usize` outright, and SafeDebug's
4+
/// string-based generics parser produces unparsable tokens.
5+
use facet::Facet;
6+
use safe_debug::SafeDebug;
7+
8+
#[derive(Facet, SafeDebug)]
9+
struct FixedBuffer<const N: usize> {
10+
#[facet(sensitive)]
11+
data: [u8; N],
12+
label: String,
13+
}
14+
15+
fn main() {
16+
let buf: FixedBuffer<4> = FixedBuffer {
17+
data: [1, 2, 3, 4],
18+
label: "test".to_string(),
19+
};
20+
println!("{:?}", buf);
21+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error: unexpected `const` parameter declaration
2+
--> tests/compile_fail/const_generic.rs:9:20
3+
|
4+
9 | struct FixedBuffer<const N: usize> {
5+
| ^^^^^^^^^^^^^^ expected a `const` expression, not a parameter declaration
6+
7+
error: expected one of `#`, `{`, lifetime, or type, found keyword `const`
8+
--> tests/compile_fail/const_generic.rs:8:17
9+
|
10+
8 | #[derive(Facet, SafeDebug)]
11+
| ^^^^^^^^^ expected one of `#`, `{`, lifetime, or type
12+
|
13+
= note: this error originates in the derive macro `SafeDebug` (in Nightly builds, run with -Z macro-backtrace for more info)
14+
15+
error: proc-macro derive produced unparsable tokens
16+
--> tests/compile_fail/const_generic.rs:8:17
17+
|
18+
8 | #[derive(Facet, SafeDebug)]
19+
| ^^^^^^^^^
20+
21+
error[E0277]: `FixedBuffer<4>` doesn't implement `std::fmt::Debug`
22+
--> tests/compile_fail/const_generic.rs:20:22
23+
|
24+
20 | println!("{:?}", buf);
25+
| ---- ^^^ `FixedBuffer<4>` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
26+
| |
27+
| required by this formatting parameter
28+
|
29+
= help: the trait `std::fmt::Debug` is not implemented for `FixedBuffer<4>`
30+
= note: add `#[derive(Debug)]` to `FixedBuffer<4>` or manually `impl std::fmt::Debug for FixedBuffer<4>`
31+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
32+
help: consider annotating `FixedBuffer<4>` with `#[derive(Debug)]`
33+
|
34+
9 + #[derive(Debug)]
35+
10 | struct FixedBuffer<const N: usize> {
36+
|
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// Deriving SafeDebug without Facet should produce a helpful error.
2+
use safe_debug::SafeDebug;
3+
4+
#[derive(SafeDebug)]
5+
struct MissingFacet {
6+
name: String,
7+
#[facet(sensitive)]
8+
secret: String,
9+
}
10+
11+
fn main() {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: cannot find attribute `facet` in this scope
2+
--> tests/compile_fail/missing_facet_derive.rs:7:7
3+
|
4+
7 | #[facet(sensitive)]
5+
| ^^^^^
6+
|
7+
= note: `facet` is in scope, but it is a crate, not an attribute
8+
9+
error[E0599]: no associated item named `SHAPE` found for struct `MissingFacet` in the current scope
10+
--> tests/compile_fail/missing_facet_derive.rs:4:10
11+
|
12+
4 | #[derive(SafeDebug)]
13+
| ^^^^^^^^^ associated item not found in `MissingFacet`
14+
5 | struct MissingFacet {
15+
| ------------------- associated item `SHAPE` not found for this struct
16+
|
17+
= help: items from traits can only be used if the trait is implemented and in scope
18+
= note: the following trait defines an item `SHAPE`, perhaps you need to implement it:
19+
candidate #1: `Facet`
20+
= note: this error originates in the derive macro `SafeDebug` (in Nightly builds, run with -Z macro-backtrace for more info)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// A generic type parameter that doesn't implement Debug should fail to compile.
2+
use facet::Facet;
3+
use safe_debug::SafeDebug;
4+
5+
struct NotDebug;
6+
7+
#[derive(Facet, SafeDebug)]
8+
struct Container<T> {
9+
value: T,
10+
}
11+
12+
fn main() {
13+
let c = Container { value: NotDebug };
14+
// This should fail because NotDebug doesn't implement Debug
15+
println!("{:?}", c);
16+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
error[E0277]: `NotDebug` doesn't implement `std::fmt::Debug`
2+
--> tests/compile_fail/non_debug_type_param.rs:15:22
3+
|
4+
15 | println!("{:?}", c);
5+
| ---- ^ `NotDebug` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
6+
| |
7+
| required by this formatting parameter
8+
|
9+
= help: the trait `std::fmt::Debug` is not implemented for `NotDebug`
10+
= note: add `#[derive(Debug)]` to `NotDebug` or manually `impl std::fmt::Debug for NotDebug`
11+
help: the trait `std::fmt::Debug` is implemented for `Container<T>`
12+
--> tests/compile_fail/non_debug_type_param.rs:7:17
13+
|
14+
7 | #[derive(Facet, SafeDebug)]
15+
| ^^^^^^^^^
16+
note: required for `Container<NotDebug>` to implement `std::fmt::Debug`
17+
--> tests/compile_fail/non_debug_type_param.rs:7:17
18+
|
19+
7 | #[derive(Facet, SafeDebug)]
20+
| ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
21+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the derive macro `SafeDebug` (in Nightly builds, run with -Z macro-backtrace for more info)
22+
help: consider annotating `NotDebug` with `#[derive(Debug)]`
23+
|
24+
5 + #[derive(Debug)]
25+
6 | struct NotDebug;
26+
|
27+
28+
error[E0277]: the trait bound `for<'__facet> NotDebug: Facet<'__facet>` is not satisfied
29+
--> tests/compile_fail/non_debug_type_param.rs:15:22
30+
|
31+
15 | println!("{:?}", c);
32+
| ---- ^ unsatisfied trait bound
33+
| |
34+
| required by this formatting parameter
35+
|
36+
help: the trait `for<'__facet> Facet<'__facet>` is not implemented for `NotDebug`
37+
--> tests/compile_fail/non_debug_type_param.rs:5:1
38+
|
39+
5 | struct NotDebug;
40+
| ^^^^^^^^^^^^^^^
41+
= help: the following other types implement trait `Facet<'facet>`:
42+
`&'a T` implements `Facet<'a>`
43+
`&'a mut T` implements `Facet<'a>`
44+
`()` implements `Facet<'_>`
45+
`(T0, T1)` implements `Facet<'a>`
46+
`(T0, T1, T2)` implements `Facet<'a>`
47+
`(T0, T1, T2, T3)` implements `Facet<'a>`
48+
`(T0,)` implements `Facet<'a>`
49+
`*const T` implements `Facet<'a>`
50+
and $N others
51+
note: required for `Container<NotDebug>` to implement `std::fmt::Debug`
52+
--> tests/compile_fail/non_debug_type_param.rs:7:17
53+
|
54+
7 | #[derive(Facet, SafeDebug)]
55+
| ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
56+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the derive macro `SafeDebug` (in Nightly builds, run with -Z macro-backtrace for more info)

tests/compile_fail_tests.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#[test]
2+
fn compile_fail() {
3+
let t = trybuild::TestCases::new();
4+
t.compile_fail("tests/compile_fail/*.rs");
5+
}

tests/snapshot_tests.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use facet::Facet;
2+
use safe_debug::SafeDebug;
3+
4+
// --- Type definitions for snapshot tests ---
5+
6+
#[derive(Facet, SafeDebug)]
7+
struct PatientRecord {
8+
id: String,
9+
name: String,
10+
#[facet(sensitive)]
11+
ssn: String,
12+
#[facet(sensitive)]
13+
medical_history: String,
14+
}
15+
16+
#[derive(Facet, SafeDebug)]
17+
struct TupleRecord(String, #[facet(sensitive)] String, i32);
18+
19+
#[derive(Facet, SafeDebug)]
20+
#[repr(C)]
21+
enum ApiResponse {
22+
Success {
23+
code: u16,
24+
data: String,
25+
},
26+
Error {
27+
code: u16,
28+
#[facet(sensitive)]
29+
details: String,
30+
},
31+
Pending,
32+
Token(#[facet(sensitive)] String),
33+
}
34+
35+
#[derive(Facet, SafeDebug)]
36+
struct Inner {
37+
public_data: String,
38+
#[facet(sensitive)]
39+
secret_data: String,
40+
}
41+
42+
#[derive(Facet, SafeDebug)]
43+
struct Outer {
44+
id: u32,
45+
inner: Inner,
46+
}
47+
48+
// --- Snapshot tests ---
49+
50+
#[test]
51+
fn snapshot_named_struct() {
52+
let record = PatientRecord {
53+
id: "P-12345".to_string(),
54+
name: "Jane Doe".to_string(),
55+
ssn: "123-45-6789".to_string(),
56+
medical_history: "Chronic condition notes".to_string(),
57+
};
58+
insta::assert_snapshot!(format!("{:?}", record));
59+
}
60+
61+
#[test]
62+
fn snapshot_named_struct_pretty() {
63+
let record = PatientRecord {
64+
id: "P-12345".to_string(),
65+
name: "Jane Doe".to_string(),
66+
ssn: "123-45-6789".to_string(),
67+
medical_history: "Chronic condition notes".to_string(),
68+
};
69+
insta::assert_snapshot!(format!("{:#?}", record));
70+
}
71+
72+
#[test]
73+
fn snapshot_tuple_struct() {
74+
let record = TupleRecord("visible".to_string(), "secret-token".to_string(), 42);
75+
insta::assert_snapshot!(format!("{:?}", record));
76+
}
77+
78+
#[test]
79+
fn snapshot_enum_struct_variant() {
80+
let success = ApiResponse::Success {
81+
code: 200,
82+
data: "OK".to_string(),
83+
};
84+
insta::assert_snapshot!("enum_success", format!("{:?}", success));
85+
86+
let error = ApiResponse::Error {
87+
code: 500,
88+
details: "password leaked in stack trace".to_string(),
89+
};
90+
insta::assert_snapshot!("enum_error_redacted", format!("{:?}", error));
91+
}
92+
93+
#[test]
94+
fn snapshot_enum_unit_and_tuple_variants() {
95+
let pending = ApiResponse::Pending;
96+
insta::assert_snapshot!("enum_pending", format!("{:?}", pending));
97+
98+
let token = ApiResponse::Token("secret-bearer-token".to_string());
99+
insta::assert_snapshot!("enum_token_redacted", format!("{:?}", token));
100+
}
101+
102+
#[test]
103+
fn snapshot_nested_struct() {
104+
let data = Outer {
105+
id: 999,
106+
inner: Inner {
107+
public_data: "visible-info".to_string(),
108+
secret_data: "classified-payload".to_string(),
109+
},
110+
};
111+
insta::assert_snapshot!(format!("{:?}", data));
112+
}
113+
114+
#[test]
115+
fn snapshot_nested_struct_pretty() {
116+
let data = Outer {
117+
id: 999,
118+
inner: Inner {
119+
public_data: "visible-info".to_string(),
120+
secret_data: "classified-payload".to_string(),
121+
},
122+
};
123+
insta::assert_snapshot!(format!("{:#?}", data));
124+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: tests/snapshot_tests.rs
3+
expression: "format!(\"{:?}\", error)"
4+
---
5+
ApiResponse::Error { code: 500, details: "[REDACTED]" }

0 commit comments

Comments
 (0)