Skip to content

Commit d67968d

Browse files
committed
Merge remote-tracking branch 'origin/main' into derive-syntax
2 parents a2dedd1 + 6862a13 commit d67968d

File tree

17 files changed

+432
-429
lines changed

17 files changed

+432
-429
lines changed

.claude/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ For enums, it also creates `<Enum><Variant>Generator` for each data variant. Imp
9191

9292
### Testing Conventions
9393

94-
- Place tests in `tests/` as integration tests, not as inline `#[cfg(test)] mod tests` in source files.
94+
- All tests go in `tests/`, never inline in source files. Tests that don't need access to private functions go directly in `tests/` as integration tests. Tests that need access to private functions go in `tests/embedded/`, mirroring the `src/` directory structure (e.g. `src/protocol/packet.rs``tests/embedded/protocol/packet_tests.rs`). Embedded tests are included as child modules of their source file via `#[cfg(test)] #[path = "..."] mod tests;`, which gives them access to private items through `use super::*`. This keeps test code out of source files while preserving access to internals that Rust would otherwise forbid.
9595
- When a test needs a throwaway generator, prefer `generators::booleans()` as the simplest option (unless the test needs a larger value space).
9696
- In test code, prefer `.unwrap()` over `.expect("static message")`. A static expect message rarely adds information beyond what the panic already provides (error type + source location). Only use `.expect()` when the message includes a formatted value that aids debugging (e.g., `.expect(&format!("failed to open {}", path))`).
9797

.github/coverage-ratchet.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"nocov": 533
2+
"nocov": 529
33
}

justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ check-coverage:
4949

5050
check-conformance:
5151
cargo build --release --manifest-path tests/conformance/rust/Cargo.toml
52-
uv run --with hegel-core --with pytest --with hypothesis \
52+
uv run --with 'hegel-core==0.3.0' --with pytest --with hypothesis \
5353
pytest tests/conformance/test_conformance.py
5454

5555
# these aliases are provided as ux improvements for local developers. CI should use the longer

src/cbor_utils.rs

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -94,52 +94,5 @@ pub fn cbor_serialize<T: serde::Serialize>(value: &T) -> Value {
9494
}
9595

9696
#[cfg(test)]
97-
mod tests {
98-
use super::*;
99-
100-
#[test]
101-
fn test_cbor_map_macro() {
102-
let m = cbor_map! {
103-
"type" => "integer",
104-
"min_value" => 0
105-
};
106-
assert_eq!(as_text(map_get(&m, "type").unwrap()), Some("integer"));
107-
assert_eq!(as_u64(map_get(&m, "min_value").unwrap()), Some(0));
108-
}
109-
110-
#[test]
111-
fn test_cbor_array_macro() {
112-
let a = cbor_array![Value::from("a"), Value::from("b")];
113-
if let Value::Array(items) = &a {
114-
assert_eq!(items.len(), 2);
115-
} else {
116-
panic!("expected array"); // nocov
117-
}
118-
}
119-
120-
#[test]
121-
fn test_map_insert() {
122-
let mut m = cbor_map! { "a" => 1 };
123-
map_insert(&mut m, "b", 2);
124-
assert_eq!(as_u64(map_get(&m, "b").unwrap()), Some(2));
125-
126-
map_insert(&mut m, "a", 10);
127-
assert_eq!(as_u64(map_get(&m, "a").unwrap()), Some(10));
128-
}
129-
130-
#[test]
131-
fn test_as_bool() {
132-
assert_eq!(as_bool(&Value::Bool(true)), Some(true));
133-
assert_eq!(as_bool(&Value::Bool(false)), Some(false));
134-
assert_eq!(as_bool(&Value::from(42)), None);
135-
}
136-
137-
#[test]
138-
fn test_cbor_serialize() {
139-
let v = cbor_serialize(&42i32);
140-
assert_eq!(as_u64(&v), Some(42));
141-
142-
let v = cbor_serialize(&"hello");
143-
assert_eq!(as_text(&v), Some("hello"));
144-
}
145-
}
97+
#[path = "../tests/embedded/cbor_utils_tests.rs"]
98+
mod tests;

src/generators/value.rs

Lines changed: 2 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -249,117 +249,5 @@ pub fn from_hegel_value<T: de::DeserializeOwned>(value: HegelValue) -> Result<T,
249249
}
250250

251251
#[cfg(test)]
252-
mod tests {
253-
use super::*;
254-
255-
#[test]
256-
fn test_deserialize_f64() {
257-
let v = HegelValue::Number(42.5);
258-
let result: f64 = from_hegel_value(v).unwrap();
259-
assert_eq!(result, 42.5);
260-
}
261-
262-
#[test]
263-
fn test_deserialize_nan() {
264-
let v = HegelValue::Number(f64::NAN);
265-
let result: f64 = from_hegel_value(v).unwrap();
266-
assert!(result.is_nan());
267-
}
268-
269-
#[test]
270-
fn test_deserialize_infinity() {
271-
let v = HegelValue::Number(f64::INFINITY);
272-
let result: f64 = from_hegel_value(v).unwrap();
273-
assert!(result.is_infinite() && result.is_sign_positive());
274-
}
275-
276-
#[test]
277-
fn test_deserialize_neg_infinity() {
278-
let v = HegelValue::Number(f64::NEG_INFINITY);
279-
let result: f64 = from_hegel_value(v).unwrap();
280-
assert!(result.is_infinite() && result.is_sign_negative());
281-
}
282-
283-
#[test]
284-
fn test_deserialize_vec_f64() {
285-
let v = HegelValue::Array(vec![
286-
HegelValue::Number(1.0),
287-
HegelValue::Number(f64::NAN),
288-
HegelValue::Number(f64::INFINITY),
289-
]);
290-
let result: Vec<f64> = from_hegel_value(v).unwrap();
291-
assert_eq!(result.len(), 3);
292-
assert_eq!(result[0], 1.0);
293-
assert!(result[1].is_nan());
294-
assert!(result[2].is_infinite());
295-
}
296-
297-
#[test]
298-
fn test_from_ciborium_nan() {
299-
let cbor = ciborium::Value::Float(f64::NAN);
300-
let hegel = HegelValue::from(cbor);
301-
if let HegelValue::Number(n) = hegel {
302-
assert!(n.is_nan());
303-
} else {
304-
panic!("expected Number"); // nocov
305-
}
306-
}
307-
308-
#[test]
309-
fn test_from_ciborium_infinity() {
310-
let cbor = ciborium::Value::Float(f64::INFINITY);
311-
let hegel = HegelValue::from(cbor);
312-
let result: f64 = from_hegel_value(hegel).unwrap();
313-
assert!(result.is_infinite() && result.is_sign_positive());
314-
}
315-
316-
#[test]
317-
fn test_from_ciborium_neg_infinity() {
318-
let cbor = ciborium::Value::Float(f64::NEG_INFINITY);
319-
let hegel = HegelValue::from(cbor);
320-
let result: f64 = from_hegel_value(hegel).unwrap();
321-
assert!(result.is_infinite() && result.is_sign_negative());
322-
}
323-
324-
#[test]
325-
fn test_from_ciborium_big_integer() {
326-
// Value larger than 2^53
327-
let cbor = ciborium::Value::Integer(9223372036854776833u64.into());
328-
let hegel = HegelValue::from(cbor);
329-
let result: u64 = from_hegel_value(hegel).unwrap();
330-
assert_eq!(result, 9223372036854776833u64);
331-
}
332-
333-
#[test]
334-
fn test_from_ciborium_array_with_nan() {
335-
let cbor = ciborium::Value::Array(vec![
336-
ciborium::Value::Float(1.0),
337-
ciborium::Value::Float(f64::NAN),
338-
ciborium::Value::Float(f64::INFINITY),
339-
ciborium::Value::Float(f64::NEG_INFINITY),
340-
]);
341-
let hegel = HegelValue::from(cbor);
342-
let result: Vec<f64> = from_hegel_value(hegel).unwrap();
343-
assert_eq!(result[0], 1.0);
344-
assert!(result[1].is_nan());
345-
assert!(result[2].is_infinite() && result[2].is_sign_positive());
346-
assert!(result[3].is_infinite() && result[3].is_sign_negative());
347-
}
348-
349-
#[test]
350-
fn test_deserialize_struct() {
351-
#[derive(serde::Deserialize, Debug)]
352-
struct TestStruct {
353-
value: f64,
354-
name: String,
355-
}
356-
357-
let v = HegelValue::Object(HashMap::from([
358-
("value".to_string(), HegelValue::Number(f64::NAN)),
359-
("name".to_string(), HegelValue::String("test".to_string())),
360-
]));
361-
let result: TestStruct = from_hegel_value(v).unwrap();
362-
assert!(result.value.is_nan());
363-
assert_eq!(result.name, "test");
364-
}
365-
}
252+
#[path = "../../tests/embedded/generators/value_tests.rs"]
253+
mod tests;

src/protocol/connection.rs

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -120,28 +120,5 @@ impl Connection {
120120
}
121121

122122
#[cfg(test)]
123-
mod tests {
124-
use super::*;
125-
use std::os::unix::net::UnixStream;
126-
127-
/// Verify that when the reader stream closes (simulating server crash),
128-
/// streams unblock promptly instead of hanging forever.
129-
#[test]
130-
fn test_stream_unblocks_on_reader_close() {
131-
// Create a connection whose reader returns EOF immediately.
132-
// This simulates the server process dying.
133-
let (_, write_end) = UnixStream::pair().unwrap();
134-
let conn = Connection::new(Box::new(std::io::empty()), Box::new(write_end));
135-
136-
// Wait for the background reader to detect EOF
137-
while !conn.server_has_exited() {
138-
std::thread::yield_now();
139-
}
140-
141-
// Stream created AFTER server exit must still unblock, not hang.
142-
let mut stream = conn.new_stream();
143-
144-
let result = stream.receive_request();
145-
assert!(result.is_err());
146-
}
147-
}
123+
#[path = "../../tests/embedded/protocol/connection_tests.rs"]
124+
mod tests;

src/protocol/packet.rs

Lines changed: 2 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -112,98 +112,5 @@ pub fn read_packet<R: Read + ?Sized>(reader: &mut R) -> std::io::Result<Packet>
112112
}
113113

114114
#[cfg(test)]
115-
mod tests {
116-
use super::*;
117-
use std::os::unix::net::UnixStream;
118-
119-
use ciborium::Value;
120-
121-
#[test]
122-
fn test_packet_roundtrip() {
123-
let (mut client, mut server) = UnixStream::pair().unwrap();
124-
125-
let packet = Packet {
126-
stream: 1,
127-
message_id: 42,
128-
is_reply: false,
129-
payload: b"hello world".to_vec(),
130-
};
131-
write_packet(&mut client, &packet).unwrap();
132-
133-
let received = read_packet(&mut server).unwrap();
134-
assert_eq!(received.stream, 1);
135-
assert_eq!(received.message_id, 42);
136-
assert!(!received.is_reply);
137-
assert_eq!(received.payload, b"hello world");
138-
}
139-
140-
#[test]
141-
fn test_reply_packet() {
142-
let (mut client, mut server) = UnixStream::pair().unwrap();
143-
144-
let packet = Packet {
145-
stream: 2,
146-
message_id: 100,
147-
is_reply: true,
148-
payload: b"response".to_vec(),
149-
};
150-
write_packet(&mut client, &packet).unwrap();
151-
152-
let received = read_packet(&mut server).unwrap();
153-
assert_eq!(received.stream, 2);
154-
assert_eq!(received.message_id, 100);
155-
assert!(received.is_reply);
156-
assert_eq!(received.payload, b"response");
157-
}
158-
159-
#[test]
160-
fn test_cbor_value_roundtrip() {
161-
use crate::cbor_utils::cbor_map;
162-
// Test that ciborium::Value roundtrips through CBOR
163-
let value = cbor_map! {
164-
"type" => "integer",
165-
"min_value" => 0,
166-
"max_value" => 100
167-
};
168-
169-
// Serialize to CBOR bytes
170-
let mut cbor_bytes = Vec::new();
171-
ciborium::into_writer(&value, &mut cbor_bytes).unwrap();
172-
173-
// Deserialize back
174-
let back: Value = ciborium::from_reader(&cbor_bytes[..]).unwrap();
175-
176-
assert_eq!(value, back);
177-
}
178-
179-
#[test]
180-
fn test_cbor_nan_preserved() {
181-
let value = Value::Float(f64::NAN);
182-
let mut bytes = Vec::new();
183-
ciborium::into_writer(&value, &mut bytes).unwrap();
184-
let back: Value = ciborium::from_reader(&bytes[..]).unwrap();
185-
if let Value::Float(f) = back {
186-
assert!(f.is_nan());
187-
} else {
188-
panic!("expected Float"); // nocov
189-
}
190-
}
191-
192-
#[test]
193-
fn test_cbor_infinity_preserved() {
194-
let value = Value::Float(f64::INFINITY);
195-
let mut bytes = Vec::new();
196-
ciborium::into_writer(&value, &mut bytes).unwrap();
197-
let back: Value = ciborium::from_reader(&bytes[..]).unwrap();
198-
assert_eq!(back, Value::Float(f64::INFINITY));
199-
}
200-
201-
#[test]
202-
fn test_cbor_neg_infinity_preserved() {
203-
let value = Value::Float(f64::NEG_INFINITY);
204-
let mut bytes = Vec::new();
205-
ciborium::into_writer(&value, &mut bytes).unwrap();
206-
let back: Value = ciborium::from_reader(&bytes[..]).unwrap();
207-
assert_eq!(back, Value::Float(f64::NEG_INFINITY));
208-
}
209-
}
115+
#[path = "../../tests/embedded/protocol/packet_tests.rs"]
116+
mod tests;

0 commit comments

Comments
 (0)