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

14 KiB

Testing Guide - Certificates Package

License: MIT Go Version

Comprehensive testing documentation for the certificates package and subpackages.


Table of Contents


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 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 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

# 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)

# 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

go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html

View Coverage by Function

go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out

Advanced Options

Focus on Specific Tests

# Run tests matching pattern
ginkgo --focus="Parse" -r

# Skip tests matching pattern
ginkgo --skip="Encoding" -r

Output Formats

# JUnit XML report (CI integration)
ginkgo --junit-report=test-results.xml -r

# JSON output
ginkgo --json-report=test-results.json -r

Specific Packages

# 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

// 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

// ✅ 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)

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

// 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

// 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

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

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

// ✅ 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

// ✅ 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

// ✅ 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

// ✅ 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:

// 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:

# Verify encoding with actual output
ginkgo -v --focus="Encoding"

Stale Coverage

Problem: Coverage report doesn't reflect recent changes.

Solution:

go clean -testcache
go test -coverprofile=coverage.out ./...

Debugging Techniques

Run Specific Tests

# Focus on specific package
cd auth
go test -v

# Focus on specific test
ginkgo --focus="Parse" -v

Verbose Output

# Ginkgo verbose mode
ginkgo -v --trace -r

# Standard Go verbose
go test -v ./...

Check Test Data

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

Certificates Package

Testing Tools


License

MIT License - See LICENSE file for details.