mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-24 08:30:59 +00:00
40ba104838
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.
362 lines
11 KiB
Go
362 lines
11 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
. "gopkg.in/check.v1"
|
|
)
|
|
|
|
type SnapshotAPITestSuite struct {
|
|
router *gin.Engine
|
|
}
|
|
|
|
var _ = Suite(&SnapshotAPITestSuite{})
|
|
|
|
func (s *SnapshotAPITestSuite) SetUpTest(c *C) {
|
|
s.router = gin.New()
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
// Set up API routes
|
|
s.router.GET("/api/snapshots", apiSnapshotsList)
|
|
s.router.POST("/api/snapshots", apiSnapshotsCreate)
|
|
s.router.POST("/api/mirrors/:name/snapshots", apiSnapshotsCreateFromMirror)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsListGet(c *C) {
|
|
// Test GET /api/snapshots endpoint
|
|
req, _ := http.NewRequest("GET", "/api/snapshots", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should handle the request without crashing (will likely error due to no context)
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsListWithSort(c *C) {
|
|
// Test GET /api/snapshots with sort parameter
|
|
req, _ := http.NewRequest("GET", "/api/snapshots?sort=name", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsListWithDifferentSorts(c *C) {
|
|
// Test various sort methods
|
|
sortMethods := []string{"name", "time", "created"}
|
|
|
|
for _, sortMethod := range sortMethods {
|
|
req, _ := http.NewRequest("GET", "/api/snapshots?sort="+sortMethod, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Not(Equals), 0, Commentf("Sort method: %s", sortMethod))
|
|
}
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreatePost(c *C) {
|
|
// Test POST /api/snapshots endpoint
|
|
requestBody := snapshotsCreateParams{
|
|
Name: "test-snapshot",
|
|
Description: "Test snapshot",
|
|
SourceSnapshots: []string{"source1"},
|
|
PackageRefs: []string{},
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/snapshots", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should handle the request without crashing (will likely error due to no context)
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateInvalidJSON(c *C) {
|
|
// Test POST with invalid JSON
|
|
req, _ := http.NewRequest("POST", "/api/snapshots", strings.NewReader("invalid json"))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 400) // Should return bad request for invalid JSON
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateMissingName(c *C) {
|
|
// Test POST with missing required name field
|
|
requestBody := map[string]interface{}{
|
|
"Description": "Test without name",
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/snapshots", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 400) // Should return bad request for missing name
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateFromMirrorPost(c *C) {
|
|
// Test POST /api/mirrors/{name}/snapshots endpoint
|
|
requestBody := snapshotsCreateFromMirrorParams{
|
|
Name: "mirror-snapshot",
|
|
Description: "Snapshot from mirror",
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/mirrors/test-mirror/snapshots", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should handle the request without crashing (will likely error due to no context)
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateFromMirrorInvalidJSON(c *C) {
|
|
// Test POST with invalid JSON for mirror snapshot
|
|
req, _ := http.NewRequest("POST", "/api/mirrors/test-mirror/snapshots", strings.NewReader("invalid json"))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 400) // Should return bad request for invalid JSON
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateFromMirrorMissingName(c *C) {
|
|
// Test POST with missing required name field for mirror snapshot
|
|
requestBody := map[string]interface{}{
|
|
"Description": "Mirror snapshot without name",
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/mirrors/test-mirror/snapshots", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 400) // Should return bad request for missing name
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateWithAsync(c *C) {
|
|
// Test POST with async parameter
|
|
requestBody := snapshotsCreateParams{
|
|
Name: "async-snapshot",
|
|
Description: "Async test snapshot",
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/snapshots?_async=true", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateFromMirrorWithAsync(c *C) {
|
|
// Test POST mirror snapshot with async parameter
|
|
requestBody := snapshotsCreateFromMirrorParams{
|
|
Name: "async-mirror-snapshot",
|
|
Description: "Async mirror snapshot",
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/mirrors/test-mirror/snapshots?_async=true", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestSnapshotsCreateParamsStruct(c *C) {
|
|
// Test snapshotsCreateParams struct
|
|
params := snapshotsCreateParams{
|
|
Name: "test-name",
|
|
Description: "test-description",
|
|
SourceSnapshots: []string{"snap1", "snap2"},
|
|
PackageRefs: []string{"ref1", "ref2"},
|
|
}
|
|
|
|
c.Check(params.Name, Equals, "test-name")
|
|
c.Check(params.Description, Equals, "test-description")
|
|
c.Check(params.SourceSnapshots, DeepEquals, []string{"snap1", "snap2"})
|
|
c.Check(params.PackageRefs, DeepEquals, []string{"ref1", "ref2"})
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestSnapshotsCreateFromMirrorParamsStruct(c *C) {
|
|
// Test snapshotsCreateFromMirrorParams struct
|
|
params := snapshotsCreateFromMirrorParams{
|
|
Name: "mirror-test-name",
|
|
Description: "mirror-test-description",
|
|
}
|
|
|
|
c.Check(params.Name, Equals, "mirror-test-name")
|
|
c.Check(params.Description, Equals, "mirror-test-description")
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateEmptyRequest(c *C) {
|
|
// Test POST with empty request body
|
|
req, _ := http.NewRequest("POST", "/api/snapshots", strings.NewReader(""))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 400) // Should return bad request for empty body
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateFromMirrorEmptyRequest(c *C) {
|
|
// Test POST mirror snapshot with empty request body
|
|
req, _ := http.NewRequest("POST", "/api/mirrors/test-mirror/snapshots", strings.NewReader(""))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 400) // Should return bad request for empty body
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsListDefaultSort(c *C) {
|
|
// Test that default sort is applied when no sort parameter provided
|
|
req, _ := http.NewRequest("GET", "/api/snapshots", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Endpoint should handle default sort without issues
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateComplexPayload(c *C) {
|
|
// Test POST with complex payload including all fields
|
|
requestBody := snapshotsCreateParams{
|
|
Name: "complex-snapshot",
|
|
Description: "Complex test snapshot with multiple sources",
|
|
SourceSnapshots: []string{"base-snapshot", "updates-snapshot", "security-snapshot"},
|
|
PackageRefs: []string{"pkg1_1.0_amd64", "pkg2_2.0_i386", "pkg3_3.0_all"},
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/snapshots", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsHTTPMethods(c *C) {
|
|
// Test that only allowed HTTP methods work
|
|
|
|
// Test unsupported methods for snapshots list
|
|
deniedMethods := []string{"PUT", "DELETE", "PATCH"}
|
|
for _, method := range deniedMethods {
|
|
req, _ := http.NewRequest(method, "/api/snapshots", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 404, Commentf("Method %s should not be allowed for snapshots list", method))
|
|
}
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateSpecialCharacters(c *C) {
|
|
// Test snapshot creation with special characters in names
|
|
specialNames := []string{
|
|
"snapshot-with-dashes",
|
|
"snapshot_with_underscores",
|
|
"snapshot.with.dots",
|
|
"snapshot123",
|
|
"UPPERCASESNAPSHOT",
|
|
}
|
|
|
|
for _, name := range specialNames {
|
|
requestBody := snapshotsCreateParams{
|
|
Name: name,
|
|
Description: "Test snapshot with special characters",
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/snapshots", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Not(Equals), 0, Commentf("Special name test failed: %s", name))
|
|
}
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsListEmptyResponse(c *C) {
|
|
// Test snapshots list when no snapshots exist
|
|
req, _ := http.NewRequest("GET", "/api/snapshots", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should return some response (likely error due to no context, but shouldn't crash)
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateWithoutContentType(c *C) {
|
|
// Test POST without Content-Type header
|
|
requestBody := `{"Name": "test-snapshot"}`
|
|
req, _ := http.NewRequest("POST", "/api/snapshots", strings.NewReader(requestBody))
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should handle missing content type
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
}
|
|
|
|
func (s *SnapshotAPITestSuite) TestApiSnapshotsParameterEdgeCases(c *C) {
|
|
// Test edge cases for parameter validation
|
|
|
|
// Test with very long name
|
|
longName := strings.Repeat("a", 1000)
|
|
requestBody := snapshotsCreateParams{
|
|
Name: longName,
|
|
}
|
|
|
|
jsonBody, _ := json.Marshal(requestBody)
|
|
req, _ := http.NewRequest("POST", "/api/snapshots", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
|
|
// Test with empty arrays
|
|
emptyArrayBody := snapshotsCreateParams{
|
|
Name: "empty-arrays",
|
|
SourceSnapshots: []string{},
|
|
PackageRefs: []string{},
|
|
}
|
|
|
|
jsonBody, _ = json.Marshal(emptyArrayBody)
|
|
req, _ = http.NewRequest("POST", "/api/snapshots", bytes.NewBuffer(jsonBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w = httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Not(Equals), 0)
|
|
} |