mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-19 07:40:20 +00:00
fcb9bd7bd6
- Add unit tests for API endpoints (db, error, files, gpg, graph, metrics, publish, repos, snapshot, storage, task) - Add command-line interface tests for all cmd subcommands - Add database tests including race condition tests for etcddb and goleveldb - Add package management tests (collections, contents, graph, import, index files, dependencies) - Add infrastructure tests (pgp, systemd activation, task management) - Add utility tests (checksum, config accessor, logging, sanitize) - Add race condition tests for concurrent operations Test coverage improves reliability and helps catch regressions early.
418 lines
13 KiB
Go
418 lines
13 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
. "gopkg.in/check.v1"
|
|
)
|
|
|
|
type TaskTestSuite struct {
|
|
router *gin.Engine
|
|
}
|
|
|
|
var _ = Suite(&TaskTestSuite{})
|
|
|
|
func (s *TaskTestSuite) SetUpTest(c *C) {
|
|
s.router = gin.New()
|
|
s.router.GET("/api/tasks", apiTasksList)
|
|
s.router.POST("/api/tasks-clear", apiTasksClear)
|
|
s.router.GET("/api/tasks-wait", apiTasksWait)
|
|
s.router.GET("/api/tasks/:id/wait", apiTasksWaitForTaskByID)
|
|
s.router.GET("/api/tasks/:id", apiTasksShow)
|
|
s.router.GET("/api/tasks/:id/output", apiTasksOutputShow)
|
|
s.router.GET("/api/tasks/:id/detail", apiTasksDetailShow)
|
|
s.router.GET("/api/tasks/:id/return_value", apiTasksReturnValueShow)
|
|
s.router.DELETE("/api/tasks/:id", apiTasksDelete)
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksListEmpty(c *C) {
|
|
// Test listing tasks when none exist
|
|
req, _ := http.NewRequest("GET", "/api/tasks", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 200)
|
|
c.Check(w.Header().Get("Content-Type"), Equals, "application/json; charset=utf-8")
|
|
|
|
// Will likely return empty array due to no context, but tests structure
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksClearStructure(c *C) {
|
|
// Test clearing tasks
|
|
req, _ := http.NewRequest("POST", "/api/tasks-clear", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 200)
|
|
c.Check(w.Header().Get("Content-Type"), Equals, "application/json; charset=utf-8")
|
|
|
|
// Should return empty object
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksWaitStructure(c *C) {
|
|
// Test waiting for all tasks
|
|
req, _ := http.NewRequest("GET", "/api/tasks-wait", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 200)
|
|
c.Check(w.Header().Get("Content-Type"), Equals, "application/json; charset=utf-8")
|
|
|
|
// Should return empty object after waiting
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksWaitForTaskByIDStructure(c *C) {
|
|
// Test waiting for specific task by ID
|
|
req, _ := http.NewRequest("GET", "/api/tasks/123/wait", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Will error due to no context or invalid task, but tests structure
|
|
c.Check(w.Code, Not(Equals), 200)
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksWaitForTaskByIDInvalidID(c *C) {
|
|
// Test waiting for task with invalid ID
|
|
invalidIDs := []string{"invalid", "abc", "-1", "", "123.45"}
|
|
|
|
for _, id := range invalidIDs {
|
|
req, _ := http.NewRequest("GET", "/api/tasks/"+id+"/wait", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should return 500 for invalid ID format
|
|
c.Check(w.Code, Equals, 500, Commentf("ID: %s", id))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksShowStructure(c *C) {
|
|
// Test showing specific task by ID
|
|
req, _ := http.NewRequest("GET", "/api/tasks/123", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Will error due to no context or invalid task, but tests structure
|
|
c.Check(w.Code, Not(Equals), 200)
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksShowInvalidID(c *C) {
|
|
// Test showing task with invalid ID
|
|
invalidIDs := []string{"invalid", "abc", "-1", "", "123.45", "999999999999999999999"}
|
|
|
|
for _, id := range invalidIDs {
|
|
req, _ := http.NewRequest("GET", "/api/tasks/"+id, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should return 500 for invalid ID format
|
|
c.Check(w.Code, Equals, 500, Commentf("ID: %s", id))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksOutputStructure(c *C) {
|
|
// Test getting task output by ID
|
|
req, _ := http.NewRequest("GET", "/api/tasks/123/output", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Will error due to no context or invalid task, but tests structure
|
|
c.Check(w.Code, Not(Equals), 200)
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksOutputInvalidID(c *C) {
|
|
// Test getting task output with invalid ID
|
|
invalidIDs := []string{"invalid", "abc", "-1", "", "123.45"}
|
|
|
|
for _, id := range invalidIDs {
|
|
req, _ := http.NewRequest("GET", "/api/tasks/"+id+"/output", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should return 500 for invalid ID format
|
|
c.Check(w.Code, Equals, 500, Commentf("ID: %s", id))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksDetailStructure(c *C) {
|
|
// Test getting task detail by ID
|
|
req, _ := http.NewRequest("GET", "/api/tasks/123/detail", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Will error due to no context or invalid task, but tests structure
|
|
c.Check(w.Code, Not(Equals), 200)
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksDetailInvalidID(c *C) {
|
|
// Test getting task detail with invalid ID
|
|
invalidIDs := []string{"invalid", "abc", "-1", "", "123.45"}
|
|
|
|
for _, id := range invalidIDs {
|
|
req, _ := http.NewRequest("GET", "/api/tasks/"+id+"/detail", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should return 500 for invalid ID format
|
|
c.Check(w.Code, Equals, 500, Commentf("ID: %s", id))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksReturnValueStructure(c *C) {
|
|
// Test getting task return value by ID
|
|
req, _ := http.NewRequest("GET", "/api/tasks/123/return_value", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Will error due to no context or invalid task, but tests structure
|
|
c.Check(w.Code, Not(Equals), 200)
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksReturnValueInvalidID(c *C) {
|
|
// Test getting task return value with invalid ID
|
|
invalidIDs := []string{"invalid", "abc", "-1", "", "123.45"}
|
|
|
|
for _, id := range invalidIDs {
|
|
req, _ := http.NewRequest("GET", "/api/tasks/"+id+"/return_value", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should return 500 for invalid ID format
|
|
c.Check(w.Code, Equals, 500, Commentf("ID: %s", id))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksDeleteStructure(c *C) {
|
|
// Test deleting task by ID
|
|
req, _ := http.NewRequest("DELETE", "/api/tasks/123", nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Will error due to no context or invalid task, but tests structure
|
|
c.Check(w.Code, Not(Equals), 200)
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksDeleteInvalidID(c *C) {
|
|
// Test deleting task with invalid ID
|
|
invalidIDs := []string{"invalid", "abc", "-1", "", "123.45"}
|
|
|
|
for _, id := range invalidIDs {
|
|
req, _ := http.NewRequest("DELETE", "/api/tasks/"+id, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should return 500 for invalid ID format
|
|
c.Check(w.Code, Equals, 500, Commentf("ID: %s", id))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksValidIDFormats(c *C) {
|
|
// Test various valid ID formats
|
|
validIDs := []string{"0", "1", "123", "999", "2147483647"} // Max int32
|
|
|
|
for _, id := range validIDs {
|
|
// Test show endpoint
|
|
req, _ := http.NewRequest("GET", "/api/tasks/"+id, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should not be 500 (invalid format), might be 404 (not found) or other error
|
|
c.Check(w.Code, Not(Equals), 500, Commentf("ID: %s", id))
|
|
|
|
// Test wait endpoint
|
|
req, _ = http.NewRequest("GET", "/api/tasks/"+id+"/wait", nil)
|
|
w = httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should not be 500 (invalid format)
|
|
c.Check(w.Code, Not(Equals), 500, Commentf("ID: %s", id))
|
|
|
|
// Test output endpoint
|
|
req, _ = http.NewRequest("GET", "/api/tasks/"+id+"/output", nil)
|
|
w = httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should not be 500 (invalid format)
|
|
c.Check(w.Code, Not(Equals), 500, Commentf("ID: %s", id))
|
|
|
|
// Test detail endpoint
|
|
req, _ = http.NewRequest("GET", "/api/tasks/"+id+"/detail", nil)
|
|
w = httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should not be 500 (invalid format)
|
|
c.Check(w.Code, Not(Equals), 500, Commentf("ID: %s", id))
|
|
|
|
// Test return_value endpoint
|
|
req, _ = http.NewRequest("GET", "/api/tasks/"+id+"/return_value", nil)
|
|
w = httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should not be 500 (invalid format)
|
|
c.Check(w.Code, Not(Equals), 500, Commentf("ID: %s", id))
|
|
|
|
// Test delete endpoint
|
|
req, _ = http.NewRequest("DELETE", "/api/tasks/"+id, nil)
|
|
w = httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should not be 500 (invalid format)
|
|
c.Check(w.Code, Not(Equals), 500, Commentf("ID: %s", id))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksParameterEdgeCases(c *C) {
|
|
// Test edge cases in parameter handling
|
|
edgeCases := []struct {
|
|
path string
|
|
description string
|
|
}{
|
|
{"/api/tasks/0", "zero ID"},
|
|
{"/api/tasks/1", "single digit ID"},
|
|
{"/api/tasks/2147483647", "max int32 ID"},
|
|
{"/api/tasks/00123", "leading zeros"},
|
|
{"/api/tasks/+123", "positive sign"},
|
|
}
|
|
|
|
for _, tc := range edgeCases {
|
|
req, _ := http.NewRequest("GET", tc.path, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should handle edge cases gracefully without crashing
|
|
c.Check(w.Code, Not(Equals), 0, Commentf("Test: %s", tc.description))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksHTTPMethods(c *C) {
|
|
// Test that correct HTTP methods are supported for each endpoint
|
|
methodTests := []struct {
|
|
path string
|
|
allowedMethods []string
|
|
deniedMethods []string
|
|
}{
|
|
{"/api/tasks", []string{"GET"}, []string{"POST", "PUT", "DELETE", "PATCH"}},
|
|
{"/api/tasks-clear", []string{"POST"}, []string{"GET", "PUT", "DELETE", "PATCH"}},
|
|
{"/api/tasks-wait", []string{"GET"}, []string{"POST", "PUT", "DELETE", "PATCH"}},
|
|
{"/api/tasks/123", []string{"GET", "DELETE"}, []string{"POST", "PUT", "PATCH"}},
|
|
{"/api/tasks/123/wait", []string{"GET"}, []string{"POST", "PUT", "DELETE", "PATCH"}},
|
|
{"/api/tasks/123/output", []string{"GET"}, []string{"POST", "PUT", "DELETE", "PATCH"}},
|
|
{"/api/tasks/123/detail", []string{"GET"}, []string{"POST", "PUT", "DELETE", "PATCH"}},
|
|
{"/api/tasks/123/return_value", []string{"GET"}, []string{"POST", "PUT", "DELETE", "PATCH"}},
|
|
}
|
|
|
|
for _, test := range methodTests {
|
|
// Test denied methods return 404 (method not allowed for route)
|
|
for _, method := range test.deniedMethods {
|
|
req, _ := http.NewRequest(method, test.path, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
c.Check(w.Code, Equals, 404, Commentf("Path: %s, Method: %s", test.path, method))
|
|
}
|
|
|
|
// Test allowed methods don't return 404 for method not allowed
|
|
for _, method := range test.allowedMethods {
|
|
req, _ := http.NewRequest(method, test.path, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// Should not be 404 (method not allowed), might be other errors due to missing context
|
|
c.Check(w.Code, Not(Equals), 404, Commentf("Path: %s, Method: %s", test.path, method))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksContentTypes(c *C) {
|
|
// Test content type handling for different endpoints
|
|
contentTypeTests := []struct {
|
|
path string
|
|
method string
|
|
expectedType string
|
|
}{
|
|
{"/api/tasks", "GET", "application/json"},
|
|
{"/api/tasks-clear", "POST", "application/json"},
|
|
{"/api/tasks-wait", "GET", "application/json"},
|
|
{"/api/tasks/123", "GET", "application/json"},
|
|
{"/api/tasks/123/wait", "GET", "application/json"},
|
|
{"/api/tasks/123/output", "GET", ""}, // Text content
|
|
{"/api/tasks/123/detail", "GET", "application/json"},
|
|
{"/api/tasks/123/return_value", "GET", "application/json"},
|
|
}
|
|
|
|
for _, test := range contentTypeTests {
|
|
req, _ := http.NewRequest(test.method, test.path, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
if test.expectedType != "" {
|
|
// Check that JSON endpoints return JSON content type
|
|
contentType := w.Header().Get("Content-Type")
|
|
c.Check(contentType, Matches, ".*"+test.expectedType+".*",
|
|
Commentf("Path: %s, Expected: %s, Got: %s", test.path, test.expectedType, contentType))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksErrorConditions(c *C) {
|
|
// Test various error conditions
|
|
errorTests := []struct {
|
|
description string
|
|
path string
|
|
method string
|
|
expectedErr bool
|
|
}{
|
|
{"Non-existent task ID", "/api/tasks/999999", "GET", true},
|
|
{"Non-existent task wait", "/api/tasks/999999/wait", "GET", true},
|
|
{"Non-existent task output", "/api/tasks/999999/output", "GET", true},
|
|
{"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
|
|
{"Extra path segments", "/api/tasks/123/extra/segment", "GET", false}, // Route not matched
|
|
}
|
|
|
|
for _, test := range errorTests {
|
|
req, _ := http.NewRequest(test.method, test.path, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// All should return some response without crashing
|
|
c.Check(w.Code, Not(Equals), 0, Commentf("Test: %s", test.description))
|
|
}
|
|
}
|
|
|
|
func (s *TaskTestSuite) TestTasksResourceManagement(c *C) {
|
|
// Test that endpoints handle resource management correctly
|
|
endpoints := []string{
|
|
"/api/tasks",
|
|
"/api/tasks-clear",
|
|
"/api/tasks-wait",
|
|
"/api/tasks/1",
|
|
"/api/tasks/1/wait",
|
|
"/api/tasks/1/output",
|
|
"/api/tasks/1/detail",
|
|
"/api/tasks/1/return_value",
|
|
}
|
|
|
|
for _, endpoint := range endpoints {
|
|
method := "GET"
|
|
if endpoint == "/api/tasks-clear" {
|
|
method = "POST"
|
|
}
|
|
|
|
req, _ := http.NewRequest(method, endpoint, nil)
|
|
w := httptest.NewRecorder()
|
|
s.router.ServeHTTP(w, req)
|
|
|
|
// 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))
|
|
}
|
|
} |