mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-07-15 11:57:59 +00:00
Add comprehensive CI/CD improvements and test coverage
This commit introduces major enhancements to the CI/CD pipeline and testing infrastructure: CI/CD Improvements: - Consolidated modern and legacy CI workflows into a single comprehensive pipeline - Removed all publishing functionality from CI (no longer needed) - Added 8 new advanced testing jobs for pull requests: * advanced-coverage: Detailed coverage analysis with base branch comparison * performance-profile: CPU and memory profiling with benchmarks * fuzz-test: Automated fuzz testing for supported packages * deep-analysis: Multiple static analysis tools (shadow, ineffassign, gosec, staticcheck) * mutation-test: Tests effectiveness of test suite on changed files * dependency-audit: Security vulnerabilities and outdated dependency checks * stress-test: Race detection with 100 iterations and parallel testing * test-report-summary: Aggregates all reports into a single PR comment - Enabled RUN_LONG_TESTS by default for thorough testing - Added automatic PR comment generation with all test results Testing Infrastructure: - Added comprehensive test files across all packages to improve coverage - Implemented unit tests for previously untested packages - Added race condition tests for concurrent operations - Created integration tests for API endpoints - Added storage backend tests (etcd, goleveldb) - Implemented command-line interface tests Local Testing Support: - Added act configuration for testing GitHub Actions locally - Created docker-compose.ci.yml for full CI environment simulation - Updated CONTRIBUTING.md with detailed local testing instructions Documentation Updates: - Added comprehensive CI documentation to CONTRIBUTING.md - Removed obsolete references to Travis CI - Updated Go version requirements to 1.24 - Added act usage instructions and examples Other Improvements: - Updated .gitignore to exclude coverage reports and build artifacts - Added test-act.yml workflow for testing act functionality - Created CI_SUMMARY.md documenting all CI capabilities These changes transform aptly's CI from a basic testing pipeline into a comprehensive quality assurance system that provides immediate feedback on code quality, performance, security, and test effectiveness.
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type LoggingSuite struct {
|
||||
origLogger zerolog.Logger
|
||||
}
|
||||
|
||||
var _ = Suite(&LoggingSuite{})
|
||||
|
||||
func (s *LoggingSuite) SetUpTest(c *C) {
|
||||
// Save original logger
|
||||
s.origLogger = log.Logger
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TearDownTest(c *C) {
|
||||
// Restore original logger
|
||||
log.Logger = s.origLogger
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestRunningOnTerminal(c *C) {
|
||||
// Test RunningOnTerminal function
|
||||
// The result depends on whether we're running in a terminal
|
||||
result := RunningOnTerminal()
|
||||
c.Check(result, FitsTypeOf, true)
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestLogWriter(c *C) {
|
||||
// Test LogWriter struct and Write method
|
||||
var buf bytes.Buffer
|
||||
logger := zerolog.New(&buf)
|
||||
logWriter := LogWriter{Logger: logger}
|
||||
|
||||
// Test Write method
|
||||
testData := []byte("test log message")
|
||||
n, err := logWriter.Write(testData)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(n, Equals, len(testData))
|
||||
|
||||
// Check that something was written to the buffer
|
||||
c.Check(buf.Len() > 0, Equals, true)
|
||||
|
||||
// Check that the output contains the message
|
||||
output := buf.String()
|
||||
c.Check(strings.Contains(output, "test log message"), Equals, true)
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestLogWriterEmpty(c *C) {
|
||||
// Test LogWriter with empty data
|
||||
var buf bytes.Buffer
|
||||
logger := zerolog.New(&buf)
|
||||
logWriter := LogWriter{Logger: logger}
|
||||
|
||||
n, err := logWriter.Write([]byte{})
|
||||
c.Check(err, IsNil)
|
||||
c.Check(n, Equals, 0)
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestSetupJSONLogger(c *C) {
|
||||
// Test SetupJSONLogger function
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Test with different log levels
|
||||
testLevels := []string{"debug", "info", "warn", "error"}
|
||||
|
||||
for _, level := range testLevels {
|
||||
buf.Reset()
|
||||
SetupJSONLogger(level, &buf)
|
||||
|
||||
// Check that message and level field names are set correctly
|
||||
c.Check(zerolog.MessageFieldName, Equals, "message")
|
||||
c.Check(zerolog.LevelFieldName, Equals, "level")
|
||||
|
||||
// Test logging something
|
||||
log.Info().Msg("test message")
|
||||
|
||||
// Check that JSON was written
|
||||
output := buf.String()
|
||||
if len(output) > 0 {
|
||||
c.Check(strings.Contains(output, "message"), Equals, true)
|
||||
c.Check(strings.Contains(output, "test message"), Equals, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestSetupDefaultLogger(c *C) {
|
||||
// Test SetupDefaultLogger function
|
||||
testLevels := []string{"debug", "info", "warn", "error"}
|
||||
|
||||
for _, level := range testLevels {
|
||||
SetupDefaultLogger(level)
|
||||
|
||||
// Check that message and level field names are set correctly
|
||||
c.Check(zerolog.MessageFieldName, Equals, "message")
|
||||
c.Check(zerolog.LevelFieldName, Equals, "level")
|
||||
|
||||
// Check that logger is configured (hard to test output since it goes to stderr)
|
||||
c.Check(log.Logger, NotNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestGetLogLevelOrDebugValid(c *C) {
|
||||
// Test GetLogLevelOrDebug with valid levels
|
||||
testCases := map[string]zerolog.Level{
|
||||
"debug": zerolog.DebugLevel,
|
||||
"DEBUG": zerolog.DebugLevel,
|
||||
"info": zerolog.InfoLevel,
|
||||
"INFO": zerolog.InfoLevel,
|
||||
"warn": zerolog.WarnLevel,
|
||||
"WARN": zerolog.WarnLevel,
|
||||
"warning": zerolog.WarnLevel,
|
||||
"WARNING": zerolog.WarnLevel,
|
||||
"error": zerolog.ErrorLevel,
|
||||
"ERROR": zerolog.ErrorLevel,
|
||||
"fatal": zerolog.FatalLevel,
|
||||
"FATAL": zerolog.FatalLevel,
|
||||
"panic": zerolog.PanicLevel,
|
||||
"PANIC": zerolog.PanicLevel,
|
||||
"trace": zerolog.TraceLevel,
|
||||
"TRACE": zerolog.TraceLevel,
|
||||
}
|
||||
|
||||
for levelStr, expectedLevel := range testCases {
|
||||
result := GetLogLevelOrDebug(levelStr)
|
||||
c.Check(result, Equals, expectedLevel, Commentf("Failed for level: %s", levelStr))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestGetLogLevelOrDebugInvalid(c *C) {
|
||||
// Test GetLogLevelOrDebug with invalid levels
|
||||
invalidLevels := []string{
|
||||
"invalid",
|
||||
"unknown",
|
||||
"",
|
||||
"verbose",
|
||||
"critical",
|
||||
}
|
||||
|
||||
// Capture log output to verify warning is logged
|
||||
var buf bytes.Buffer
|
||||
originalLogger := log.Logger
|
||||
log.Logger = zerolog.New(&buf).Level(zerolog.TraceLevel)
|
||||
defer func() { log.Logger = originalLogger }()
|
||||
|
||||
for _, levelStr := range invalidLevels {
|
||||
buf.Reset()
|
||||
result := GetLogLevelOrDebug(levelStr)
|
||||
|
||||
// Should default to debug level
|
||||
c.Check(result, Equals, zerolog.DebugLevel, Commentf("Failed for invalid level: %s", levelStr))
|
||||
|
||||
// Should log a warning (if levelStr is not empty)
|
||||
if levelStr != "" {
|
||||
output := buf.String()
|
||||
c.Check(strings.Contains(output, "Unknown log level"), Equals, true, Commentf("No warning logged for: %s, got: %s", levelStr, output))
|
||||
c.Check(strings.Contains(output, levelStr), Equals, true, Commentf("Level not mentioned in warning: %s", levelStr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestTimestampHook(c *C) {
|
||||
// Test timestampHook struct and Run method
|
||||
hook := ×tampHook{}
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := zerolog.New(&buf).Hook(hook)
|
||||
|
||||
// Log a message
|
||||
logger.Info().Msg("test message with timestamp")
|
||||
|
||||
// Check that output contains timestamp
|
||||
output := buf.String()
|
||||
c.Check(strings.Contains(output, "time"), Equals, true)
|
||||
c.Check(strings.Contains(output, "test message with timestamp"), Equals, true)
|
||||
|
||||
// Check that timestamp is in RFC3339 format (contains T and Z or +/- timezone)
|
||||
c.Check(strings.Contains(output, "T") || strings.Contains(output, ":"), Equals, true)
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestTimestampHookMultipleLevels(c *C) {
|
||||
// Test timestampHook with different log levels
|
||||
hook := ×tampHook{}
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := zerolog.New(&buf).Hook(hook)
|
||||
|
||||
// Test different log levels
|
||||
testCases := []struct {
|
||||
level zerolog.Level
|
||||
message string
|
||||
}{
|
||||
{zerolog.DebugLevel, "debug message"},
|
||||
{zerolog.InfoLevel, "info message"},
|
||||
{zerolog.WarnLevel, "warn message"},
|
||||
{zerolog.ErrorLevel, "error message"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
buf.Reset()
|
||||
logger.WithLevel(tc.level).Msg(tc.message)
|
||||
|
||||
output := buf.String()
|
||||
if len(output) > 0 {
|
||||
c.Check(strings.Contains(output, "time"), Equals, true, Commentf("No timestamp for level: %v", tc.level))
|
||||
c.Check(strings.Contains(output, tc.message), Equals, true, Commentf("Message missing for level: %v", tc.level))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestLogLevelCaseInsensitive(c *C) {
|
||||
// Test that log level parsing is case insensitive
|
||||
mixedCaseLevels := []string{
|
||||
"Debug", "deBuG", "DeBuG",
|
||||
"Info", "inFo", "InFo",
|
||||
"Warn", "waRn", "WaRn",
|
||||
"Error", "erRor", "ErRoR",
|
||||
}
|
||||
|
||||
for _, levelStr := range mixedCaseLevels {
|
||||
result := GetLogLevelOrDebug(levelStr)
|
||||
// Should not default to debug (unless it's actually debug)
|
||||
if strings.ToLower(levelStr) != "debug" {
|
||||
c.Check(result != zerolog.DebugLevel || strings.ToLower(levelStr) == "debug", Equals, true,
|
||||
Commentf("Case insensitive parsing failed for: %s", levelStr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoggingSuite) TestSetupLoggersIntegration(c *C) {
|
||||
// Test integration between setup functions and actual logging
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Test JSON logger
|
||||
SetupJSONLogger("info", &buf)
|
||||
log.Info().Str("key", "value").Msg("json test message")
|
||||
|
||||
jsonOutput := buf.String()
|
||||
if len(jsonOutput) > 0 {
|
||||
c.Check(strings.Contains(jsonOutput, "json test message"), Equals, true)
|
||||
c.Check(strings.Contains(jsonOutput, "key"), Equals, true)
|
||||
c.Check(strings.Contains(jsonOutput, "value"), Equals, true)
|
||||
}
|
||||
|
||||
// Test default logger (output goes to stderr, so we can't easily capture it)
|
||||
SetupDefaultLogger("warn")
|
||||
c.Check(log.Logger, NotNil)
|
||||
}
|
||||
Reference in New Issue
Block a user