hedl-test 2.0.0

Shared test fixtures and utilities for HEDL format converters
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
# hedl-test

**Shared test fixtures and utilities for HEDL -comprehensive test data, builders, and error cases for consistent testing across all crates.**

Testing format converters requires representative documents. Building test cases by hand is tedious and error-prone. Common test scenarios should be reusable across crates. Edge cases need systematic coverage. Error conditions must be validated consistently.

`hedl-test` provides 15 pre-built fixtures covering all HEDL features, 4 builder types for programmatic document construction, comprehensive error fixtures (15 invalid HEDL, 15 invalid expressions, 8 semantic violations), and edge case generators (deep nesting, wide documents, long strings, many references).

## What's Implemented

Comprehensive test infrastructure:

1. **15 Pre-Built Fixtures**: Scalars, special strings, references, expressions, tensors, named values, user list, mixed type list, with references, with nest, deep nest, edge cases, comprehensive, blog, empty
2. **4 Builder Types**: DocumentBuilder, MatrixListBuilder, NodeBuilder, ValueBuilder
3. **Error Fixtures**: 15 invalid HEDL samples, 15 invalid expressions, 8 semantically invalid documents
4. **Edge Case Generators**: deeply_nested_document, wide_document, long_string_document, many_references_document
5. **Counting Utilities**: count_nodes, count_references
6. **Expression Helpers**: expr, try_expr, expr_value, try_expr_value with ExprError type

## Installation

```toml
[dev-dependencies]
hedl-test = { workspace = true }
```

Or specify version directly:

```toml
[dev-dependencies]
hedl-test = "2.0"
```

## Pre-Built Fixtures

### Basic Fixtures

```rust
use hedl_test::fixtures;

// Scalar types (all primitives)
let doc = fixtures::scalars();
// Contains: int, float, bool, null, string examples

// Special strings (escapes, Unicode, etc.)
let doc = fixtures::special_strings();
// Contains: quotes, escapes, newlines, Unicode

// References (qualified and local)
let doc = fixtures::references();
// Contains: local references (@id) and typed references (@Type:id)

// Tensors (matrix literals)
let doc = fixtures::tensors();
// Contains: 1D, 2D, 3D tensors and empty tensor
```

### Complex Fixtures

```rust
use hedl_test::fixtures;

// User list (simple matrix)
let doc = fixtures::user_list();
// 3 users: alice, bob, charlie with id, name, email

// Mixed type list
let doc = fixtures::mixed_type_list();
// Items with int, float, bool, null, and string fields

// With references
let doc = fixtures::with_references();
// Users and Posts with author references

// With NEST hierarchy
let doc = fixtures::with_nest();
// Users with nested posts (parent-child via NEST)

// Deep NEST hierarchy
let doc = fixtures::deep_nest();
// Organization → Department → Employee (3 levels)

// Blog (nested structure)
let doc = fixtures::blog();
// Complex blog platform: users, categories, tags, posts, comments, reactions, post_tags, followers
```

### Comprehensive Fixture

All HEDL features in one document:

```rust
use hedl_test::fixtures;

let doc = fixtures::comprehensive();
// Contains:
// - Scalar values (bool, string, int, float)
// - Expressions (function calls, identifiers, literals)
// - Tensors
// - Users list with nested posts (via NEST)
// - Comments and Tags lists
// - References between entities
// - Multiple STRUCT definitions
// - NEST directive
```

## Builder Types

### DocumentBuilder

Programmatically construct documents:

```rust
use hedl_test::fixtures::builders::DocumentBuilder;
use hedl_core::Value;

let doc = DocumentBuilder::new()
    .version(1, 0)
    .struct_def("User", vec!["id".to_string(), "name".to_string(), "email".to_string()])
    .alias("api_url", "https://api.example.com")
    .nest("User", "Post")
    .scalar("app_name", Value::String("MyApp".to_string().into()))
    .scalar("debug", Value::Bool(true))
    .build();
```

**Methods**:
- `version(major, minor)` - Set document version
- `struct_def(name, fields)` - Add struct definition
- `alias(alias, target)` - Add alias
- `nest(parent, child)` - Add NEST relationship
- `scalar(name, value)` - Add scalar value to root
- `list(name, list)` - Add MatrixList to root
- `item(name, item)` - Add Item to root
- `build()` - Build the Document

### MatrixListBuilder

Build typed entity lists:

```rust
use hedl_test::fixtures::builders::MatrixListBuilder;
use hedl_core::{Node, Value};

let list = MatrixListBuilder::new("User")
    .schema(vec!["id".to_string(), "name".to_string(), "age".to_string()])
    .row(Node::new("User", "alice", vec![
        Value::String("alice".to_string().into()),
        Value::String("Alice Smith".to_string().into()),
        Value::Int(30),
    ]))
    .row(Node::new("User", "bob", vec![
        Value::String("bob".to_string().into()),
        Value::String("Bob Jones".to_string().into()),
        Value::Int(25),
    ]))
    .build();
```

**Methods**:
- `schema(fields)` - Set schema (all field names)
- `field(field)` - Add single field to schema
- `row(node)` - Add a row (node)
- `rows(nodes)` - Add multiple rows
- `count_hint(count)` - Set count hint
- `build()` - Build the MatrixList

### NodeBuilder

Build individual entities:

```rust
use hedl_test::fixtures::builders::NodeBuilder;
use hedl_core::Value;

let node = NodeBuilder::new("User", "alice")
    .field(Value::String("alice".to_string().into()))
    .field(Value::String("Alice Smith".to_string().into()))
    .field(Value::Int(30))
    .build();
```

**Methods**:
- `field(value)` - Add a field value
- `fields(values)` - Add multiple field values
- `children(rel_name, nodes)` - Add child nodes under a relationship
- `child(rel_name, node)` - Add single child node
- `child_count(count)` - Set child count hint
- `build()` - Build the Node

### ValueBuilder

Static helper methods for creating values:

```rust
use hedl_test::fixtures::builders::ValueBuilder;

// Scalar values
let v = ValueBuilder::null();
let v = ValueBuilder::bool_val(true);
let v = ValueBuilder::int(42);
let v = ValueBuilder::float(3.14);
let v = ValueBuilder::string("Hello");

// References
let v = ValueBuilder::reference("User", "alice");  // Qualified reference
let v = ValueBuilder::local_ref("some_id");        // Local reference

// Tensors
let v = ValueBuilder::tensor_1d(vec![1.0, 2.0, 3.0]);
let v = ValueBuilder::tensor_2d(vec![vec![1.0, 2.0], vec![3.0, 4.0]]);
```

**Methods**:
- `null()` - Create null value
- `bool_val(value)` - Create boolean value
- `int(value)` - Create integer value
- `float(value)` - Create float value
- `string(value)` - Create string value
- `reference(type_name, id)` - Create qualified reference
- `local_ref(id)` - Create local reference
- `tensor_1d(values)` - Create 1D tensor
- `tensor_2d(rows)` - Create 2D tensor

## Error Fixtures

### Invalid HEDL Syntax

```rust
use hedl_test::fixtures::errors;

// Get all invalid HEDL samples as (name, hedl_text) pairs
let samples = errors::invalid_hedl_samples();
for (name, hedl_text) in samples {
    // Test parser with invalid input
    // Each should fail to parse
}
```

**Available samples** (15 total):
- `empty` - Empty document
- `whitespace_only` - Only whitespace
- `invalid_directive` - Invalid directive name
- `missing_separator` - Missing --- separator
- `invalid_version` - Invalid version number
- `malformed_struct` - Malformed STRUCT directive
- `unclosed_string` - Unclosed string literal
- `invalid_escape` - Invalid escape sequence
- `malformed_reference` - Malformed reference
- `invalid_tensor` - Malformed tensor syntax
- `mismatched_brackets` - Mismatched brackets
- `invalid_number` - Invalid number format
- `malformed_expression` - Malformed expression
- `invalid_identifier` - Invalid identifier
- `duplicate_directive` - Duplicate directive

### Invalid Expressions

```rust
use hedl_test::fixtures::errors;

// Get all invalid expression samples as (description, expr_string) pairs
let samples = errors::invalid_expression_samples();
for (desc, expr_str) in samples {
    // Test expression parser with invalid input
    // Each should fail to parse
}
```

**Available samples** (15 total):
- `empty` - Empty expression
- `whitespace_only` - Only whitespace
- `unclosed_paren` - Unclosed parenthesis
- `unclosed_string` - Unclosed string literal
- `invalid_chars` - Invalid characters
- `double_dot` - Double dot in path
- `trailing_dot` - Trailing dot
- `leading_dot` - Leading dot
- `mismatched_parens` - Mismatched parentheses
- `empty_call` - Empty function call
- `invalid_identifier` - Invalid identifier
- `special_chars_only` - Only special characters
- `nested_unclosed` - Nested unclosed parentheses
- `comma_without_args` - Comma without arguments
- `trailing_comma` - Trailing comma

### Semantic Errors

Valid syntax but semantic violations:

```rust
use hedl_test::fixtures::errors;

// Get all semantically invalid documents as (name, document) pairs
let docs = errors::semantically_invalid_docs();
for (name, doc) in docs {
    // Test validation logic with semantically invalid documents
    // Each should fail validation but parse correctly
}
```

**Available documents** (8 total):
- `undefined_struct` - MatrixList references undefined struct
- `undefined_nest` - NEST directive references non-existent child type
- `circular_nest` - Circular NEST references (TypeA → TypeB → TypeA)
- `dangling_reference` - Reference points to non-existent node
- `mismatched_schema` - MatrixList schema doesn't match struct definition
- `empty_type_name` - MatrixList has empty type name
- `duplicate_ids` - Multiple nodes with same ID in a list
- `invalid_alias` - Alias references non-existent item

## Edge Case Generators

### Deeply Nested Document

Test parser depth limits:

```rust
use hedl_test::fixtures::errors;

let doc = errors::deeply_nested_document(1000);
// Creates 1000 levels of nested children via NEST
```

**Use Case**: Verify recursion depth limits and stack safety

### Wide Document

Test handling of many entities:

```rust
use hedl_test::fixtures::errors;

let doc = errors::wide_document(1000);
// Creates a MatrixList with 1000 rows
```

**Use Case**: Verify large list handling and memory efficiency

### Long Strings

Test string length limits:

```rust
use hedl_test::fixtures::errors;

let doc = errors::long_string_document(1024 * 1024);
// Creates a document with a 1 MB string value
```

**Use Case**: Verify string buffer limits and allocation safety

### Many References

Test reference resolution performance:

```rust
use hedl_test::fixtures::errors;

let doc = errors::many_references_document(10_000);
// Creates 10K target nodes and 10K references
```

**Use Case**: Verify reference resolution performance and correctness

## Test Utilities

### Counting Utilities

```rust
use hedl_test::{count_nodes, count_references};

// Count total nodes (entities) in document
let count = count_nodes(&doc);

// Count total references in document
let ref_count = count_references(&doc);
```

**Available functions**:
- `count_nodes(&doc)` - Count all nodes in the document (including nested children)
- `count_references(&doc)` - Count all reference values in the document

### Expression Utilities

```rust
use hedl_test::{expr, try_expr, expr_value, try_expr_value, ExprError};

// Create Expression from string (panics on error)
let e = expr("42");
let e = expr("now()");
let e = expr("user.name");

// Create Expression safely (returns Result)
let result = try_expr("invalid!!!");
match result {
    Ok(e) => println!("Parsed: {:?}", e),
    Err(ExprError::ParseFailed { source, input }) => {
        println!("Failed to parse '{}': {}", input, source);
    }
    Err(ExprError::EmptyInput) => println!("Empty input"),
    Err(ExprError::Missing) => println!("Missing expression"),
}

// Create Value::Expression from string
let v = expr_value("42");        // Panics on error
let v = try_expr_value("42")?;   // Returns Result
```

**Available functions**:
- `expr(s)` - Parse expression string, panic on error
- `try_expr(s)` - Parse expression string, return Result
- `expr_value(s)` - Create Value::Expression, panic on error
- `try_expr_value(s)` - Create Value::Expression, return Result

**Error type**:
- `ExprError::EmptyInput` - Expression string is empty
- `ExprError::ParseFailed { source, input }` - Parse failed with lexical error
- `ExprError::Missing` - Expression is missing or null

## Quick Builder Helpers

The `fixtures::builders::quick` module provides convenient helper functions for common patterns:

```rust
use hedl_test::fixtures::builders::quick;

// Create document with simple scalar fields
let doc = quick::simple_scalars(vec![
    ("name", "Alice"),
    ("city", "NYC"),
]);

// Create document with simple user list
let doc = quick::simple_user_list(vec![
    ("alice", "Alice Smith", "alice@example.com"),
    ("bob", "Bob Jones", "bob@example.com"),
]);

// Create document with references
let doc = quick::with_references(
    vec![("alice", "Alice"), ("bob", "Bob")],  // users
    vec![("post1", "Title", "alice")],          // posts with author refs
);
```

## Usage Patterns

### Testing Parsers

```rust
use hedl_test::fixtures;

#[test]
fn test_parse_user_list() {
    let doc = fixtures::user_list();
    assert_eq!(doc.version, (1, 0));

    if let Some(hedl_core::Item::List(list)) = doc.root.get("users") {
        assert_eq!(list.rows.len(), 3);
        assert_eq!(list.type_name, "User");
    }
}
```

### Testing Format Converters

```rust
use hedl_test::fixtures;

#[test]
fn test_all_fixtures() {
    for (name, fixture_fn) in fixtures::all() {
        let doc = fixture_fn();

        // Test your converter with each fixture
        let json = to_json(&doc).unwrap();
        let back = from_json(&json).unwrap();

        // Verify roundtrip
        assert_eq!(doc, back, "Fixture {} failed roundtrip", name);
    }
}
```

### Testing Error Handling

```rust
use hedl_test::fixtures::errors;

#[test]
fn test_invalid_hedl_samples() {
    for (name, hedl_text) in errors::invalid_hedl_samples() {
        let result = parse(hedl_text);
        assert!(result.is_err(), "Sample {} should fail to parse", name);
    }
}

#[test]
fn test_semantic_errors() {
    for (name, doc) in errors::semantically_invalid_docs() {
        let result = validate(&doc);
        assert!(result.is_err(), "Document {} should fail validation", name);
    }
}
```

### Testing Edge Cases

```rust
use hedl_test::fixtures::errors;
use hedl_test::count_nodes;

#[test]
fn test_deeply_nested() {
    let doc = errors::deeply_nested_document(100);
    let count = count_nodes(&doc);

    // Verify deep nesting is handled correctly
    assert!(count > 0);
}

#[test]
fn test_many_references() {
    let doc = errors::many_references_document(1000);

    // Test reference resolution performance
    let result = resolve_references(&doc);
    assert!(result.is_ok());
}
```

## What This Crate Doesn't Do

**Property-Based Testing**: Fixtures are hand-crafted examples. For property-based testing, use `proptest` or `quickcheck` with custom generators.

**Performance Testing**: Fixtures designed for correctness testing. For performance, use `hedl-bench` with realistic workloads.

**Fuzz Testing**: No fuzzing infrastructure. For fuzz testing, use `cargo-fuzz` with custom fuzz targets.

**Test Data Generation**: Fixtures are static. For dynamic test data, write custom generators using builders.

## Dependencies

- `hedl-core` - Core HEDL types and parsing
- `hedl-c14n` - Canonicalization for HEDL documents
- `smallvec` - Optimized small vector implementation

## License

Apache-2.0