Files
golib/certificates/TESTING.md
nabbar 344498a7d8 Improvements, test & documentatons (2025-11 #3)
[root]
- UPDATE documentation: enhanced README and TESTING guidelines
- UPDATE security md file: fix minimal go version needed
- ADD script: add coverage_report.sh script (see TESTING for info)

[ioutils/aggregator]
- ADD package: add new package to simplify aggregation of multiple write
  to a unique writer function
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases

[router]
- UPDATE documentation

[semaphore]
- FIX bug if given context is nil or have error trigger

[shell]
- UPDATE package & sub-package: fix bugs and optimize code
- ADD sub-package tty: allow to backup and restore tty setting
- ADD documentation: add enhanced README and TESTING guidelines
- ADD tests: complete test suites with benchmarks, concurrency, and edge cases

[socket]
- UPDATE package & sub-package: rename function Handler to HandlerFunc
- UPDATE package & sub-package: add new interface Handler to expose a
  socket compatible handler function

[Other]
- UPDATE go.mod: bump dependencies
2025-11-22 18:04:16 +01:00

598 lines
14 KiB
Markdown

# Testing Guide - Certificates Package
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.24-blue)](https://golang.org/)
Comprehensive testing documentation for the certificates package and subpackages.
---
## Table of Contents
- [Overview](#overview)
- [Test Framework](#test-framework)
- [Running Tests](#running-tests)
- [Test Coverage](#test-coverage)
- [Test Organization](#test-organization)
- [Writing Tests](#writing-tests)
- [Best Practices](#best-practices)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
- [Resources](#resources)
---
## Overview
The certificates package uses **Ginkgo v2** (BDD testing framework) and **Gomega** (matcher library) to achieve comprehensive test coverage across all subpackages.
### Test Philosophy
1. **Behavior-Driven**: Tests describe behavior in readable, hierarchical structures
2. **Security-Focused**: Verify secure defaults and TLS configuration correctness
3. **Format Coverage**: Test multiple encoding formats (JSON, YAML, TOML, CBOR)
4. **Parser Robustness**: Test parsing with valid and invalid inputs
5. **Maintainable**: Clear test structure with reusable patterns
### Coverage Scope
- ✅ TLS configuration management and generation
- ✅ Certificate parsing and validation
- ✅ CA certificate management (root and client)
- ✅ TLS version, cipher suite, and curve configuration
- ✅ Client authentication modes
- ✅ Multiple encoding formats (JSON, YAML, TOML, CBOR)
- ✅ Parser edge cases and error handling
---
## Test Framework
### Ginkgo v2
[Ginkgo](https://onsi.github.io/ginkgo/) is a BDD-style testing framework providing:
- Hierarchical test organization (`Describe`, `Context`, `It` blocks)
- Setup/teardown hooks (`BeforeEach`, `AfterEach`, `DeferCleanup`)
- Parallel test execution
- Rich CLI with filtering and reporting
- Excellent failure diagnostics
### Gomega
[Gomega](https://onsi.github.io/gomega/) is the matcher library offering:
- Readable assertion syntax: `Expect(value).To(Equal(expected))`
- Extensive built-in matchers
- Detailed failure messages
- Custom matcher support
---
## Running Tests
### Quick Start
**Standard Go Testing**
```bash
# Run all tests
go test ./...
# With verbose output
go test -v ./...
# With coverage
go test -cover ./...
# With race detection (recommended)
CGO_ENABLED=1 go test -race ./...
```
**Ginkgo CLI** (install: `go install github.com/onsi/ginkgo/v2/ginkgo@latest`)
```bash
# Run all tests recursively
ginkgo -r
# Verbose output
ginkgo -v -r
# With coverage
ginkgo -cover -r
# Parallel execution
ginkgo -p -r
# Watch mode (re-run on file changes)
ginkgo watch -r
```
### Coverage Reports
**Generate HTML Coverage Report**
```bash
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
```
**View Coverage by Function**
```bash
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
```
### Advanced Options
**Focus on Specific Tests**
```bash
# Run tests matching pattern
ginkgo --focus="Parse" -r
# Skip tests matching pattern
ginkgo --skip="Encoding" -r
```
**Output Formats**
```bash
# JUnit XML report (CI integration)
ginkgo --junit-report=test-results.xml -r
# JSON output
ginkgo --json-report=test-results.json -r
```
**Specific Packages**
```bash
# Test only main package
cd /path/to/golib/certificates
go test .
# Test specific subpackage
cd auth
go test .
```
---
## Test Coverage
### Coverage Metrics
| Package | Coverage | Specs | Description |
|---------|----------|-------|-------------|
| `certificates` | ~70% | 15 | Main TLS configuration |
| `auth` | 73.0% | 12 | Client authentication modes |
| `ca` | 68.5% | 18 | CA certificate management |
| `certs` | 47.8% | 9 | Certificate pair management |
| `cipher` | 50.6% | 12 | Cipher suite configuration |
| `curves` | 50.5% | 9 | Elliptic curve configuration |
| `tlsversion` | 54.5% | 9 | TLS version management |
| **Total** | **~60%** | **84** | **All packages** |
### Coverage by Component
**Main Package** (`certificates_config_test.go` - 15 specs)
- TLS configuration creation and validation
- Certificate pair management
- Root CA and Client CA operations
- Version, cipher, and curve configuration
- TLS config generation
**auth Package** (`auth_test.go` - 12 specs)
- Parse client authentication modes from strings
- Parse from integer values
- Encoding/decoding (JSON, YAML, TOML, CBOR)
- String representation and validation
- TLS type conversion
**ca Package** (`ca_test.go` - 18 specs)
- Parse CA certificates from PEM strings
- Parse certificate chains
- File path handling
- Certificate pool operations
- Encoding/decoding in multiple formats
- Error handling for invalid certificates
**certs Package** (`certs_test.go` - 9 specs)
- Parse certificate pairs from PEM
- Parse from ConfigPair and ConfigChain
- Certificate validation
- TLS certificate generation
- Encoding/decoding
**cipher Package** (`cipher_test.go` - 12 specs)
- Parse cipher suites from strings
- List available cipher suites
- Validate cipher suite IDs
- String representation
- Encoding/decoding
**curves Package** (`curves_test.go` - 9 specs)
- Parse elliptic curves from strings
- List available curves
- Validate curve IDs
- String representation
- Encoding/decoding
**tlsversion Package** (`tlsversion_test.go` - 9 specs)
- Parse TLS versions from strings
- Parse from integer values
- List available versions
- String representation
- Encoding/decoding
---
## Test Organization
### File Structure
```
certificates/
├── certificates_suite_test.go # Main test suite setup
├── certificates_config_test.go # TLS configuration tests (15 specs)
└── Subpackages/
├── auth/
│ ├── auth_suite_test.go # Auth test suite setup
│ └── auth_test.go # Auth mode tests (12 specs)
├── ca/
│ ├── ca_suite_test.go # CA test suite setup
│ └── ca_test.go # CA certificate tests (18 specs)
├── certs/
│ ├── certs_suite_test.go # Certs test suite setup
│ └── certs_test.go # Certificate pair tests (9 specs)
├── cipher/
│ ├── cipher_suite_test.go # Cipher test suite setup
│ └── cipher_test.go # Cipher suite tests (12 specs)
├── curves/
│ ├── curves_suite_test.go # Curves test suite setup
│ └── curves_test.go # Elliptic curve tests (9 specs)
└── tlsversion/
├── tlsversion_suite_test.go # Version test suite setup
└── tlsversion_test.go # TLS version tests (9 specs)
```
### Test Structure Pattern
```go
// Hierarchical BDD structure
Describe("certificates/Feature", func() {
Context("When parsing input", func() {
It("should parse valid PEM string", func() {
// Arrange
pemData := `-----BEGIN CERTIFICATE-----...`
// Act
cert, err := ca.Parse(pemData)
// Assert
Expect(err).ToNot(HaveOccurred())
Expect(cert).ToNot(BeNil())
Expect(cert.Len()).To(Equal(1))
})
It("should return error for invalid input", func() {
// Arrange
invalid := "not a certificate"
// Act
_, err := ca.Parse(invalid)
// Assert
Expect(err).To(HaveOccurred())
})
})
})
```
---
## Writing Tests
### Test Guidelines
**1. Descriptive Test Names**
```go
// ✅ Good: Clear, specific description
It("should parse ECDHE-RSA-AES128-GCM-SHA256 cipher suite", func() {
// Test implementation
})
// ❌ Bad: Vague description
It("should work", func() {
// Test implementation
})
```
**2. Follow AAA Pattern** (Arrange, Act, Assert)
```go
It("should parse TLS version from string", func() {
// Arrange
input := "1.2"
// Act
version := tlsversion.Parse(input)
// Assert
Expect(version).To(Equal(tlsversion.VersionTLS12))
})
```
**3. Test Multiple Formats**
```go
// Test encoding/decoding for all supported formats
It("should marshal to JSON", func() {
auth := auth.RequireAndVerifyClientCert
data, err := json.Marshal(auth)
Expect(err).ToNot(HaveOccurred())
Expect(string(data)).To(ContainSubstring("require"))
})
It("should unmarshal from JSON", func() {
var auth auth.ClientAuth
err := json.Unmarshal([]byte(`"verify"`), &auth)
Expect(err).ToNot(HaveOccurred())
Expect(auth).To(Equal(auth.VerifyClientCertIfGiven))
})
```
**4. Test Edge Cases**
```go
// Test with empty, nil, invalid, and boundary inputs
It("should handle empty string", func() {
version := tlsversion.Parse("")
Expect(version).To(Equal(tlsversion.VersionUnknown))
})
It("should handle invalid cipher suite ID", func() {
valid := cipher.Check(0xFFFF)
Expect(valid).To(BeFalse())
})
```
**5. Test Security Properties**
```go
It("should default to secure TLS version", func() {
cfg := certificates.New()
min := cfg.GetVersionMin()
Expect(min).To(Or(
Equal(tlsversion.VersionTLS12),
Equal(tlsversion.VersionTLS13),
))
})
```
### Test Template
```go
var _ = Describe("certificates/NewFeature", func() {
Context("When feature is used", func() {
It("should perform expected behavior", func() {
// Arrange
cfg := certificates.New()
// Act
cfg.SetVersionMin(tlsversion.VersionTLS12)
version := cfg.GetVersionMin()
// Assert
Expect(version).To(Equal(tlsversion.VersionTLS12))
})
It("should handle edge case", func() {
// Test edge case
cfg := certificates.New()
certs := cfg.GetCertificatePair()
Expect(certs).To(BeEmpty())
})
})
})
```
---
## Best Practices
### Test Independence
```go
// ✅ Good: Each test creates its own config
It("should add certificate", func() {
cfg := certificates.New()
err := cfg.AddCertificatePairFile("key.pem", "cert.pem")
Expect(err).ToNot(HaveOccurred())
})
// ❌ Bad: Tests share state
var sharedCfg certificates.TLSConfig
It("test1", func() {
sharedCfg.SetVersionMin(tlsversion.VersionTLS12)
})
It("test2", func() {
// Depends on test1!
version := sharedCfg.GetVersionMin()
})
```
### Parser Testing
```go
// ✅ Good: Test various input formats
It("should parse flexible string formats", func() {
inputs := []string{"1.2", "TLS 1.2", "tls_1_2", "tls12"}
for _, input := range inputs {
version := tlsversion.Parse(input)
Expect(version).To(Equal(tlsversion.VersionTLS12))
}
})
```
### Encoding Testing
```go
// ✅ Good: Test round-trip encoding
It("should round-trip through JSON", func() {
original := cipher.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
// Marshal
data, err := json.Marshal(original)
Expect(err).ToNot(HaveOccurred())
// Unmarshal
var decoded cipher.Cipher
err = json.Unmarshal(data, &decoded)
Expect(err).ToNot(HaveOccurred())
// Verify
Expect(decoded).To(Equal(original))
})
```
### Security Testing
```go
// ✅ Good: Verify secure defaults
It("should use secure cipher suites by default", func() {
cfg := certificates.New()
ciphers := cfg.GetCiphers()
for _, c := range ciphers {
// No weak ciphers
Expect(c).ToNot(Equal(cipher.Unknown))
}
})
```
---
## Troubleshooting
### Common Issues
**File Not Found Errors**
*Problem*: Tests fail when loading certificate files.
*Solution*:
```go
// Use test fixtures or embedded test data
const testCertPEM = `-----BEGIN CERTIFICATE-----
MIIBkTCB+wIJAKHHCgVZU...
-----END CERTIFICATE-----`
It("should parse certificate", func() {
cert, err := ca.Parse(testCertPEM)
Expect(err).ToNot(HaveOccurred())
})
```
**Encoding Mismatches**
*Problem*: Encoding tests fail due to format differences.
*Solution*:
```bash
# Verify encoding with actual output
ginkgo -v --focus="Encoding"
```
**Stale Coverage**
*Problem*: Coverage report doesn't reflect recent changes.
*Solution*:
```bash
go clean -testcache
go test -coverprofile=coverage.out ./...
```
### Debugging Techniques
**Run Specific Tests**
```bash
# Focus on specific package
cd auth
go test -v
# Focus on specific test
ginkgo --focus="Parse" -v
```
**Verbose Output**
```bash
# Ginkgo verbose mode
ginkgo -v --trace -r
# Standard Go verbose
go test -v ./...
```
**Check Test Data**
```go
It("should parse certificate", func() {
cert, err := ca.Parse(pemData)
// Debug output
fmt.Fprintf(GinkgoWriter, "Cert: %+v\n", cert)
fmt.Fprintf(GinkgoWriter, "Error: %v\n", err)
Expect(err).ToNot(HaveOccurred())
})
```
---
## Contributing
### Test Contributions
**Guidelines**
- Do not use AI to generate test implementation code
- AI may assist with test documentation and bug fixing
- Follow existing test patterns and structure
- Add tests for new features
- Test edge cases and error conditions
**Adding New Tests**
1. Choose appropriate test file based on feature
2. Use descriptive test names
3. Follow AAA pattern (Arrange, Act, Assert)
4. Test multiple input formats
5. Test edge cases and error conditions
6. Verify security properties
**Test Review Checklist**
- [ ] Tests are independent
- [ ] Resources are properly managed
- [ ] Edge cases are covered
- [ ] Multiple formats tested (JSON, YAML, TOML, CBOR)
- [ ] Descriptions are clear
- [ ] Security properties verified
- [ ] Coverage maintained or improved
---
## AI Transparency Notice
In accordance with Article 50.4 of the EU AI Act, AI assistance has been used for testing, documentation, and bug fixing under human supervision.
---
## Resources
**Documentation**
- [Ginkgo v2 Documentation](https://onsi.github.io/ginkgo/)
- [Gomega Matcher Reference](https://onsi.github.io/gomega/)
- [Go Testing Package](https://pkg.go.dev/testing)
- [Go crypto/tls](https://pkg.go.dev/crypto/tls)
**Certificates Package**
- [Package GoDoc](https://pkg.go.dev/github.com/nabbar/golib/certificates)
- [README.md](README.md) - Package overview and examples
- [GitHub Repository](https://github.com/nabbar/golib)
**Testing Tools**
- [Go Test Command](https://pkg.go.dev/cmd/go#hdr-Test_packages)
- [Coverage Tool](https://go.dev/blog/cover)
---
## License
MIT License - See [LICENSE](../../LICENSE) file for details.