Testing & Contributing
This chapter covers the testing architecture, contribution guidelines, and development workflows for the trueblocks-dalle library.
Test Architecture
The library includes comprehensive test coverage across multiple layers:
Unit Tests
Core Components:
- Context creation and management (
context_test.go) - Series operations and filtering (
series_test.go) - DalleDress creation and templating (
pkg/model/dalledress_test.go) - Progress tracking and metrics (
pkg/progress/progress_test.go) - Storage operations (
pkg/storage/*_test.go) - Image annotation (
pkg/annotate/annotate_test.go)
Key Test Areas:
- Attribute derivation determinism
- Template execution correctness
- Series filtering logic
- Progress phase transitions
- Cache management and validation
- Error handling and recovery
Integration Tests
API Integration:
- Text-to-speech functionality (
text2speech_test.go) - Image generation pipeline (requires API key)
- End-to-end generation workflow
Storage Integration:
- File system operations
- Directory structure creation
- Artifact persistence and retrieval
Running Tests
Basic Test Execution
# Run all tests
go test ./...
# Run with verbose output
go test -v ./...
# Run specific package tests
go test ./pkg/progress
go test ./pkg/model
Test Categories
Offline Tests (no API required):
# Ensure no API key is set for deterministic results
unset OPENAI_API_KEY
go test ./pkg/... -short
Integration Tests (API required):
export OPENAI_API_KEY="sk-..."
go test ./... -run Integration
Benchmarks:
go test -bench=. ./pkg/storage
go test -bench=BenchmarkAttribute ./pkg/prompt
Test Configuration
Environment Variables:
# Disable enhancement for deterministic tests
export TB_DALLE_NO_ENHANCE=1
# Use test data directory
export TB_DALLE_DATA_DIR="/tmp/dalle-test"
# Enable debug logging for tests
export TB_DALLE_LOG_LEVEL=debug
Test Utilities and Helpers
Context Management
// Reset context cache between tests
func TestExample(t *testing.T) {
defer dalle.ResetContextManagerForTest()
// Test logic here
}
Progress Testing
// Reset progress metrics for clean test state
func TestProgressFlow(t *testing.T) {
defer progress.ResetMetricsForTest()
// Progress testing logic
}
Mock Infrastructure
Time Mocking (progress tests):
type mockClock struct {
current time.Time
}
func (m *mockClock) Now() time.Time {
return m.current
}
func (m *mockClock) Advance(d time.Duration) {
m.current = m.current.Add(d)
}
Test Data Generation:
func generateTestSeries(t *testing.T, suffix string) Series {
return Series{
Suffix: suffix,
Purpose: "test series",
Adjectives: []string{"test", "mock", "example"},
Nouns: []string{"warrior", "scholar"},
// ... additional test attributes
}
}
Testing Best Practices
Deterministic Testing
Seed-Based Tests:
func TestAttributeSelection(t *testing.T) {
tests := []struct {
seed string
expected map[string]string
}{
{
seed: "0x1234567890abcdef",
expected: map[string]string{
"adjective": "expected_adjective",
"noun": "expected_noun",
},
},
}
for _, tt := range tests {
// Test deterministic attribute selection
}
}
Template Testing:
func TestPromptGeneration(t *testing.T) {
dd := &model.DalleDress{
// Initialize with known attributes
}
result, err := dd.ExecuteTemplate(template, filter)
assert.NoError(t, err)
assert.Contains(t, result, "expected content")
}
Error Handling Tests
func TestErrorScenarios(t *testing.T) {
tests := []struct {
name string
setupFunc func()
expectedError string
}{
{
name: "missing API key",
setupFunc: func() { os.Unsetenv("OPENAI_API_KEY") },
expectedError: "API key required",
},
{
name: "invalid series",
setupFunc: func() { /* setup invalid series */ },
expectedError: "series not found",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.setupFunc()
_, err := dalle.GenerateAnnotatedImage("test", "0x123", false, 0)
assert.Error(t, err)
assert.Contains(t, err.Error(), tt.expectedError)
})
}
}
Performance Testing
func BenchmarkDatabaseLookup(b *testing.B) {
cm := storage.GetCacheManager()
cm.LoadOrBuild()
b.ResetTimer()
for i := 0; i < b.N; i++ {
db := cm.GetDatabase("adjectives")
_ = db.Records[i%len(db.Records)]
}
}
Contributing Guidelines
Development Workflow
- Issue Creation: Open an issue describing the change and rationale
- Fork & Branch: Create feature branch (
feat/<topic>) or bugfix branch (fix/<topic>) - Implementation: Write code with comprehensive tests
- Testing: Ensure all tests pass locally
- Documentation: Update relevant documentation
- Pull Request: Submit PR with clear description and code references
Branch Naming
feat/new-attribute-database # New feature
fix/progress-tracking-bug # Bug fix
docs/api-reference-update # Documentation
refactor/storage-optimization # Code improvement
Commit Guidelines
Format: type(scope): description
feat(prompt): add support for custom templates
fix(storage): handle permission errors gracefully
docs(api): update function signatures in reference
test(progress): add comprehensive phase testing
Code Quality Standards
Formatting:
go fmt ./...
go vet ./...
golint ./...
Testing Requirements:
- All new code must include tests
- Maintain or improve coverage percentage
- Include both positive and negative test cases
- Test error conditions and edge cases
Documentation:
- Update book chapters for user-facing changes
- Add inline documentation for exported functions
- Include code examples for new features
Code Style Guidelines
General Principles:
- Prefer early returns over deep nesting
- Keep exported API surface minimal
- Use structured logging with key/value pairs
- Follow Go idioms and conventions
Error Handling:
// Good: Specific error types
if err != nil {
return fmt.Errorf("failed to load series %s: %w", series, err)
}
// Good: Early return
if condition {
return result, nil
}
// Continue with main logic
Logging:
// Good: Structured logging
logger.Info("generation.start", "series", series, "address", address)
// Good: Error context
logger.Error("database.load.failed", "database", dbName, "error", err)
Adding New Features
New Attribute Database
- Add CSV file to
pkg/storage/databases/ - Update database extraction in cache management
- Add attribute method to
DalleDress - Update templates to use new attribute
- Add series filtering support
- Write comprehensive tests
- Update documentation
New Template Type
- Define template string in
pkg/prompt/prompt.go - Compile template in context initialization
- Add execution method to
DalleDress - Add output directory handling
- Update artifact pipeline
- Test template rendering
New Progress Phase
- Add phase constant to
pkg/progress/progress.go - Update OrderedPhases slice
- Add transition points in generation pipeline
- Update progress calculations
- Test phase ordering and timing
Performance Considerations
Optimization Guidelines
Context Caching:
- Avoid forcing context reloads in hot paths
- Use appropriate TTL settings for your use case
- Monitor context cache hit rates
Database Operations:
- Binary cache provides 50x speedup over CSV parsing
- Validate cache integrity on startup
- Rebuild cache automatically on corruption
Image Processing:
- Batch operations when possible
- Use appropriate image sizes for use case
- Consider caching annotated images
Memory Management:
- Context cache uses LRU eviction
- DalleDress cache has per-context limits
- Monitor memory usage in long-running applications
Benchmarking
# Database operations
go test -bench=BenchmarkDatabase ./pkg/storage
# Template execution
go test -bench=BenchmarkTemplate ./pkg/model
# Progress tracking
go test -bench=BenchmarkProgress ./pkg/progress
Debugging and Troubleshooting
Debug Configuration
# Enable debug logging
export TB_DALLE_LOG_LEVEL=debug
# Disable caching for testing
export TB_DALLE_NO_CACHE=1
# Use test mode (mocks API calls)
export TB_DALLE_TEST_MODE=1
Common Issues
Context Loading Failures:
- Check series file permissions
- Verify JSON format validity
- Ensure data directory accessibility
Cache Problems:
- Clear cache directory:
rm -rf $TB_DALLE_DATA_DIR/cache - Check disk space and permissions
- Verify embedded database integrity
API Integration Issues:
- Validate API key format and permissions
- Check network connectivity
- Monitor rate limits and quotas
This comprehensive testing and contribution framework ensures the library maintains high quality while remaining accessible to new contributors.