-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdoc.go
More file actions
79 lines (79 loc) · 3.79 KB
/
doc.go
File metadata and controls
79 lines (79 loc) · 3.79 KB
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
// Package deterministic provides deterministic implementations of functions
// commonly replaced during testing, enabling reliable and reproducible test execution.
//
// # Why Deterministic Functions Matter in Testing
//
// Many Go applications rely on non-deterministic sources of data:
// - Cryptographic random number generation (crypto/rand)
// - Current system time (time.Now)
// - Timestamped log output
// - OpenTelemetry trace and span IDs and timestamps
//
// These functions are essential for production code but problematic for testing because:
//
// 1. Different results on every run: Tests that depend on random data or current time
// may pass one moment and fail the next, making debugging difficult.
//
// 2. Flaky tests: Timing-sensitive tests may fail intermittently on slow systems
// or under load, while passing locally.
//
// 3. Non-reproducible failures: When a test fails, running it again may produce
// different results, making root cause analysis nearly impossible.
//
// 4. Hard to verify behavior: Testing code that processes arbitrary random data
// or timestamps requires either mocking complex behavior or accepting imprecise
// test assertions.
//
// # What This Package Provides
//
// The deterministic package offers drop-in replacements for common non-deterministic
// functions. Each function returns a "generator" that produces predictable output:
//
// [RandFunc] returns a function with the signature of crypto/rand.Read that generates
// a predictable byte sequence. Each call fills the buffer with a signature sequence
// (0xdeadbeef) followed by incrementing bytes, allowing tests to verify cryptographic
// operations with consistent, reproducible output.
//
// [NowFunc] returns a function with the signature of time.Now that reports a fixed
// starting time (2006-01-02T15:04:05Z) and increments by exactly one second on each call.
// This allows tests to control time progression precisely without relying on actual
// system time.
//
// [SlogHandler] wraps any slog.Handler and replaces all log record
// timestamps with deterministic values from a [NowFunc]. This ensures that timestamped
// log output is reproducible and can be used in golden file tests or snapshot testing.
//
// [TraceRecorder] provides an in-memory OpenTelemetry trace provider whose span
// and trace IDs are generated by a fixed-seed [IDGenerator] and whose timestamps
// come from a [NowFunc]. The recorder returns [Tracer] and [Span] wrappers that
// inject deterministic timestamps into every span start and end call, making
// traced code fully reproducible. The caller is responsible for calling
// [TraceRecorder.Shutdown] when the recorder is no longer needed.
//
// # Usage Pattern
//
// The typical pattern is to inject these deterministic functions during test setup:
//
// func TestMyFunction(t *testing.T) {
// randFunc := deterministic.RandFunc()
// nowFunc := deterministic.NowFunc()
//
// // Pass randFunc to code that normally uses crypto/rand.Read
// // Pass nowFunc to code that normally uses time.Now
// // Use SlogHandler to wrap your slog handler
// }
//
// Each time you call [RandFunc], [NowFunc], [NewSlogHandler], or
// [NewTraceRecorder], you get a fresh instance with its own independent state.
// This allows multiple tests or sub-tests to use deterministic functions without
// interference.
//
// # Benefits for Test Quality
//
// Using deterministic functions throughout your tests provides several advantages:
// - Reproducible test results: Same code produces same output every time.
// - Faster test execution: No need to wait for timeouts or handle timing flakes.
// - Easier debugging: Failures can be reproduced exactly.
// - Better assertions: You can make precise assertions about output.
// - Snapshot testing: Log output and generated data can be captured in golden files.
package deterministic