agentic-team-templates 0.9.2 → 0.11.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.
@@ -0,0 +1,300 @@
1
+ # Rust Testing
2
+
3
+ Rust has testing built into the language and toolchain. `cargo test` runs unit tests, integration tests, and doc tests in one command.
4
+
5
+ ## Unit Tests
6
+
7
+ ### Module-Level Tests
8
+
9
+ ```rust
10
+ // Tests live in the same file as the code they test
11
+ pub fn celsius_to_fahrenheit(c: f64) -> f64 {
12
+ c * 9.0 / 5.0 + 32.0
13
+ }
14
+
15
+ #[cfg(test)]
16
+ mod tests {
17
+ use super::*;
18
+
19
+ #[test]
20
+ fn freezing_point() {
21
+ assert!((celsius_to_fahrenheit(0.0) - 32.0).abs() < f64::EPSILON);
22
+ }
23
+
24
+ #[test]
25
+ fn boiling_point() {
26
+ assert!((celsius_to_fahrenheit(100.0) - 212.0).abs() < f64::EPSILON);
27
+ }
28
+
29
+ #[test]
30
+ fn negative_temperature() {
31
+ assert!((celsius_to_fahrenheit(-40.0) - -40.0).abs() < f64::EPSILON);
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### Testing Error Cases
37
+
38
+ ```rust
39
+ #[test]
40
+ fn parse_invalid_input_returns_error() {
41
+ let result = parse_config("not valid toml");
42
+ assert!(result.is_err());
43
+
44
+ let err = result.unwrap_err();
45
+ assert!(err.to_string().contains("expected"));
46
+ }
47
+
48
+ #[test]
49
+ fn empty_name_fails_validation() {
50
+ let input = CreateUserInput { name: "".into(), email: "a@b.com".into() };
51
+ let result = validate_user(&input);
52
+
53
+ assert!(matches!(result, Err(ValidationError::EmptyField { field }) if field == "name"));
54
+ }
55
+
56
+ // Testing that something panics
57
+ #[test]
58
+ #[should_panic(expected = "index out of bounds")]
59
+ fn panics_on_out_of_bounds() {
60
+ let v = vec![1, 2, 3];
61
+ let _ = v[10];
62
+ }
63
+ ```
64
+
65
+ ### Test Organization
66
+
67
+ ```rust
68
+ // #[cfg(test)] ensures test code is never compiled into release builds
69
+ #[cfg(test)]
70
+ mod tests {
71
+ use super::*;
72
+
73
+ // Shared test fixtures
74
+ fn sample_user() -> User {
75
+ User {
76
+ id: UserId::new("test-123"),
77
+ name: "Alice".into(),
78
+ email: "alice@example.com".into(),
79
+ }
80
+ }
81
+
82
+ // Group related tests with nested modules
83
+ mod validation {
84
+ use super::*;
85
+
86
+ #[test]
87
+ fn rejects_empty_name() { ... }
88
+
89
+ #[test]
90
+ fn rejects_invalid_email() { ... }
91
+ }
92
+
93
+ mod serialization {
94
+ use super::*;
95
+
96
+ #[test]
97
+ fn round_trips_through_json() { ... }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Integration Tests
103
+
104
+ ```rust
105
+ // tests/api_test.rs — separate compilation unit, tests public API only
106
+ use my_crate::{Config, Server};
107
+
108
+ #[test]
109
+ fn server_responds_to_health_check() {
110
+ let config = Config::default();
111
+ let server = Server::new(config);
112
+
113
+ let response = server.handle_request("/healthz");
114
+
115
+ assert_eq!(response.status(), 200);
116
+ assert_eq!(response.body(), r#"{"status":"ok"}"#);
117
+ }
118
+ ```
119
+
120
+ ## Async Tests
121
+
122
+ ```rust
123
+ #[tokio::test]
124
+ async fn fetches_user_by_id() {
125
+ let db = setup_test_db().await;
126
+ let repo = UserRepo::new(db);
127
+
128
+ let user = repo.find_by_id("123").await.unwrap();
129
+
130
+ assert_eq!(user.name, "Alice");
131
+ }
132
+
133
+ // With custom runtime config
134
+ #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
135
+ async fn concurrent_operations() {
136
+ // ...
137
+ }
138
+ ```
139
+
140
+ ## Property-Based Testing
141
+
142
+ ```rust
143
+ use proptest::prelude::*;
144
+
145
+ proptest! {
146
+ #[test]
147
+ fn parse_never_panics(s in "\\PC*") {
148
+ // Whatever string we throw at parse, it must not panic
149
+ let _ = parse_input(&s);
150
+ }
151
+
152
+ #[test]
153
+ fn serialization_round_trips(
154
+ name in "[a-zA-Z]{1,50}",
155
+ age in 0u32..150,
156
+ ) {
157
+ let user = User { name, age };
158
+ let json = serde_json::to_string(&user).unwrap();
159
+ let decoded: User = serde_json::from_str(&json).unwrap();
160
+ assert_eq!(user, decoded);
161
+ }
162
+ }
163
+ ```
164
+
165
+ ## Doc Tests
166
+
167
+ ```rust
168
+ /// Adds two numbers together.
169
+ ///
170
+ /// # Examples
171
+ ///
172
+ /// ```
173
+ /// use my_crate::add;
174
+ ///
175
+ /// assert_eq!(add(2, 3), 5);
176
+ /// assert_eq!(add(-1, 1), 0);
177
+ /// ```
178
+ ///
179
+ /// # Panics
180
+ ///
181
+ /// Panics if the result overflows.
182
+ ///
183
+ /// ```should_panic
184
+ /// use my_crate::add;
185
+ ///
186
+ /// add(i64::MAX, 1); // Overflow!
187
+ /// ```
188
+ pub fn add(a: i64, b: i64) -> i64 {
189
+ a.checked_add(b).expect("overflow")
190
+ }
191
+
192
+ // Doc tests are compiled and run — they're both documentation AND tests.
193
+ // They verify your examples actually work.
194
+ ```
195
+
196
+ ## Mocking and Test Doubles
197
+
198
+ ```rust
199
+ // Prefer trait-based dependency injection over mocking frameworks
200
+
201
+ // Define trait at the consumer
202
+ trait Clock {
203
+ fn now(&self) -> DateTime<Utc>;
204
+ }
205
+
206
+ // Production implementation
207
+ struct SystemClock;
208
+ impl Clock for SystemClock {
209
+ fn now(&self) -> DateTime<Utc> { Utc::now() }
210
+ }
211
+
212
+ // Test implementation — deterministic
213
+ struct FakeClock(DateTime<Utc>);
214
+ impl Clock for FakeClock {
215
+ fn now(&self) -> DateTime<Utc> { self.0 }
216
+ }
217
+
218
+ #[test]
219
+ fn token_expires_after_one_hour() {
220
+ let fixed_time = Utc.with_ymd_and_hms(2025, 1, 1, 12, 0, 0).unwrap();
221
+ let clock = FakeClock(fixed_time);
222
+ let token = generate_token(&clock);
223
+
224
+ let check_time = fixed_time + Duration::hours(2);
225
+ assert!(token.is_expired_at(check_time));
226
+ }
227
+
228
+ // When you do need a mocking library: mockall
229
+ #[automock]
230
+ trait Database {
231
+ fn get(&self, key: &str) -> Option<String>;
232
+ }
233
+
234
+ #[test]
235
+ fn returns_cached_value() {
236
+ let mut mock = MockDatabase::new();
237
+ mock.expect_get()
238
+ .with(eq("user:123"))
239
+ .returning(|_| Some("Alice".into()));
240
+
241
+ let service = Service::new(mock);
242
+ assert_eq!(service.get_user("123"), Some("Alice".into()));
243
+ }
244
+ ```
245
+
246
+ ## Benchmarks
247
+
248
+ ```rust
249
+ // Using criterion (de facto standard)
250
+ // benches/my_benchmark.rs
251
+ use criterion::{criterion_group, criterion_main, Criterion, black_box};
252
+
253
+ fn bench_parse(c: &mut Criterion) {
254
+ let input = include_str!("../testdata/large_input.txt");
255
+
256
+ c.bench_function("parse_large_input", |b| {
257
+ b.iter(|| parse(black_box(input)))
258
+ });
259
+ }
260
+
261
+ fn bench_comparison(c: &mut Criterion) {
262
+ let mut group = c.benchmark_group("serialization");
263
+ let data = generate_test_data();
264
+
265
+ group.bench_function("json", |b| {
266
+ b.iter(|| serde_json::to_vec(black_box(&data)))
267
+ });
268
+
269
+ group.bench_function("bincode", |b| {
270
+ b.iter(|| bincode::serialize(black_box(&data)))
271
+ });
272
+
273
+ group.finish();
274
+ }
275
+
276
+ criterion_group!(benches, bench_parse, bench_comparison);
277
+ criterion_main!(benches);
278
+ ```
279
+
280
+ ## Test Anti-Patterns
281
+
282
+ ```rust
283
+ // Never: Tests that depend on execution order
284
+ // Each test runs in its own thread — no shared mutable state
285
+
286
+ // Never: Ignoring tests instead of fixing them
287
+ #[ignore] // Why? For how long? File an issue.
288
+
289
+ // Never: Testing private internals through hacks
290
+ // If you can't test it through the public API, the design needs work
291
+
292
+ // Never: Assertions without messages in complex tests
293
+ assert!(result.is_ok()); // What was the input? What was the error?
294
+ // Better:
295
+ assert!(result.is_ok(), "expected Ok for input {input:?}, got {result:?}");
296
+
297
+ // Never: Flaky tests
298
+ // Flaky = broken. Fix the race condition or the timing dependency.
299
+ // Use deterministic clocks, seeded RNGs, and proper synchronization.
300
+ ```
@@ -0,0 +1,236 @@
1
+ # Rust Traits and Generics
2
+
3
+ Traits are Rust's mechanism for abstraction, polymorphism, and code reuse. Combined with generics, they enable zero-cost abstractions that rival hand-written specialized code.
4
+
5
+ ## Trait Design
6
+
7
+ ### Small, Focused Traits
8
+
9
+ ```rust
10
+ // Good: Single-purpose traits
11
+ pub trait Validate {
12
+ fn validate(&self) -> Result<(), ValidationError>;
13
+ }
14
+
15
+ pub trait Serialize {
16
+ fn serialize(&self, writer: &mut dyn Write) -> Result<(), SerializeError>;
17
+ }
18
+
19
+ // Bad: God trait
20
+ pub trait Entity {
21
+ fn validate(&self) -> Result<(), Error>;
22
+ fn serialize(&self) -> Vec<u8>;
23
+ fn save(&self, db: &Database) -> Result<(), Error>;
24
+ fn render(&self) -> Html;
25
+ // Too many responsibilities — split by concern
26
+ }
27
+ ```
28
+
29
+ ### Extension Traits
30
+
31
+ ```rust
32
+ // Add methods to existing types via extension traits
33
+ pub trait StrExt {
34
+ fn is_blank(&self) -> bool;
35
+ fn truncate_to(&self, max_len: usize) -> &str;
36
+ }
37
+
38
+ impl StrExt for str {
39
+ fn is_blank(&self) -> bool {
40
+ self.trim().is_empty()
41
+ }
42
+
43
+ fn truncate_to(&self, max_len: usize) -> &str {
44
+ if self.len() <= max_len {
45
+ self
46
+ } else {
47
+ // Find a char boundary to avoid panic
48
+ let mut end = max_len;
49
+ while !self.is_char_boundary(end) {
50
+ end -= 1;
51
+ }
52
+ &self[..end]
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ ### Sealed Traits
59
+
60
+ ```rust
61
+ // Prevent external implementations when your trait is part of an internal contract
62
+ mod private {
63
+ pub trait Sealed {}
64
+ }
65
+
66
+ pub trait Backend: private::Sealed {
67
+ fn execute(&self, query: &str) -> Result<Rows>;
68
+ }
69
+
70
+ // Only types in your crate can implement Sealed, so only they can implement Backend
71
+ impl private::Sealed for PostgresBackend {}
72
+ impl Backend for PostgresBackend { ... }
73
+ ```
74
+
75
+ ### The Newtype Pattern
76
+
77
+ ```rust
78
+ // Implement foreign traits on foreign types via newtype
79
+ struct Meters(f64);
80
+ struct Seconds(f64);
81
+
82
+ impl fmt::Display for Meters {
83
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84
+ write!(f, "{:.2}m", self.0)
85
+ }
86
+ }
87
+
88
+ // Also prevents mixing units at compile time
89
+ fn speed(distance: Meters, time: Seconds) -> f64 {
90
+ distance.0 / time.0
91
+ }
92
+ // speed(Seconds(1.0), Meters(5.0)) — compile error! Arguments are swapped.
93
+ ```
94
+
95
+ ## Generics
96
+
97
+ ### Trait Bounds
98
+
99
+ ```rust
100
+ // Prefer impl Trait for simple cases
101
+ fn print_all(items: &[impl Display]) {
102
+ for item in items {
103
+ println!("{item}");
104
+ }
105
+ }
106
+
107
+ // Use where clauses for complex bounds
108
+ fn merge<T, U>(left: T, right: U) -> Merged
109
+ where
110
+ T: IntoIterator<Item = Record>,
111
+ U: IntoIterator<Item = Record>,
112
+ T::IntoIter: ExactSizeIterator,
113
+ {
114
+ // ...
115
+ }
116
+
117
+ // Use explicit generic parameters when the caller needs to specify the type
118
+ fn parse<T: FromStr>(input: &str) -> Result<T, T::Err> {
119
+ input.parse()
120
+ }
121
+ let n: i32 = parse("42")?;
122
+ let f: f64 = parse("3.14")?;
123
+ ```
124
+
125
+ ### Associated Types vs Generic Parameters
126
+
127
+ ```rust
128
+ // Associated types: one implementation per type
129
+ trait Iterator {
130
+ type Item; // Each iterator has exactly one Item type
131
+ fn next(&mut self) -> Option<Self::Item>;
132
+ }
133
+
134
+ // Generic parameters: multiple implementations per type
135
+ trait Convert<T> {
136
+ fn convert(&self) -> T;
137
+ }
138
+ // A single type can implement Convert<String>, Convert<i32>, etc.
139
+
140
+ // Rule of thumb: If there should be only one implementation
141
+ // for a given Self type, use an associated type.
142
+ ```
143
+
144
+ ### Phantom Types
145
+
146
+ ```rust
147
+ use std::marker::PhantomData;
148
+
149
+ // Type-state pattern: encode state in the type system
150
+ struct Validated;
151
+ struct Unvalidated;
152
+
153
+ struct Form<State> {
154
+ data: FormData,
155
+ _state: PhantomData<State>,
156
+ }
157
+
158
+ impl Form<Unvalidated> {
159
+ fn new(data: FormData) -> Self {
160
+ Form { data, _state: PhantomData }
161
+ }
162
+
163
+ fn validate(self) -> Result<Form<Validated>, ValidationError> {
164
+ // validation logic...
165
+ Ok(Form { data: self.data, _state: PhantomData })
166
+ }
167
+ }
168
+
169
+ impl Form<Validated> {
170
+ fn submit(self) -> Result<(), SubmitError> {
171
+ // Only validated forms can be submitted
172
+ send(self.data)
173
+ }
174
+ }
175
+
176
+ // Form::new(data).submit() — compile error! Must validate first.
177
+ ```
178
+
179
+ ## Dynamic Dispatch
180
+
181
+ ```rust
182
+ // Static dispatch (monomorphization) — zero cost, larger binary
183
+ fn process(handler: impl Handler) { handler.handle(); }
184
+
185
+ // Dynamic dispatch (trait objects) — vtable indirection, smaller binary
186
+ fn process(handler: &dyn Handler) { handler.handle(); }
187
+
188
+ // Use trait objects when:
189
+ // - You need a heterogeneous collection
190
+ // - You want to reduce binary size (e.g., embedded)
191
+ // - The type is determined at runtime
192
+
193
+ // Trait object safety: a trait is object-safe if:
194
+ // - No methods return Self
195
+ // - No methods have generic type parameters
196
+ // - No associated functions (no &self receiver)
197
+ ```
198
+
199
+ ## Derive and Common Traits
200
+
201
+ ```rust
202
+ // Derive what you can — it's correct and free
203
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
204
+ pub struct UserId(String);
205
+
206
+ // Standard trait hierarchy to consider:
207
+ // Debug — always derive, essential for diagnostics
208
+ // Clone — when values need to be duplicated
209
+ // PartialEq/Eq — when values need comparison
210
+ // Hash — when used as map keys (requires Eq)
211
+ // Default — when a meaningful zero value exists
212
+ // Display — for user-facing output (implement manually)
213
+ // Serialize/Deserialize — for serde (derive with feature flag)
214
+
215
+ // Don't derive Copy unless the type is truly trivially copyable
216
+ // and you want implicit copies. Copy types can't have Drop.
217
+ ```
218
+
219
+ ## Anti-Patterns
220
+
221
+ ```rust
222
+ // Never: Trait objects everywhere when generics would work
223
+ // Box<dyn Fn()> is fine for callbacks stored in structs
224
+ // impl Fn() is better for function parameters
225
+
226
+ // Never: Overusing generics for types that will only ever have one concrete type
227
+ fn process<T: Into<String>>(name: T) { } // Just take String or &str
228
+
229
+ // Never: Unused type parameters
230
+ struct Wrapper<T> { // T is not used — won't compile without PhantomData
231
+ data: Vec<u8>,
232
+ }
233
+
234
+ // Never: Implementing traits for types you don't own without a newtype
235
+ // Orphan rule prevents this anyway — respect it
236
+ ```