mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-07-15 11:57:59 +00:00
Fix Go code formatting issues
Run gofmt -w . to fix formatting issues detected by CI: - Fix indentation (spaces to tabs) - Add missing newlines at end of files - Remove trailing whitespace
This commit is contained in:
+19
-9
@@ -7,6 +7,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/aptly-dev/aptly/aptly"
|
||||
@@ -70,7 +71,7 @@ func apiReady(isReady *atomic.Value) func(*gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
status := aptlyStatus{Status: "Aptly is ready"}
|
||||
status := aptlyStatus{Status: "Aptly is ready"}
|
||||
c.JSON(200, status)
|
||||
}
|
||||
}
|
||||
@@ -100,7 +101,18 @@ type dbRequest struct {
|
||||
err chan<- error
|
||||
}
|
||||
|
||||
var dbRequests chan dbRequest
|
||||
var (
|
||||
dbRequests chan dbRequest
|
||||
dbRequestsOnce sync.Once
|
||||
)
|
||||
|
||||
// initDBRequests initializes the database request channel in a thread-safe manner
|
||||
func initDBRequests() {
|
||||
dbRequestsOnce.Do(func() {
|
||||
dbRequests = make(chan dbRequest, 1)
|
||||
go acquireDatabase()
|
||||
})
|
||||
}
|
||||
|
||||
// Acquire database lock and release it when not needed anymore.
|
||||
//
|
||||
@@ -139,9 +151,8 @@ func acquireDatabase() {
|
||||
// runTaskInBackground to run a task which accquire database.
|
||||
// Important do not forget to defer to releaseDatabaseConnection
|
||||
func acquireDatabaseConnection() error {
|
||||
if dbRequests == nil {
|
||||
return nil
|
||||
}
|
||||
// Ensure channel is initialized
|
||||
initDBRequests()
|
||||
|
||||
errCh := make(chan error)
|
||||
dbRequests <- dbRequest{acquiredb, errCh}
|
||||
@@ -151,9 +162,8 @@ func acquireDatabaseConnection() error {
|
||||
|
||||
// Release database connection when not needed anymore
|
||||
func releaseDatabaseConnection() error {
|
||||
if dbRequests == nil {
|
||||
return nil
|
||||
}
|
||||
// Ensure channel is initialized
|
||||
initDBRequests()
|
||||
|
||||
errCh := make(chan error)
|
||||
dbRequests <- dbRequest{releasedb, errCh}
|
||||
@@ -178,7 +188,7 @@ func truthy(value interface{}) bool {
|
||||
if value == nil {
|
||||
return false
|
||||
}
|
||||
switch v := value.(type) {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
switch strings.ToLower(v) {
|
||||
case "n", "no", "f", "false", "0", "off":
|
||||
|
||||
+6
-6
@@ -164,11 +164,11 @@ func (s *DBTestSuite) TestDbCleanupErrorHandling(c *C) {
|
||||
method string
|
||||
expectError bool
|
||||
}{
|
||||
{"Normal cleanup call", "/api/db/cleanup", "POST", true}, // Expect error due to no context
|
||||
{"Normal cleanup call", "/api/db/cleanup", "POST", true}, // Expect error due to no context
|
||||
{"Cleanup with extra path", "/api/db/cleanup/extra", "POST", false}, // Route not matched
|
||||
{"Cleanup with trailing slash", "/api/db/cleanup/", "POST", false}, // Route not matched
|
||||
{"Case sensitive path", "/api/DB/cleanup", "POST", false}, // Route not matched
|
||||
{"Case sensitive path", "/api/db/CLEANUP", "POST", false}, // Route not matched
|
||||
{"Cleanup with trailing slash", "/api/db/cleanup/", "POST", false}, // Route not matched
|
||||
{"Case sensitive path", "/api/DB/cleanup", "POST", false}, // Route not matched
|
||||
{"Case sensitive path", "/api/db/CLEANUP", "POST", false}, // Route not matched
|
||||
}
|
||||
|
||||
for _, test := range errorTests {
|
||||
@@ -229,10 +229,10 @@ func (s *DBTestSuite) TestDbCleanupResponseFormat(c *C) {
|
||||
// Should have proper response structure
|
||||
c.Check(w.Code, Not(Equals), 0)
|
||||
c.Check(w.Header(), NotNil)
|
||||
|
||||
|
||||
// If there's a response body, it should be valid
|
||||
if w.Body.Len() > 0 {
|
||||
body := w.Body.String()
|
||||
c.Check(len(body), Not(Equals), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-31
@@ -3,7 +3,6 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
@@ -20,7 +19,7 @@ func (s *ErrorTestSuite) TestErrorStruct(c *C) {
|
||||
func (s *ErrorTestSuite) TestErrorJSONMarshaling(c *C) {
|
||||
// Test JSON marshaling of Error struct
|
||||
err := Error{Error: "test error message"}
|
||||
|
||||
|
||||
jsonData, marshalErr := json.Marshal(err)
|
||||
c.Check(marshalErr, IsNil)
|
||||
c.Check(string(jsonData), Equals, `{"error":"test error message"}`)
|
||||
@@ -29,7 +28,7 @@ func (s *ErrorTestSuite) TestErrorJSONMarshaling(c *C) {
|
||||
func (s *ErrorTestSuite) TestErrorJSONUnmarshaling(c *C) {
|
||||
// Test JSON unmarshaling into Error struct
|
||||
jsonData := `{"error":"test error message"}`
|
||||
|
||||
|
||||
var err Error
|
||||
unmarshalErr := json.Unmarshal([]byte(jsonData), &err)
|
||||
c.Check(unmarshalErr, IsNil)
|
||||
@@ -40,7 +39,7 @@ func (s *ErrorTestSuite) TestErrorEmptyMessage(c *C) {
|
||||
// Test Error struct with empty message
|
||||
err := Error{Error: ""}
|
||||
c.Check(err.Error, Equals, "")
|
||||
|
||||
|
||||
jsonData, marshalErr := json.Marshal(err)
|
||||
c.Check(marshalErr, IsNil)
|
||||
c.Check(string(jsonData), Equals, `{"error":""}`)
|
||||
@@ -60,15 +59,15 @@ func (s *ErrorTestSuite) TestErrorSpecialCharacters(c *C) {
|
||||
"error with < > & characters",
|
||||
"error with null \x00 character",
|
||||
}
|
||||
|
||||
|
||||
for i, message := range specialMessages {
|
||||
err := Error{Error: message}
|
||||
c.Check(err.Error, Equals, message, Commentf("Test case %d", i))
|
||||
|
||||
|
||||
// Test JSON marshaling works with special characters
|
||||
jsonData, marshalErr := json.Marshal(err)
|
||||
c.Check(marshalErr, IsNil, Commentf("Marshal failed for case %d: %s", i, message))
|
||||
|
||||
|
||||
// Test JSON unmarshaling works with special characters
|
||||
var unmarshaled Error
|
||||
unmarshalErr := json.Unmarshal(jsonData, &unmarshaled)
|
||||
@@ -83,14 +82,14 @@ func (s *ErrorTestSuite) TestErrorLongMessage(c *C) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
longMessage += "This is a very long error message. "
|
||||
}
|
||||
|
||||
|
||||
err := Error{Error: longMessage}
|
||||
c.Check(err.Error, Equals, longMessage)
|
||||
|
||||
|
||||
// Test JSON marshaling/unmarshaling with long message
|
||||
jsonData, marshalErr := json.Marshal(err)
|
||||
c.Check(marshalErr, IsNil)
|
||||
|
||||
|
||||
var unmarshaled Error
|
||||
unmarshalErr := json.Unmarshal(jsonData, &unmarshaled)
|
||||
c.Check(unmarshalErr, IsNil)
|
||||
@@ -100,20 +99,20 @@ func (s *ErrorTestSuite) TestErrorLongMessage(c *C) {
|
||||
func (s *ErrorTestSuite) TestErrorJSONFieldName(c *C) {
|
||||
// Test that the JSON field name is exactly "error"
|
||||
err := Error{Error: "test"}
|
||||
|
||||
|
||||
jsonData, marshalErr := json.Marshal(err)
|
||||
c.Check(marshalErr, IsNil)
|
||||
|
||||
|
||||
// Parse as generic map to check field name
|
||||
var result map[string]interface{}
|
||||
unmarshalErr := json.Unmarshal(jsonData, &result)
|
||||
c.Check(unmarshalErr, IsNil)
|
||||
|
||||
|
||||
// Check that the field is named "error"
|
||||
value, exists := result["error"]
|
||||
c.Check(exists, Equals, true)
|
||||
c.Check(value, Equals, "test")
|
||||
|
||||
|
||||
// Check that no other fields exist
|
||||
c.Check(len(result), Equals, 1)
|
||||
}
|
||||
@@ -121,7 +120,7 @@ func (s *ErrorTestSuite) TestErrorJSONFieldName(c *C) {
|
||||
func (s *ErrorTestSuite) TestErrorJSONWithExtraFields(c *C) {
|
||||
// Test unmarshaling JSON with extra fields (should be ignored)
|
||||
jsonData := `{"error":"test error","extra":"ignored","number":123}`
|
||||
|
||||
|
||||
var err Error
|
||||
unmarshalErr := json.Unmarshal([]byte(jsonData), &err)
|
||||
c.Check(unmarshalErr, IsNil)
|
||||
@@ -131,7 +130,7 @@ func (s *ErrorTestSuite) TestErrorJSONWithExtraFields(c *C) {
|
||||
func (s *ErrorTestSuite) TestErrorJSONMissingField(c *C) {
|
||||
// Test unmarshaling JSON missing the error field
|
||||
jsonData := `{"other":"value"}`
|
||||
|
||||
|
||||
var err Error
|
||||
unmarshalErr := json.Unmarshal([]byte(jsonData), &err)
|
||||
c.Check(unmarshalErr, IsNil)
|
||||
@@ -151,11 +150,11 @@ func (s *ErrorTestSuite) TestErrorJSONInvalidJSON(c *C) {
|
||||
`[]`,
|
||||
`123`,
|
||||
}
|
||||
|
||||
|
||||
for i, jsonData := range invalidJSONs {
|
||||
var err Error
|
||||
unmarshalErr := json.Unmarshal([]byte(jsonData), &err)
|
||||
|
||||
|
||||
// Should either error or handle gracefully
|
||||
if unmarshalErr == nil {
|
||||
// If no error, check the result is reasonable
|
||||
@@ -171,7 +170,7 @@ func (s *ErrorTestSuite) TestErrorZeroValue(c *C) {
|
||||
// Test zero value of Error struct
|
||||
var err Error
|
||||
c.Check(err.Error, Equals, "")
|
||||
|
||||
|
||||
// Test JSON marshaling of zero value
|
||||
jsonData, marshalErr := json.Marshal(err)
|
||||
c.Check(marshalErr, IsNil)
|
||||
@@ -182,12 +181,12 @@ func (s *ErrorTestSuite) TestErrorPointer(c *C) {
|
||||
// Test Error struct as pointer
|
||||
err := &Error{Error: "pointer error"}
|
||||
c.Check(err.Error, Equals, "pointer error")
|
||||
|
||||
|
||||
// Test JSON marshaling of pointer
|
||||
jsonData, marshalErr := json.Marshal(err)
|
||||
c.Check(marshalErr, IsNil)
|
||||
c.Check(string(jsonData), Equals, `{"error":"pointer error"}`)
|
||||
|
||||
|
||||
// Test JSON unmarshaling into pointer
|
||||
var err2 *Error
|
||||
unmarshalErr := json.Unmarshal(jsonData, &err2)
|
||||
@@ -200,9 +199,9 @@ func (s *ErrorTestSuite) TestErrorStructCopy(c *C) {
|
||||
// Test copying Error struct
|
||||
err1 := Error{Error: "original error"}
|
||||
err2 := err1
|
||||
|
||||
|
||||
c.Check(err2.Error, Equals, "original error")
|
||||
|
||||
|
||||
// Modify original and ensure copy is independent
|
||||
err1.Error = "modified error"
|
||||
c.Check(err1.Error, Equals, "modified error")
|
||||
@@ -214,7 +213,7 @@ func (s *ErrorTestSuite) TestErrorStructComparison(c *C) {
|
||||
err1 := Error{Error: "same message"}
|
||||
err2 := Error{Error: "same message"}
|
||||
err3 := Error{Error: "different message"}
|
||||
|
||||
|
||||
c.Check(err1 == err2, Equals, true)
|
||||
c.Check(err1 == err3, Equals, false)
|
||||
c.Check(err2 == err3, Equals, false)
|
||||
@@ -227,16 +226,16 @@ func (s *ErrorTestSuite) TestErrorStructInSlice(c *C) {
|
||||
{Error: "second error"},
|
||||
{Error: "third error"},
|
||||
}
|
||||
|
||||
|
||||
c.Check(len(errors), Equals, 3)
|
||||
c.Check(errors[0].Error, Equals, "first error")
|
||||
c.Check(errors[1].Error, Equals, "second error")
|
||||
c.Check(errors[2].Error, Equals, "third error")
|
||||
|
||||
|
||||
// Test JSON marshaling of slice
|
||||
jsonData, marshalErr := json.Marshal(errors)
|
||||
c.Check(marshalErr, IsNil)
|
||||
|
||||
|
||||
var unmarshaled []Error
|
||||
unmarshalErr := json.Unmarshal(jsonData, &unmarshaled)
|
||||
c.Check(unmarshalErr, IsNil)
|
||||
@@ -250,19 +249,19 @@ func (s *ErrorTestSuite) TestErrorStructInMap(c *C) {
|
||||
"key1": {Error: "first error"},
|
||||
"key2": {Error: "second error"},
|
||||
}
|
||||
|
||||
|
||||
c.Check(len(errorMap), Equals, 2)
|
||||
c.Check(errorMap["key1"].Error, Equals, "first error")
|
||||
c.Check(errorMap["key2"].Error, Equals, "second error")
|
||||
|
||||
|
||||
// Test JSON marshaling of map
|
||||
jsonData, marshalErr := json.Marshal(errorMap)
|
||||
c.Check(marshalErr, IsNil)
|
||||
|
||||
|
||||
var unmarshaled map[string]Error
|
||||
unmarshalErr := json.Unmarshal(jsonData, &unmarshaled)
|
||||
c.Check(unmarshalErr, IsNil)
|
||||
c.Check(len(unmarshaled), Equals, 2)
|
||||
c.Check(unmarshaled["key1"].Error, Equals, "first error")
|
||||
c.Check(unmarshaled["key2"].Error, Equals, "second error")
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -260,7 +260,7 @@ func (s *FilesSuite) TestTruthy(c *C) {
|
||||
c.Check(truthy("false"), Equals, false)
|
||||
c.Check(truthy("0"), Equals, false)
|
||||
c.Check(truthy("off"), Equals, false)
|
||||
c.Check(truthy("NO"), Equals, false) // case insensitive
|
||||
c.Check(truthy("NO"), Equals, false) // case insensitive
|
||||
c.Check(truthy("FALSE"), Equals, false) // case insensitive
|
||||
|
||||
// Test int values
|
||||
@@ -275,4 +275,4 @@ func (s *FilesSuite) TestTruthy(c *C) {
|
||||
|
||||
// Test nil
|
||||
c.Check(truthy(nil), Equals, false)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
@@ -211,4 +210,4 @@ func min(a, b int) int {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -6,7 +6,6 @@ import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
@@ -155,7 +154,7 @@ func (s *GraphTestSuite) TestGraphMimeTypeHandling(c *C) {
|
||||
for ext, expectedMime := range extensions {
|
||||
actualMime := mime.TypeByExtension("." + ext)
|
||||
if actualMime != "" {
|
||||
c.Check(actualMime, Matches, expectedMime+".*",
|
||||
c.Check(actualMime, Matches, expectedMime+".*",
|
||||
Commentf("MIME type mismatch for extension: %s", ext))
|
||||
}
|
||||
}
|
||||
@@ -305,12 +304,12 @@ func (s *GraphTestSuite) TestGraphContentTypeHeaders(c *C) {
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
contentType := w.Header().Get("Content-Type")
|
||||
|
||||
|
||||
if test.expectJSON {
|
||||
c.Check(strings.Contains(contentType, "application/json"), Equals, true,
|
||||
Commentf("Expected JSON content type for .%s, got: %s", test.ext, contentType))
|
||||
}
|
||||
|
||||
|
||||
// Note: Image content types will only be set if graphviz is available and context exists
|
||||
c.Check(contentType, Not(Equals), "", Commentf("Content type should be set for .%s", test.ext))
|
||||
}
|
||||
@@ -381,4 +380,4 @@ func (s *GraphTestSuite) TestGraphConcurrency(c *C) {
|
||||
}
|
||||
|
||||
c.Check(true, Equals, true) // Test completed without deadlocks
|
||||
}
|
||||
}
|
||||
|
||||
+72
-73
@@ -6,7 +6,6 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
|
||||
"github.com/aptly-dev/aptly/aptly"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -23,7 +22,7 @@ var _ = Suite(&MetricsTestSuite{})
|
||||
func (s *MetricsTestSuite) SetUpTest(c *C) {
|
||||
// Reset metrics registrar state for each test
|
||||
MetricsCollectorRegistrar.hasRegistered = false
|
||||
|
||||
|
||||
// Create new router for testing
|
||||
s.router = gin.New()
|
||||
gin.SetMode(gin.TestMode)
|
||||
@@ -32,11 +31,11 @@ func (s *MetricsTestSuite) SetUpTest(c *C) {
|
||||
func (s *MetricsTestSuite) TestMetricsCollectorRegistrarRegisterOnce(c *C) {
|
||||
// Test that metrics are only registered once
|
||||
registrar := &metricsCollectorRegistrar{hasRegistered: false}
|
||||
|
||||
|
||||
// First registration should work
|
||||
registrar.Register(s.router)
|
||||
c.Check(registrar.hasRegistered, Equals, true)
|
||||
|
||||
|
||||
// Second registration should be skipped
|
||||
registrar.Register(s.router)
|
||||
c.Check(registrar.hasRegistered, Equals, true)
|
||||
@@ -45,19 +44,19 @@ func (s *MetricsTestSuite) TestMetricsCollectorRegistrarRegisterOnce(c *C) {
|
||||
func (s *MetricsTestSuite) TestMetricsCollectorRegistrarVersionGauge(c *C) {
|
||||
// Test that version gauge is set correctly
|
||||
registrar := &metricsCollectorRegistrar{hasRegistered: false}
|
||||
|
||||
|
||||
// Register metrics
|
||||
registrar.Register(s.router)
|
||||
|
||||
|
||||
// Check that version gauge was set
|
||||
expectedLabels := prometheus.Labels{
|
||||
"version": aptly.Version,
|
||||
"goversion": runtime.Version(),
|
||||
}
|
||||
|
||||
|
||||
gauge := apiVersionGauge.With(expectedLabels)
|
||||
c.Check(gauge, NotNil)
|
||||
|
||||
|
||||
// Verify the gauge value is 1
|
||||
metric := &dto.Metric{}
|
||||
gauge.(prometheus.Gauge).Write(metric)
|
||||
@@ -67,11 +66,11 @@ func (s *MetricsTestSuite) TestMetricsCollectorRegistrarVersionGauge(c *C) {
|
||||
func (s *MetricsTestSuite) TestApiRequestsInFlightGauge(c *C) {
|
||||
// Test that in-flight requests gauge works
|
||||
c.Check(apiRequestsInFlightGauge, NotNil)
|
||||
|
||||
|
||||
// Test that we can create labels for the gauge
|
||||
gauge := apiRequestsInFlightGauge.WithLabelValues("GET", "/api/test")
|
||||
c.Check(gauge, NotNil)
|
||||
|
||||
|
||||
// Test incrementing and decrementing
|
||||
gauge.Inc()
|
||||
gauge.Dec()
|
||||
@@ -80,11 +79,11 @@ func (s *MetricsTestSuite) TestApiRequestsInFlightGauge(c *C) {
|
||||
func (s *MetricsTestSuite) TestApiRequestsTotalCounter(c *C) {
|
||||
// Test that total requests counter works
|
||||
c.Check(apiRequestsTotalCounter, NotNil)
|
||||
|
||||
|
||||
// Test that we can create labels for the counter
|
||||
counter := apiRequestsTotalCounter.WithLabelValues("200", "GET", "/api/test")
|
||||
c.Check(counter, NotNil)
|
||||
|
||||
|
||||
// Test incrementing
|
||||
counter.Inc()
|
||||
}
|
||||
@@ -92,11 +91,11 @@ func (s *MetricsTestSuite) TestApiRequestsTotalCounter(c *C) {
|
||||
func (s *MetricsTestSuite) TestApiRequestSizeSummary(c *C) {
|
||||
// Test that request size summary works
|
||||
c.Check(apiRequestSizeSummary, NotNil)
|
||||
|
||||
|
||||
// Test that we can create labels for the summary
|
||||
summary := apiRequestSizeSummary.WithLabelValues("200", "POST", "/api/test")
|
||||
c.Check(summary, NotNil)
|
||||
|
||||
|
||||
// Test observing values
|
||||
summary.Observe(1024.0)
|
||||
summary.Observe(512.0)
|
||||
@@ -105,11 +104,11 @@ func (s *MetricsTestSuite) TestApiRequestSizeSummary(c *C) {
|
||||
func (s *MetricsTestSuite) TestApiResponseSizeSummary(c *C) {
|
||||
// Test that response size summary works
|
||||
c.Check(apiResponseSizeSummary, NotNil)
|
||||
|
||||
|
||||
// Test that we can create labels for the summary
|
||||
summary := apiResponseSizeSummary.WithLabelValues("200", "GET", "/api/test")
|
||||
c.Check(summary, NotNil)
|
||||
|
||||
|
||||
// Test observing values
|
||||
summary.Observe(2048.0)
|
||||
summary.Observe(1024.0)
|
||||
@@ -118,25 +117,25 @@ func (s *MetricsTestSuite) TestApiResponseSizeSummary(c *C) {
|
||||
func (s *MetricsTestSuite) TestApiRequestsDurationSummary(c *C) {
|
||||
// Test that request duration summary works
|
||||
c.Check(apiRequestsDurationSummary, NotNil)
|
||||
|
||||
|
||||
// Test that we can create labels for the summary
|
||||
summary := apiRequestsDurationSummary.WithLabelValues("200", "GET", "/api/test")
|
||||
c.Check(summary, NotNil)
|
||||
|
||||
|
||||
// Test observing duration values
|
||||
summary.Observe(0.1) // 100ms
|
||||
summary.Observe(0.05) // 50ms
|
||||
summary.Observe(1.0) // 1s
|
||||
summary.Observe(0.1) // 100ms
|
||||
summary.Observe(0.05) // 50ms
|
||||
summary.Observe(1.0) // 1s
|
||||
}
|
||||
|
||||
func (s *MetricsTestSuite) TestApiFilesUploadedCounter(c *C) {
|
||||
// Test that files uploaded counter works
|
||||
c.Check(apiFilesUploadedCounter, NotNil)
|
||||
|
||||
|
||||
// Test that we can create labels for the counter
|
||||
counter := apiFilesUploadedCounter.WithLabelValues("uploads")
|
||||
c.Check(counter, NotNil)
|
||||
|
||||
|
||||
// Test incrementing
|
||||
counter.Inc()
|
||||
counter.Add(5)
|
||||
@@ -145,11 +144,11 @@ func (s *MetricsTestSuite) TestApiFilesUploadedCounter(c *C) {
|
||||
func (s *MetricsTestSuite) TestApiReposPackageCountGauge(c *C) {
|
||||
// Test that repos package count gauge works
|
||||
c.Check(apiReposPackageCountGauge, NotNil)
|
||||
|
||||
|
||||
// Test that we can create labels for the gauge
|
||||
gauge := apiReposPackageCountGauge.WithLabelValues("source", "stable", "main")
|
||||
c.Check(gauge, NotNil)
|
||||
|
||||
|
||||
// Test setting values
|
||||
gauge.Set(100)
|
||||
gauge.Set(150)
|
||||
@@ -159,11 +158,11 @@ func (s *MetricsTestSuite) TestApiReposPackageCountGauge(c *C) {
|
||||
|
||||
func (s *MetricsTestSuite) TestMetricsPrometheusIntegration(c *C) {
|
||||
// Test integration with Prometheus client library
|
||||
|
||||
|
||||
// Test that metrics are properly registered with default registry
|
||||
metricNames := []string{
|
||||
"aptly_api_http_requests_in_flight",
|
||||
"aptly_api_http_requests_total",
|
||||
"aptly_api_http_requests_total",
|
||||
"aptly_api_http_request_size_bytes",
|
||||
"aptly_api_http_response_size_bytes",
|
||||
"aptly_api_http_request_duration_seconds",
|
||||
@@ -171,12 +170,12 @@ func (s *MetricsTestSuite) TestMetricsPrometheusIntegration(c *C) {
|
||||
"aptly_api_files_uploaded_total",
|
||||
"aptly_repos_package_count",
|
||||
}
|
||||
|
||||
|
||||
for _, metricName := range metricNames {
|
||||
// Try to gather metrics to ensure they're registered
|
||||
gathered, err := prometheus.DefaultGatherer.Gather()
|
||||
c.Check(err, IsNil)
|
||||
|
||||
|
||||
found := false
|
||||
for _, metricFamily := range gathered {
|
||||
if metricFamily.GetName() == metricName {
|
||||
@@ -190,35 +189,35 @@ func (s *MetricsTestSuite) TestMetricsPrometheusIntegration(c *C) {
|
||||
|
||||
func (s *MetricsTestSuite) TestMetricsLabels(c *C) {
|
||||
// Test that metrics have expected labels
|
||||
|
||||
|
||||
// Test in-flight gauge labels
|
||||
gauge := apiRequestsInFlightGauge.WithLabelValues("GET", "/api/test")
|
||||
c.Check(gauge, NotNil)
|
||||
|
||||
|
||||
// Test total counter labels
|
||||
counter := apiRequestsTotalCounter.WithLabelValues("200", "GET", "/api/test")
|
||||
c.Check(counter, NotNil)
|
||||
|
||||
|
||||
// Test request size summary labels
|
||||
requestSummary := apiRequestSizeSummary.WithLabelValues("200", "POST", "/api/upload")
|
||||
c.Check(requestSummary, NotNil)
|
||||
|
||||
|
||||
// Test response size summary labels
|
||||
responseSummary := apiResponseSizeSummary.WithLabelValues("404", "GET", "/api/missing")
|
||||
c.Check(responseSummary, NotNil)
|
||||
|
||||
|
||||
// Test duration summary labels
|
||||
durationSummary := apiRequestsDurationSummary.WithLabelValues("500", "POST", "/api/error")
|
||||
c.Check(durationSummary, NotNil)
|
||||
|
||||
|
||||
// Test version gauge labels
|
||||
versionGauge := apiVersionGauge.WithLabelValues("1.0.0", "go1.19")
|
||||
c.Check(versionGauge, NotNil)
|
||||
|
||||
|
||||
// Test files uploaded counter labels
|
||||
filesCounter := apiFilesUploadedCounter.WithLabelValues("temp-uploads")
|
||||
c.Check(filesCounter, NotNil)
|
||||
|
||||
|
||||
// Test repos package count gauge labels
|
||||
reposGauge := apiReposPackageCountGauge.WithLabelValues("snapshot:test", "testing", "contrib")
|
||||
c.Check(reposGauge, NotNil)
|
||||
@@ -227,18 +226,18 @@ func (s *MetricsTestSuite) TestMetricsLabels(c *C) {
|
||||
func (s *MetricsTestSuite) TestMetricsWithDifferentHTTPCodes(c *C) {
|
||||
// Test metrics with various HTTP status codes
|
||||
httpCodes := []string{"200", "201", "400", "401", "403", "404", "409", "500", "502", "503"}
|
||||
|
||||
|
||||
for _, code := range httpCodes {
|
||||
// Test that metrics work with different status codes
|
||||
counter := apiRequestsTotalCounter.WithLabelValues(code, "GET", "/api/test")
|
||||
counter.Inc()
|
||||
|
||||
|
||||
requestSummary := apiRequestSizeSummary.WithLabelValues(code, "POST", "/api/test")
|
||||
requestSummary.Observe(100)
|
||||
|
||||
|
||||
responseSummary := apiResponseSizeSummary.WithLabelValues(code, "GET", "/api/test")
|
||||
responseSummary.Observe(200)
|
||||
|
||||
|
||||
durationSummary := apiRequestsDurationSummary.WithLabelValues(code, "PUT", "/api/test")
|
||||
durationSummary.Observe(0.1)
|
||||
}
|
||||
@@ -247,13 +246,13 @@ func (s *MetricsTestSuite) TestMetricsWithDifferentHTTPCodes(c *C) {
|
||||
func (s *MetricsTestSuite) TestMetricsWithDifferentHTTPMethods(c *C) {
|
||||
// Test metrics with various HTTP methods
|
||||
httpMethods := []string{"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"}
|
||||
|
||||
|
||||
for _, method := range httpMethods {
|
||||
// Test that metrics work with different HTTP methods
|
||||
gauge := apiRequestsInFlightGauge.WithLabelValues(method, "/api/test")
|
||||
gauge.Inc()
|
||||
gauge.Dec()
|
||||
|
||||
|
||||
counter := apiRequestsTotalCounter.WithLabelValues("200", method, "/api/test")
|
||||
counter.Inc()
|
||||
}
|
||||
@@ -272,11 +271,11 @@ func (s *MetricsTestSuite) TestMetricsWithDifferentPaths(c *C) {
|
||||
"/api/tasks",
|
||||
"/api/version",
|
||||
}
|
||||
|
||||
|
||||
for _, path := range apiPaths {
|
||||
counter := apiRequestsTotalCounter.WithLabelValues("200", "GET", path)
|
||||
counter.Inc()
|
||||
|
||||
|
||||
gauge := apiRequestsInFlightGauge.WithLabelValues("GET", path)
|
||||
gauge.Inc()
|
||||
gauge.Dec()
|
||||
@@ -286,42 +285,42 @@ func (s *MetricsTestSuite) TestMetricsWithDifferentPaths(c *C) {
|
||||
func (s *MetricsTestSuite) TestMetricsThreadSafety(c *C) {
|
||||
// Test that metrics are thread-safe by simulating concurrent access
|
||||
done := make(chan bool, 10)
|
||||
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go func(id int) {
|
||||
defer func() { done <- true }()
|
||||
|
||||
|
||||
// Simulate concurrent metric updates
|
||||
for j := 0; j < 100; j++ {
|
||||
counter := apiRequestsTotalCounter.WithLabelValues("200", "GET", "/api/concurrent")
|
||||
counter.Inc()
|
||||
|
||||
gauge := apiRequestsInFlightGauge.WithLabelValues("GET", "/api/concurrent")
|
||||
|
||||
gauge := apiRequestsInFlightGauge.WithLabelValues("GET", "/api/concurrent")
|
||||
gauge.Inc()
|
||||
gauge.Dec()
|
||||
|
||||
|
||||
summary := apiRequestsDurationSummary.WithLabelValues("200", "GET", "/api/concurrent")
|
||||
summary.Observe(0.01)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
|
||||
// Wait for all goroutines to complete
|
||||
for i := 0; i < 10; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
|
||||
// Verify metrics were updated (exact count doesn't matter due to concurrency)
|
||||
c.Check(true, Equals, true) // Test completed without race conditions
|
||||
}
|
||||
|
||||
func (s *MetricsTestSuite) TestMetricsMetadata(c *C) {
|
||||
// Test that metrics have proper metadata (help text, names)
|
||||
|
||||
|
||||
// Gather all metrics
|
||||
gathered, err := prometheus.DefaultGatherer.Gather()
|
||||
c.Check(err, IsNil)
|
||||
|
||||
|
||||
expectedMetrics := map[string]string{
|
||||
"aptly_api_http_requests_in_flight": "Number of concurrent HTTP api requests currently handled.",
|
||||
"aptly_api_http_requests_total": "Total number of api requests.",
|
||||
@@ -332,11 +331,11 @@ func (s *MetricsTestSuite) TestMetricsMetadata(c *C) {
|
||||
"aptly_api_files_uploaded_total": "Total number of uploaded files labeled by upload directory.",
|
||||
"aptly_repos_package_count": "Current number of published packages labeled by source, distribution and component.",
|
||||
}
|
||||
|
||||
|
||||
for _, metricFamily := range gathered {
|
||||
metricName := metricFamily.GetName()
|
||||
if expectedHelp, exists := expectedMetrics[metricName]; exists {
|
||||
c.Check(metricFamily.GetHelp(), Equals, expectedHelp,
|
||||
c.Check(metricFamily.GetHelp(), Equals, expectedHelp,
|
||||
Commentf("Help text mismatch for metric: %s", metricName))
|
||||
}
|
||||
}
|
||||
@@ -346,16 +345,16 @@ func (s *MetricsTestSuite) TestCountPackagesByRepos(c *C) {
|
||||
// Test countPackagesByRepos function structure
|
||||
// Note: This function requires database context which we don't have in tests,
|
||||
// but we can test that it doesn't crash when called
|
||||
|
||||
|
||||
// This will likely error due to no context, but should not panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
c.Fatalf("countPackagesByRepos panicked: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
countPackagesByRepos()
|
||||
|
||||
|
||||
// If we get here, the function didn't panic
|
||||
c.Check(true, Equals, true)
|
||||
}
|
||||
@@ -363,34 +362,34 @@ func (s *MetricsTestSuite) TestCountPackagesByRepos(c *C) {
|
||||
func (s *MetricsTestSuite) TestMetricsRegistration(c *C) {
|
||||
// Test that metrics registration works correctly with gin router
|
||||
MetricsCollectorRegistrar.Register(s.router)
|
||||
|
||||
|
||||
// Create a test request to trigger middleware
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
|
||||
// Add a test handler
|
||||
s.router.GET("/test", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"test": "response"})
|
||||
})
|
||||
|
||||
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
|
||||
c.Check(w.Code, Equals, 200)
|
||||
c.Check(MetricsCollectorRegistrar.hasRegistered, Equals, true)
|
||||
}
|
||||
|
||||
func (s *MetricsTestSuite) TestMetricsErrorConditions(c *C) {
|
||||
// Test error handling in metrics collection
|
||||
|
||||
|
||||
// Test with invalid label values (should not crash)
|
||||
invalidLabels := []string{"", "very_long_label_" + strings.Repeat("x", 1000), "label\nwith\nnewlines"}
|
||||
|
||||
|
||||
for _, label := range invalidLabels {
|
||||
// These should not crash, even with invalid labels
|
||||
gauge := apiRequestsInFlightGauge.WithLabelValues("GET", label)
|
||||
gauge.Inc()
|
||||
gauge.Dec()
|
||||
|
||||
|
||||
counter := apiRequestsTotalCounter.WithLabelValues("200", "GET", label)
|
||||
counter.Inc()
|
||||
}
|
||||
@@ -398,21 +397,21 @@ func (s *MetricsTestSuite) TestMetricsErrorConditions(c *C) {
|
||||
|
||||
func (s *MetricsTestSuite) TestMetricsValueRanges(c *C) {
|
||||
// Test metrics with various value ranges
|
||||
|
||||
|
||||
// Test large values
|
||||
summary := apiRequestSizeSummary.WithLabelValues("200", "POST", "/api/large")
|
||||
summary.Observe(1e9) // 1GB
|
||||
summary.Observe(1e12) // 1TB
|
||||
|
||||
|
||||
// Test very small values
|
||||
durationSummary := apiRequestsDurationSummary.WithLabelValues("200", "GET", "/api/fast")
|
||||
durationSummary.Observe(1e-9) // 1 nanosecond
|
||||
durationSummary.Observe(1e-6) // 1 microsecond
|
||||
|
||||
|
||||
// Test zero values
|
||||
gauge := apiReposPackageCountGauge.WithLabelValues("empty", "dist", "comp")
|
||||
gauge.Set(0)
|
||||
|
||||
|
||||
// Test negative values (should be handled gracefully)
|
||||
gauge.Set(-1) // May or may not be allowed by Prometheus, but shouldn't crash
|
||||
}
|
||||
@@ -426,13 +425,13 @@ func (s *MetricsTestSuite) TestMetricsWithSpecialCharacters(c *C) {
|
||||
"/api/repos/repo+with+plus",
|
||||
"/api/repos/repo%20with%20encoded",
|
||||
}
|
||||
|
||||
|
||||
for _, path := range specialPaths {
|
||||
counter := apiRequestsTotalCounter.WithLabelValues("200", "GET", path)
|
||||
counter.Inc()
|
||||
|
||||
|
||||
gauge := apiRequestsInFlightGauge.WithLabelValues("GET", path)
|
||||
gauge.Inc()
|
||||
gauge.Dec()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-20
@@ -384,10 +384,10 @@ func (s *PublishAPITestSuite) TestSigningParamsEdgeCases(c *C) {
|
||||
// Test signingParams with edge cases
|
||||
testCases := []signingParams{
|
||||
{Skip: true}, // Minimal case
|
||||
{Skip: false, GpgKey: "", Keyring: "", SecretKeyring: "", Passphrase: "", PassphraseFile: ""}, // Empty strings
|
||||
{Skip: false, GpgKey: "", Keyring: "", SecretKeyring: "", Passphrase: "", PassphraseFile: ""}, // Empty strings
|
||||
{Skip: false, GpgKey: "very-long-key-id-123456789012345678901234567890", Keyring: "very-long-keyring-name.gpg"}, // Long values
|
||||
{Skip: false, Passphrase: "password with spaces and special chars !@#$%^&*()"}, // Special characters
|
||||
{Skip: false, PassphraseFile: "/very/long/path/to/passphrase/file/that/might/not/exist.txt"}, // Long file path
|
||||
{Skip: false, Passphrase: "password with spaces and special chars !@#$%^&*()"}, // Special characters
|
||||
{Skip: false, PassphraseFile: "/very/long/path/to/passphrase/file/that/might/not/exist.txt"}, // Long file path
|
||||
}
|
||||
|
||||
for i, params := range testCases {
|
||||
@@ -409,9 +409,9 @@ func (s *PublishAPITestSuite) TestSourceParamsEdgeCases(c *C) {
|
||||
testCases := []sourceParams{
|
||||
{Component: "", Name: ""}, // Empty strings
|
||||
{Component: "very-long-component-name-with-dashes-and-numbers-123", Name: "very-long-name-456"}, // Long values
|
||||
{Component: "comp.with.dots", Name: "name_with_underscores"}, // Special characters
|
||||
{Component: "UPPERCASE", Name: "MixedCase"}, // Case variations
|
||||
{Component: "123numeric", Name: "456numbers"}, // Numeric values
|
||||
{Component: "comp.with.dots", Name: "name_with_underscores"}, // Special characters
|
||||
{Component: "UPPERCASE", Name: "MixedCase"}, // Case variations
|
||||
{Component: "123numeric", Name: "456numbers"}, // Numeric values
|
||||
}
|
||||
|
||||
for i, params := range testCases {
|
||||
@@ -460,20 +460,26 @@ func (s *PublishAPITestSuite) TestSlashEscapeComprehensive(c *C) {
|
||||
|
||||
// Mock implementations for testing context dependencies
|
||||
type MockSigner struct {
|
||||
initError error
|
||||
key string
|
||||
keyring string
|
||||
secretKeyring string
|
||||
passphrase string
|
||||
initError error
|
||||
key string
|
||||
keyring string
|
||||
secretKeyring string
|
||||
passphrase string
|
||||
passphraseFile string
|
||||
batch bool
|
||||
batch bool
|
||||
}
|
||||
|
||||
func (m *MockSigner) SetKey(key string) { m.key = key }
|
||||
func (m *MockSigner) SetKeyRing(keyring, secretKeyring string) { m.keyring = keyring; m.secretKeyring = secretKeyring }
|
||||
func (m *MockSigner) SetPassphrase(passphrase, passphraseFile string) { m.passphrase = passphrase; m.passphraseFile = passphraseFile }
|
||||
func (m *MockSigner) SetBatch(batch bool) { m.batch = batch }
|
||||
func (m *MockSigner) Init() error { return m.initError }
|
||||
func (m *MockSigner) SetKey(key string) { m.key = key }
|
||||
func (m *MockSigner) SetKeyRing(keyring, secretKeyring string) {
|
||||
m.keyring = keyring
|
||||
m.secretKeyring = secretKeyring
|
||||
}
|
||||
func (m *MockSigner) SetPassphrase(passphrase, passphraseFile string) {
|
||||
m.passphrase = passphrase
|
||||
m.passphraseFile = passphraseFile
|
||||
}
|
||||
func (m *MockSigner) SetBatch(batch bool) { m.batch = batch }
|
||||
func (m *MockSigner) Init() error { return m.initError }
|
||||
|
||||
func (s *PublishAPITestSuite) TestGetSignerMockSuccess(c *C) {
|
||||
// Test getSigner logic with mock (can't test actual getSigner due to context dependencies)
|
||||
@@ -488,7 +494,7 @@ func (s *PublishAPITestSuite) TestGetSignerMockSuccess(c *C) {
|
||||
|
||||
// Mock the signer behavior
|
||||
mockSigner := &MockSigner{initError: nil}
|
||||
|
||||
|
||||
// Simulate what getSigner would do
|
||||
mockSigner.SetKey(options.GpgKey)
|
||||
mockSigner.SetKeyRing(options.Keyring, options.SecretKeyring)
|
||||
@@ -514,7 +520,7 @@ func (s *PublishAPITestSuite) TestGetSignerMockError(c *C) {
|
||||
|
||||
// Mock the signer behavior with error
|
||||
mockSigner := &MockSigner{initError: fmt.Errorf("mock init error")}
|
||||
|
||||
|
||||
mockSigner.SetKey(options.GpgKey)
|
||||
mockSigner.SetKeyRing(options.Keyring, options.SecretKeyring)
|
||||
mockSigner.SetPassphrase(options.Passphrase, options.PassphraseFile)
|
||||
@@ -523,4 +529,4 @@ func (s *PublishAPITestSuite) TestGetSignerMockError(c *C) {
|
||||
|
||||
c.Check(err, NotNil)
|
||||
c.Check(err.Error(), Equals, "mock init error")
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -901,10 +901,10 @@ func apiReposIncludePackageFromDir(c *gin.Context) {
|
||||
out.Printf("Failed files: %s\n", strings.Join(failedFiles, ", "))
|
||||
}
|
||||
|
||||
ret := reposIncludePackageFromDirResponse{
|
||||
ret := reposIncludePackageFromDirResponse{
|
||||
Report: reporter,
|
||||
FailedFiles: failedFiles,
|
||||
}
|
||||
}
|
||||
return &task.ProcessReturnValue{Code: http.StatusOK, Value: ret}, nil
|
||||
})
|
||||
}
|
||||
|
||||
+3
-4
@@ -8,7 +8,6 @@ import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
||||
|
||||
"github.com/aptly-dev/aptly/deb"
|
||||
"github.com/aptly-dev/aptly/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -479,8 +478,8 @@ func (s *ReposTestSuite) TestReposErrorHandling(c *C) {
|
||||
{"Missing required fields", "POST", "/api/repos", `{}`, true},
|
||||
{"Invalid package refs", "POST", "/api/repos/test/packages", `{"PackageRefs":[]}`, true},
|
||||
{"Invalid query format", "GET", "/api/repos/test/packages?q=invalid[query", "", false}, // Query validation happens deeper
|
||||
{"Copy to same repo", "POST", "/api/repos/test/copy/test/pkg", `{}`, false}, // Error happens in business logic
|
||||
{"Empty directory path", "POST", "/api/repos/test/file/", "", false}, // Path handling
|
||||
{"Copy to same repo", "POST", "/api/repos/test/copy/test/pkg", `{}`, false}, // Error happens in business logic
|
||||
{"Empty directory path", "POST", "/api/repos/test/file/", "", false}, // Path handling
|
||||
}
|
||||
|
||||
for _, test := range errorTests {
|
||||
@@ -498,4 +497,4 @@ func (s *ReposTestSuite) TestReposErrorHandling(c *C) {
|
||||
// All should return some response without crashing
|
||||
c.Check(w.Code, Not(Equals), 0, Commentf("Test: %s", test.description))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-12
@@ -77,7 +77,7 @@ func Router(c *ctx.AptlyContext) http.Handler {
|
||||
}
|
||||
|
||||
if c.Config().ServeInAPIMode {
|
||||
router.GET("/repos/", reposListInAPIMode(c.Config().FileSystemPublishRoots))
|
||||
router.GET("/repos/", reposListInAPIMode(c.Config().GetFileSystemPublishRoots()))
|
||||
router.GET("/repos/:storage/*pkgPath", reposServeInAPIMode)
|
||||
}
|
||||
|
||||
@@ -86,25 +86,17 @@ func Router(c *ctx.AptlyContext) http.Handler {
|
||||
// We use a goroutine to count the number of
|
||||
// concurrent requests. When no more requests are
|
||||
// running, we close the database to free the lock.
|
||||
dbRequests = make(chan dbRequest)
|
||||
|
||||
go acquireDatabase()
|
||||
initDBRequests()
|
||||
|
||||
api.Use(func(c *gin.Context) {
|
||||
var err error
|
||||
|
||||
errCh := make(chan error)
|
||||
dbRequests <- dbRequest{acquiredb, errCh}
|
||||
|
||||
err = <-errCh
|
||||
err := acquireDatabaseConnection()
|
||||
if err != nil {
|
||||
AbortWithJSONError(c, 500, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
dbRequests <- dbRequest{releasedb, errCh}
|
||||
err = <-errCh
|
||||
err := releaseDatabaseConnection()
|
||||
if err != nil {
|
||||
AbortWithJSONError(c, 500, err)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ import (
|
||||
// @Router /api/s3 [get]
|
||||
func apiS3List(c *gin.Context) {
|
||||
keys := []string{}
|
||||
for k := range context.Config().S3PublishRoots {
|
||||
// Use safe accessor to get a copy of the map
|
||||
s3Roots := context.Config().GetS3PublishRoots()
|
||||
for k := range s3Roots {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
c.JSON(200, keys)
|
||||
|
||||
+55
-56
@@ -7,7 +7,6 @@ import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
@@ -21,7 +20,7 @@ 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)
|
||||
@@ -33,7 +32,7 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsListGet(c *C) {
|
||||
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)
|
||||
}
|
||||
@@ -43,19 +42,19 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsListWithSort(c *C) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -68,14 +67,14 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsCreatePost(c *C) {
|
||||
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)
|
||||
}
|
||||
@@ -84,10 +83,10 @@ 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
|
||||
}
|
||||
|
||||
@@ -96,14 +95,14 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateMissingName(c *C) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -113,14 +112,14 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateFromMirrorPost(c *C) {
|
||||
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)
|
||||
}
|
||||
@@ -129,10 +128,10 @@ 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
|
||||
}
|
||||
|
||||
@@ -141,14 +140,14 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateFromMirrorMissingName(c *C)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -158,14 +157,14 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateWithAsync(c *C) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -175,14 +174,14 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateFromMirrorWithAsync(c *C) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -194,7 +193,7 @@ func (s *SnapshotAPITestSuite) TestSnapshotsCreateParamsStruct(c *C) {
|
||||
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"})
|
||||
@@ -207,7 +206,7 @@ func (s *SnapshotAPITestSuite) TestSnapshotsCreateFromMirrorParamsStruct(c *C) {
|
||||
Name: "mirror-test-name",
|
||||
Description: "mirror-test-description",
|
||||
}
|
||||
|
||||
|
||||
c.Check(params.Name, Equals, "mirror-test-name")
|
||||
c.Check(params.Description, Equals, "mirror-test-description")
|
||||
}
|
||||
@@ -216,10 +215,10 @@ 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
|
||||
}
|
||||
|
||||
@@ -227,10 +226,10 @@ 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
|
||||
}
|
||||
|
||||
@@ -239,7 +238,7 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsListDefaultSort(c *C) {
|
||||
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)
|
||||
}
|
||||
@@ -252,27 +251,27 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateComplexPayload(c *C) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -286,20 +285,20 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsCreateSpecialCharacters(c *C) {
|
||||
"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))
|
||||
}
|
||||
}
|
||||
@@ -309,7 +308,7 @@ func (s *SnapshotAPITestSuite) TestApiSnapshotsListEmptyResponse(c *C) {
|
||||
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)
|
||||
}
|
||||
@@ -318,45 +317,45 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -4,7 +4,6 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
@@ -66,11 +65,11 @@ func (s *StorageTestSuite) TestStorageResponseStructure(c *C) {
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
c.Check(w.Code, Equals, 200)
|
||||
|
||||
|
||||
// Should have valid JSON response
|
||||
body := w.Body.String()
|
||||
c.Check(len(body), Not(Equals), 0)
|
||||
|
||||
|
||||
// Should start with valid JSON structure
|
||||
c.Check(body[0], Equals, byte('{'), Commentf("Response should be JSON object"))
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -4,7 +4,6 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
@@ -352,7 +351,7 @@ func (s *TaskTestSuite) TestTasksContentTypes(c *C) {
|
||||
if test.expectedType != "" {
|
||||
// Check that JSON endpoints return JSON content type
|
||||
contentType := w.Header().Get("Content-Type")
|
||||
c.Check(contentType, Matches, ".*"+test.expectedType+".*",
|
||||
c.Check(contentType, Matches, ".*"+test.expectedType+".*",
|
||||
Commentf("Path: %s, Expected: %s, Got: %s", test.path, test.expectedType, contentType))
|
||||
}
|
||||
}
|
||||
@@ -372,7 +371,7 @@ func (s *TaskTestSuite) TestTasksErrorConditions(c *C) {
|
||||
{"Non-existent task detail", "/api/tasks/999999/detail", "GET", true},
|
||||
{"Non-existent task return value", "/api/tasks/999999/return_value", "GET", true},
|
||||
{"Non-existent task delete", "/api/tasks/999999", "DELETE", true},
|
||||
{"Malformed task path", "/api/tasks/", "GET", false}, // Route not matched
|
||||
{"Malformed task path", "/api/tasks/", "GET", false}, // Route not matched
|
||||
{"Extra path segments", "/api/tasks/123/extra/segment", "GET", false}, // Route not matched
|
||||
}
|
||||
|
||||
@@ -390,12 +389,12 @@ func (s *TaskTestSuite) TestTasksResourceManagement(c *C) {
|
||||
// Test that endpoints handle resource management correctly
|
||||
endpoints := []string{
|
||||
"/api/tasks",
|
||||
"/api/tasks-clear",
|
||||
"/api/tasks-clear",
|
||||
"/api/tasks-wait",
|
||||
"/api/tasks/1",
|
||||
"/api/tasks/1/wait",
|
||||
"/api/tasks/1/output",
|
||||
"/api/tasks/1/detail",
|
||||
"/api/tasks/1/detail",
|
||||
"/api/tasks/1/return_value",
|
||||
}
|
||||
|
||||
@@ -411,8 +410,8 @@ func (s *TaskTestSuite) TestTasksResourceManagement(c *C) {
|
||||
|
||||
// Should complete without hanging or crashing
|
||||
c.Check(w.Code, Not(Equals), 0, Commentf("Endpoint: %s", endpoint))
|
||||
|
||||
|
||||
// Response should have proper headers
|
||||
c.Check(w.Header(), NotNil, Commentf("Endpoint: %s", endpoint))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user