red64-cli 0.3.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +194 -338
  2. package/dist/cli/parseArgs.d.ts.map +1 -1
  3. package/dist/cli/parseArgs.js +5 -13
  4. package/dist/cli/parseArgs.js.map +1 -1
  5. package/dist/components/init/types.d.ts +0 -2
  6. package/dist/components/init/types.d.ts.map +1 -1
  7. package/dist/components/screens/HelpScreen.d.ts.map +1 -1
  8. package/dist/components/screens/HelpScreen.js +0 -2
  9. package/dist/components/screens/HelpScreen.js.map +1 -1
  10. package/dist/components/screens/InitScreen.d.ts.map +1 -1
  11. package/dist/components/screens/InitScreen.js +5 -8
  12. package/dist/components/screens/InitScreen.js.map +1 -1
  13. package/dist/components/screens/StartScreen.d.ts.map +1 -1
  14. package/dist/components/screens/StartScreen.js +29 -8
  15. package/dist/components/screens/StartScreen.js.map +1 -1
  16. package/dist/components/screens/StatusScreen.d.ts.map +1 -1
  17. package/dist/components/screens/StatusScreen.js +16 -1
  18. package/dist/components/screens/StatusScreen.js.map +1 -1
  19. package/dist/services/AgentInvoker.d.ts.map +1 -1
  20. package/dist/services/AgentInvoker.js +76 -37
  21. package/dist/services/AgentInvoker.js.map +1 -1
  22. package/dist/services/ClaudeErrorDetector.d.ts +1 -1
  23. package/dist/services/ClaudeErrorDetector.d.ts.map +1 -1
  24. package/dist/services/ClaudeErrorDetector.js +1 -0
  25. package/dist/services/ClaudeErrorDetector.js.map +1 -1
  26. package/dist/services/ClaudeHealthCheck.d.ts +7 -0
  27. package/dist/services/ClaudeHealthCheck.d.ts.map +1 -1
  28. package/dist/services/ClaudeHealthCheck.js +76 -12
  29. package/dist/services/ClaudeHealthCheck.js.map +1 -1
  30. package/dist/services/ConfigService.d.ts +1 -0
  31. package/dist/services/ConfigService.d.ts.map +1 -1
  32. package/dist/services/ConfigService.js.map +1 -1
  33. package/dist/services/DockerRunner.js +1 -1
  34. package/dist/services/DockerRunner.js.map +1 -1
  35. package/dist/services/PhaseExecutor.d.ts.map +1 -1
  36. package/dist/services/PhaseExecutor.js +2 -1
  37. package/dist/services/PhaseExecutor.js.map +1 -1
  38. package/dist/services/TaskRunner.d.ts.map +1 -1
  39. package/dist/services/TaskRunner.js +2 -1
  40. package/dist/services/TaskRunner.js.map +1 -1
  41. package/dist/services/index.d.ts +1 -1
  42. package/dist/services/index.d.ts.map +1 -1
  43. package/dist/services/index.js +1 -1
  44. package/dist/services/index.js.map +1 -1
  45. package/dist/types/index.d.ts +4 -3
  46. package/dist/types/index.d.ts.map +1 -1
  47. package/dist/types/index.js.map +1 -1
  48. package/framework/stacks/c/code-quality.md +326 -0
  49. package/framework/stacks/c/coding-style.md +347 -0
  50. package/framework/stacks/c/conventions.md +513 -0
  51. package/framework/stacks/c/error-handling.md +350 -0
  52. package/framework/stacks/c/feedback.md +158 -0
  53. package/framework/stacks/c/memory-safety.md +408 -0
  54. package/framework/stacks/c/tech.md +122 -0
  55. package/framework/stacks/c/testing.md +472 -0
  56. package/framework/stacks/cpp/code-quality.md +282 -0
  57. package/framework/stacks/cpp/coding-style.md +363 -0
  58. package/framework/stacks/cpp/conventions.md +420 -0
  59. package/framework/stacks/cpp/error-handling.md +264 -0
  60. package/framework/stacks/cpp/feedback.md +104 -0
  61. package/framework/stacks/cpp/memory-safety.md +351 -0
  62. package/framework/stacks/cpp/tech.md +160 -0
  63. package/framework/stacks/cpp/testing.md +323 -0
  64. package/framework/stacks/java/code-quality.md +357 -0
  65. package/framework/stacks/java/coding-style.md +400 -0
  66. package/framework/stacks/java/conventions.md +437 -0
  67. package/framework/stacks/java/error-handling.md +408 -0
  68. package/framework/stacks/java/feedback.md +180 -0
  69. package/framework/stacks/java/tech.md +126 -0
  70. package/framework/stacks/java/testing.md +485 -0
  71. package/framework/stacks/javascript/async-patterns.md +216 -0
  72. package/framework/stacks/javascript/code-quality.md +182 -0
  73. package/framework/stacks/javascript/coding-style.md +293 -0
  74. package/framework/stacks/javascript/conventions.md +268 -0
  75. package/framework/stacks/javascript/error-handling.md +216 -0
  76. package/framework/stacks/javascript/feedback.md +80 -0
  77. package/framework/stacks/javascript/tech.md +114 -0
  78. package/framework/stacks/javascript/testing.md +209 -0
  79. package/framework/stacks/loco/code-quality.md +156 -0
  80. package/framework/stacks/loco/coding-style.md +247 -0
  81. package/framework/stacks/loco/error-handling.md +225 -0
  82. package/framework/stacks/loco/feedback.md +35 -0
  83. package/framework/stacks/loco/loco.md +342 -0
  84. package/framework/stacks/loco/structure.md +193 -0
  85. package/framework/stacks/loco/tech.md +129 -0
  86. package/framework/stacks/loco/testing.md +211 -0
  87. package/framework/stacks/rust/code-quality.md +370 -0
  88. package/framework/stacks/rust/coding-style.md +475 -0
  89. package/framework/stacks/rust/conventions.md +430 -0
  90. package/framework/stacks/rust/error-handling.md +399 -0
  91. package/framework/stacks/rust/feedback.md +152 -0
  92. package/framework/stacks/rust/memory-safety.md +398 -0
  93. package/framework/stacks/rust/tech.md +121 -0
  94. package/framework/stacks/rust/testing.md +528 -0
  95. package/package.json +14 -2
@@ -0,0 +1,528 @@
1
+ # Testing Patterns
2
+
3
+ Comprehensive testing patterns for Rust projects with `cargo-nextest`, `mockall`, `rstest`, and `proptest`.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Fast feedback**: Unit tests run in milliseconds, no I/O
10
+ - **Compile-time confidence**: The type system catches most bugs; tests cover the rest
11
+ - **Readable tests**: Arrange-Act-Assert structure, descriptive names
12
+ - **Test at the right level**: Unit tests for logic, integration tests for boundaries
13
+
14
+ ---
15
+
16
+ ## Test Organization
17
+
18
+ ### Project Layout
19
+
20
+ ```
21
+ src/
22
+ lib.rs
23
+ models/
24
+ user.rs # Contains #[cfg(test)] mod tests at bottom
25
+ post.rs
26
+ services/
27
+ user_service.rs # Contains #[cfg(test)] mod tests at bottom
28
+ tests/
29
+ common/
30
+ mod.rs # Shared test helpers and fixtures
31
+ api/
32
+ users_test.rs # Integration tests for user API
33
+ posts_test.rs
34
+ db/
35
+ user_repo_test.rs
36
+ ```
37
+
38
+ **Pattern**: Unit tests live in the same file as the code. Integration tests live in `tests/`.
39
+
40
+ ### Unit Tests (Same File)
41
+
42
+ ```rust
43
+ // src/services/user_service.rs
44
+
45
+ pub fn validate_email(email: &str) -> bool {
46
+ email.contains('@') && email.contains('.')
47
+ }
48
+
49
+ pub fn normalize_email(email: &str) -> String {
50
+ email.trim().to_lowercase()
51
+ }
52
+
53
+ #[cfg(test)]
54
+ mod tests {
55
+ use super::*;
56
+
57
+ #[test]
58
+ fn test_validate_email_valid() {
59
+ assert!(validate_email("user@example.com"));
60
+ assert!(validate_email("user@sub.example.com"));
61
+ }
62
+
63
+ #[test]
64
+ fn test_validate_email_invalid() {
65
+ assert!(!validate_email("invalid"));
66
+ assert!(!validate_email(""));
67
+ assert!(!validate_email("@example.com"));
68
+ }
69
+
70
+ #[test]
71
+ fn test_normalize_email() {
72
+ assert_eq!(normalize_email(" User@Example.COM "), "user@example.com");
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### Integration Tests (`tests/` Directory)
78
+
79
+ ```rust
80
+ // tests/api/users_test.rs
81
+ use axum::http::StatusCode;
82
+ use axum_test::TestServer;
83
+ use serde_json::json;
84
+
85
+ #[tokio::test]
86
+ async fn test_create_user_success() {
87
+ let server = spawn_test_server().await;
88
+
89
+ let response = server
90
+ .post("/api/v1/users")
91
+ .json(&json!({
92
+ "email": "new@example.com",
93
+ "name": "New User",
94
+ "password": "secure123"
95
+ }))
96
+ .await;
97
+
98
+ assert_eq!(response.status_code(), StatusCode::CREATED);
99
+ let body: serde_json::Value = response.json();
100
+ assert_eq!(body["email"], "new@example.com");
101
+ assert!(body.get("password").is_none());
102
+ }
103
+
104
+ #[tokio::test]
105
+ async fn test_create_user_duplicate_email_returns_409() {
106
+ let server = spawn_test_server().await;
107
+ seed_user(&server, "taken@example.com").await;
108
+
109
+ let response = server
110
+ .post("/api/v1/users")
111
+ .json(&json!({
112
+ "email": "taken@example.com",
113
+ "name": "Duplicate",
114
+ "password": "secure123"
115
+ }))
116
+ .await;
117
+
118
+ assert_eq!(response.status_code(), StatusCode::CONFLICT);
119
+ }
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Async Tests with `tokio::test`
125
+
126
+ ```rust
127
+ // GOOD: Async test with tokio
128
+ #[tokio::test]
129
+ async fn test_fetch_user_from_database() {
130
+ let pool = setup_test_db().await;
131
+ let user = create_test_user(&pool).await;
132
+
133
+ let found = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", user.id)
134
+ .fetch_optional(&pool)
135
+ .await
136
+ .unwrap();
137
+
138
+ assert!(found.is_some());
139
+ assert_eq!(found.unwrap().email, user.email);
140
+ }
141
+
142
+ // With custom runtime configuration
143
+ #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
144
+ async fn test_concurrent_operations() {
145
+ // ...
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Assertions
152
+
153
+ ### Standard Assertions
154
+
155
+ ```rust
156
+ #[test]
157
+ fn test_assertions() {
158
+ // Basic assertions
159
+ assert!(result.is_ok());
160
+ assert_eq!(user.name, "Alice");
161
+ assert_ne!(user.id, 0);
162
+
163
+ // With custom messages
164
+ assert!(
165
+ user.is_active,
166
+ "Expected user {} to be active, but was inactive",
167
+ user.id
168
+ );
169
+
170
+ // Pattern matching with assert_matches (nightly or use matches! macro)
171
+ assert!(matches!(result, Ok(User { is_active: true, .. })));
172
+ assert!(matches!(error, AppError::NotFound { .. }));
173
+ }
174
+ ```
175
+
176
+ ### Testing Errors
177
+
178
+ ```rust
179
+ #[test]
180
+ fn test_invalid_email_returns_error() {
181
+ let result = Email::new("invalid");
182
+ assert!(result.is_err());
183
+
184
+ let err = result.unwrap_err();
185
+ assert!(err.to_string().contains("Invalid email"));
186
+ }
187
+
188
+ #[tokio::test]
189
+ async fn test_get_missing_user_returns_not_found() {
190
+ let pool = setup_test_db().await;
191
+ let service = UserService::new(pool);
192
+
193
+ let result = service.get_user(99999).await;
194
+
195
+ assert!(matches!(result, Err(AppError::NotFound { .. })));
196
+ }
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Fixtures with `rstest`
202
+
203
+ ### Basic Fixtures
204
+
205
+ ```rust
206
+ use rstest::rstest;
207
+
208
+ #[rstest]
209
+ fn test_validate_email(
210
+ #[values("user@example.com", "admin@test.org", "a@b.co")]
211
+ valid_email: &str,
212
+ ) {
213
+ assert!(validate_email(valid_email));
214
+ }
215
+
216
+ #[rstest]
217
+ fn test_reject_invalid_email(
218
+ #[values("", "invalid", "@example.com", "user@", "user @example.com")]
219
+ invalid_email: &str,
220
+ ) {
221
+ assert!(!validate_email(invalid_email));
222
+ }
223
+ ```
224
+
225
+ ### Shared Fixtures
226
+
227
+ ```rust
228
+ use rstest::*;
229
+
230
+ #[fixture]
231
+ fn test_user() -> User {
232
+ User {
233
+ id: 1,
234
+ email: "test@example.com".to_string(),
235
+ name: "Test User".to_string(),
236
+ is_active: true,
237
+ }
238
+ }
239
+
240
+ #[fixture]
241
+ async fn test_pool() -> PgPool {
242
+ setup_test_db().await
243
+ }
244
+
245
+ #[rstest]
246
+ fn test_user_display(test_user: User) {
247
+ assert_eq!(test_user.to_string(), "Test User <test@example.com>");
248
+ }
249
+
250
+ #[rstest]
251
+ #[tokio::test]
252
+ async fn test_save_user(#[future] test_pool: PgPool, test_user: User) {
253
+ let pool = test_pool.await;
254
+ let saved = save_user(&pool, &test_user).await.unwrap();
255
+ assert!(saved.id > 0);
256
+ }
257
+ ```
258
+
259
+ ### Parametrized Tests
260
+
261
+ ```rust
262
+ #[rstest]
263
+ #[case("draft", true)]
264
+ #[case("published", false)]
265
+ #[case("archived", false)]
266
+ fn test_can_publish(#[case] status: &str, #[case] expected: bool) {
267
+ let post = Post { status: status.parse().unwrap(), ..Default::default() };
268
+ assert_eq!(post.can_publish(), expected);
269
+ }
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Mocking with `mockall`
275
+
276
+ ### Trait-Based Mocking
277
+
278
+ ```rust
279
+ use mockall::automock;
280
+
281
+ #[automock]
282
+ #[async_trait::async_trait]
283
+ pub trait UserRepository {
284
+ async fn get(&self, id: i64) -> Result<Option<User>, sqlx::Error>;
285
+ async fn get_by_email(&self, email: &str) -> Result<Option<User>, sqlx::Error>;
286
+ async fn save(&self, user: &NewUser) -> Result<User, sqlx::Error>;
287
+ }
288
+
289
+ #[tokio::test]
290
+ async fn test_create_user_success() {
291
+ // Arrange
292
+ let mut mock_repo = MockUserRepository::new();
293
+ mock_repo
294
+ .expect_get_by_email()
295
+ .with(mockall::predicate::eq("new@example.com"))
296
+ .returning(|_| Ok(None));
297
+ mock_repo
298
+ .expect_save()
299
+ .returning(|new_user| Ok(User {
300
+ id: 1,
301
+ email: new_user.email.clone(),
302
+ name: new_user.name.clone(),
303
+ is_active: true,
304
+ }));
305
+
306
+ let service = UserService::new(Box::new(mock_repo));
307
+
308
+ // Act
309
+ let result = service.create_user(&CreateUserRequest {
310
+ email: "new@example.com".to_string(),
311
+ name: "New User".to_string(),
312
+ password: "secure123".to_string(),
313
+ }).await;
314
+
315
+ // Assert
316
+ assert!(result.is_ok());
317
+ assert_eq!(result.unwrap().email, "new@example.com");
318
+ }
319
+
320
+ #[tokio::test]
321
+ async fn test_create_user_duplicate_email() {
322
+ let mut mock_repo = MockUserRepository::new();
323
+ mock_repo
324
+ .expect_get_by_email()
325
+ .returning(|_| Ok(Some(User {
326
+ id: 1,
327
+ email: "taken@example.com".to_string(),
328
+ name: "Existing".to_string(),
329
+ is_active: true,
330
+ })));
331
+
332
+ let service = UserService::new(Box::new(mock_repo));
333
+
334
+ let result = service.create_user(&CreateUserRequest {
335
+ email: "taken@example.com".to_string(),
336
+ name: "Duplicate".to_string(),
337
+ password: "secure123".to_string(),
338
+ }).await;
339
+
340
+ assert!(matches!(result, Err(AppError::Conflict(_))));
341
+ }
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Property Testing with `proptest`
347
+
348
+ ### Basic Property Tests
349
+
350
+ ```rust
351
+ use proptest::prelude::*;
352
+
353
+ proptest! {
354
+ #[test]
355
+ fn test_normalize_email_always_lowercase(email in "[a-zA-Z0-9]+@[a-zA-Z]+\\.[a-zA-Z]+") {
356
+ let normalized = normalize_email(&email);
357
+ assert_eq!(normalized, normalized.to_lowercase());
358
+ }
359
+
360
+ #[test]
361
+ fn test_parse_and_serialize_roundtrip(id in 1i64..=i64::MAX) {
362
+ let user_id = UserId(id);
363
+ let serialized = serde_json::to_string(&user_id).unwrap();
364
+ let deserialized: UserId = serde_json::from_str(&serialized).unwrap();
365
+ assert_eq!(user_id.0, deserialized.0);
366
+ }
367
+
368
+ #[test]
369
+ fn test_pagination_invariants(page in 1u32..=1000, per_page in 1u32..=100) {
370
+ let offset = calculate_offset(page, per_page);
371
+ assert!(offset < page as u64 * per_page as u64);
372
+ assert_eq!(offset, ((page - 1) as u64) * (per_page as u64));
373
+ }
374
+ }
375
+ ```
376
+
377
+ ### Custom Strategies
378
+
379
+ ```rust
380
+ use proptest::prelude::*;
381
+
382
+ fn valid_email_strategy() -> impl Strategy<Value = String> {
383
+ ("[a-z]{1,10}", "[a-z]{1,10}", "[a-z]{2,4}")
384
+ .prop_map(|(user, domain, tld)| format!("{user}@{domain}.{tld}"))
385
+ }
386
+
387
+ proptest! {
388
+ #[test]
389
+ fn test_email_validation_accepts_valid(email in valid_email_strategy()) {
390
+ assert!(validate_email(&email));
391
+ }
392
+ }
393
+ ```
394
+
395
+ ---
396
+
397
+ ## Test Execution with `cargo-nextest`
398
+
399
+ ### Running Tests
400
+
401
+ ```bash
402
+ # Install
403
+ cargo install cargo-nextest
404
+
405
+ # Run all tests
406
+ cargo nextest run
407
+
408
+ # Run with output
409
+ cargo nextest run --no-capture
410
+
411
+ # Run specific test
412
+ cargo nextest run test_create_user
413
+
414
+ # Run tests in specific module
415
+ cargo nextest run --filter-expr 'test(user_service)'
416
+
417
+ # Run with retries for flaky tests
418
+ cargo nextest run --retries 2
419
+
420
+ # Run only integration tests
421
+ cargo nextest run --filter-expr 'kind(test)'
422
+
423
+ # Run with specific number of threads
424
+ cargo nextest run -j 4
425
+ ```
426
+
427
+ ### `.config/nextest.toml`
428
+
429
+ ```toml
430
+ [profile.default]
431
+ retries = 0
432
+ slow-timeout = { period = "30s", terminate-after = 2 }
433
+ fail-fast = true
434
+
435
+ [profile.ci]
436
+ retries = 2
437
+ fail-fast = false
438
+ ```
439
+
440
+ ---
441
+
442
+ ## Coverage with `cargo-llvm-cov`
443
+
444
+ ```bash
445
+ # Install
446
+ cargo install cargo-llvm-cov
447
+
448
+ # Run with coverage
449
+ cargo llvm-cov
450
+
451
+ # Generate HTML report
452
+ cargo llvm-cov --html
453
+ open target/llvm-cov/html/index.html
454
+
455
+ # Fail if coverage is below threshold
456
+ cargo llvm-cov --fail-under-lines 80
457
+
458
+ # Generate LCOV for CI integration
459
+ cargo llvm-cov --lcov --output-path lcov.info
460
+
461
+ # Coverage for nextest
462
+ cargo llvm-cov nextest
463
+ ```
464
+
465
+ ---
466
+
467
+ ## Test Markers and Attributes
468
+
469
+ ```rust
470
+ // Ignore slow tests by default
471
+ #[test]
472
+ #[ignore]
473
+ fn test_full_pipeline() {
474
+ // Run with: cargo test -- --ignored
475
+ }
476
+
477
+ // Conditional compilation
478
+ #[cfg(feature = "integration-tests")]
479
+ #[tokio::test]
480
+ async fn test_real_database() {
481
+ // Run with: cargo test --features integration-tests
482
+ }
483
+
484
+ // Should panic
485
+ #[test]
486
+ #[should_panic(expected = "index out of bounds")]
487
+ fn test_index_out_of_bounds() {
488
+ let v: Vec<i32> = vec![];
489
+ let _ = v[0];
490
+ }
491
+ ```
492
+
493
+ ---
494
+
495
+ ## Test Commands Summary
496
+
497
+ ```bash
498
+ # Fast feedback
499
+ cargo nextest run -j auto # All tests, parallel
500
+ cargo nextest run --filter-expr 'test(user)' # Pattern match
501
+ cargo test --doc # Doctests only
502
+
503
+ # Full suite
504
+ cargo nextest run --all-features # All features enabled
505
+ cargo llvm-cov nextest --fail-under-lines 80 # With coverage gate
506
+
507
+ # Specific targets
508
+ cargo nextest run -p my-crate # Single crate in workspace
509
+ cargo nextest run --filter-expr 'kind(test)' # Integration tests only
510
+ ```
511
+
512
+ ---
513
+
514
+ ## Anti-Patterns
515
+
516
+ | Anti-Pattern | Problem | Correct Approach |
517
+ |---|---|---|
518
+ | `#[test] fn test1()` | Non-descriptive name | `fn test_create_user_with_duplicate_email_returns_conflict()` |
519
+ | Testing implementation details | Brittle, breaks on refactor | Test behavior and public API |
520
+ | No async test runtime | `async fn` tests silently do nothing | Use `#[tokio::test]` |
521
+ | Shared mutable state between tests | Flaky, order-dependent | Each test sets up its own state |
522
+ | `.unwrap()` without context in tests | Confusing failure messages | Use `.expect("reason")` |
523
+ | Giant test functions | Hard to identify failure | One assertion per logical concept |
524
+ | Mocking everything | Tests prove nothing | Mock boundaries, test logic directly |
525
+
526
+ ---
527
+
528
+ _Tests document behavior. Each test should read as a specification of what the code does._
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "red64-cli",
3
- "version": "0.3.0",
3
+ "version": "0.6.0",
4
4
  "description": "Red64 Flow Orchestrator - Deterministic spec-driven development CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,8 +24,20 @@
24
24
  "workflow",
25
25
  "orchestration"
26
26
  ],
27
- "author": "Yacin Bahi <yacin@red64.io>",
27
+ "author": {
28
+ "name": "Yacin Bahi",
29
+ "email": "yacin@red64.io",
30
+ "url": "https://red64.io"
31
+ },
28
32
  "license": "MIT",
33
+ "homepage": "https://github.com/Red64llc/red64-cli#readme",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/Red64llc/red64-cli.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/Red64llc/red64-cli/issues"
40
+ },
29
41
  "dependencies": {
30
42
  "@inkjs/ui": "^2.0.0",
31
43
  "date-fns": "^3.6.0",