169 lines
5.7 KiB
Go
169 lines
5.7 KiB
Go
package api_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/shcizo/package-updater/internal/api"
|
|
"github.com/shcizo/package-updater/internal/discovery"
|
|
"github.com/shcizo/package-updater/internal/updater"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type fakeFinder struct {
|
|
jobs []discovery.Job
|
|
err error
|
|
}
|
|
|
|
func (f *fakeFinder) FindJobs(_ context.Context, _ string) ([]discovery.Job, error) {
|
|
return f.jobs, f.err
|
|
}
|
|
|
|
type fakeSubmitter struct {
|
|
results []updater.Result
|
|
}
|
|
|
|
func (f *fakeSubmitter) Submit(_ context.Context, jobs []discovery.Job) []updater.Result {
|
|
if f.results != nil {
|
|
return f.results
|
|
}
|
|
out := make([]updater.Result, len(jobs))
|
|
for i, j := range jobs {
|
|
out[i] = updater.Result{Job: j, Status: updater.StatusUpdated}
|
|
}
|
|
return out
|
|
}
|
|
|
|
type fakePinger struct{ err error }
|
|
|
|
func (f *fakePinger) Ping(_ context.Context) (types.Ping, error) {
|
|
return types.Ping{}, f.err
|
|
}
|
|
|
|
func decode[T any](t *testing.T, body io.Reader) T {
|
|
t.Helper()
|
|
var v T
|
|
require.NoError(t, json.NewDecoder(body).Decode(&v))
|
|
return v
|
|
}
|
|
|
|
func TestUpdate_ValidationError(t *testing.T) {
|
|
h := api.NewHandlers(&fakeFinder{}, &fakeSubmitter{}, &fakePinger{}, "v0.0.0", "abc", "now")
|
|
req := httptest.NewRequest(http.MethodPost, "/update",
|
|
strings.NewReader(`{"tag":"v1.2.3"}`))
|
|
w := httptest.NewRecorder()
|
|
h.Update(w, req)
|
|
require.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestUpdate_BadJSON(t *testing.T) {
|
|
h := api.NewHandlers(&fakeFinder{}, &fakeSubmitter{}, &fakePinger{}, "v0.0.0", "abc", "now")
|
|
req := httptest.NewRequest(http.MethodPost, "/update", strings.NewReader(`not json`))
|
|
w := httptest.NewRecorder()
|
|
h.Update(w, req)
|
|
require.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestUpdate_DiscoveryFailureReturns500(t *testing.T) {
|
|
finder := &fakeFinder{err: errors.New("daemon unreachable")}
|
|
h := api.NewHandlers(finder, &fakeSubmitter{}, &fakePinger{}, "v0.0.0", "abc", "now")
|
|
body, _ := json.Marshal(api.UpdateRequest{Image: "r/x"})
|
|
req := httptest.NewRequest(http.MethodPost, "/update", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
h.Update(w, req)
|
|
require.Equal(t, http.StatusInternalServerError, w.Code)
|
|
}
|
|
|
|
func TestUpdate_ZeroMatchesReturns200(t *testing.T) {
|
|
h := api.NewHandlers(&fakeFinder{}, &fakeSubmitter{}, &fakePinger{}, "v0.0.0", "abc", "now")
|
|
body, _ := json.Marshal(api.UpdateRequest{Image: "r/x"})
|
|
req := httptest.NewRequest(http.MethodPost, "/update", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
h.Update(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
resp := decode[api.UpdateResponse](t, w.Body)
|
|
require.Equal(t, 0, resp.Matched)
|
|
}
|
|
|
|
func TestUpdate_AllSucceeded200(t *testing.T) {
|
|
finder := &fakeFinder{jobs: []discovery.Job{
|
|
{Project: "p", Service: "s", WorkingDir: "/x", ConfigFiles: []string{"/x/c.yml"}},
|
|
}}
|
|
h := api.NewHandlers(finder, &fakeSubmitter{}, &fakePinger{}, "v0.0.0", "abc", "now")
|
|
body, _ := json.Marshal(api.UpdateRequest{Image: "r/x", Tag: "v1"})
|
|
req := httptest.NewRequest(http.MethodPost, "/update", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
h.Update(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
resp := decode[api.UpdateResponse](t, w.Body)
|
|
require.Equal(t, 1, resp.Matched)
|
|
require.Equal(t, "updated", resp.Results[0].Status)
|
|
require.Equal(t, "/x/c.yml", resp.Results[0].ComposeFile)
|
|
}
|
|
|
|
func TestUpdate_MixedReturns207(t *testing.T) {
|
|
jobs := []discovery.Job{
|
|
{Project: "p1", Service: "s", ConfigFiles: []string{"/x/c.yml"}},
|
|
{Project: "p2", Service: "s", ConfigFiles: []string{"/y/c.yml"}},
|
|
}
|
|
finder := &fakeFinder{jobs: jobs}
|
|
submitter := &fakeSubmitter{results: []updater.Result{
|
|
{Job: jobs[0], Status: updater.StatusUpdated},
|
|
{Job: jobs[1], Status: updater.StatusFailed, Error: "boom"},
|
|
}}
|
|
h := api.NewHandlers(finder, submitter, &fakePinger{}, "v0.0.0", "abc", "now")
|
|
body, _ := json.Marshal(api.UpdateRequest{Image: "r/x"})
|
|
req := httptest.NewRequest(http.MethodPost, "/update", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
h.Update(w, req)
|
|
require.Equal(t, http.StatusMultiStatus, w.Code)
|
|
}
|
|
|
|
func TestUpdate_AllFailedReturns500(t *testing.T) {
|
|
jobs := []discovery.Job{{Project: "p", Service: "s", ConfigFiles: []string{"/x/c.yml"}}}
|
|
finder := &fakeFinder{jobs: jobs}
|
|
submitter := &fakeSubmitter{results: []updater.Result{
|
|
{Job: jobs[0], Status: updater.StatusFailed, Error: "boom"},
|
|
}}
|
|
h := api.NewHandlers(finder, submitter, &fakePinger{}, "v0.0.0", "abc", "now")
|
|
body, _ := json.Marshal(api.UpdateRequest{Image: "r/x"})
|
|
req := httptest.NewRequest(http.MethodPost, "/update", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
h.Update(w, req)
|
|
require.Equal(t, http.StatusInternalServerError, w.Code)
|
|
}
|
|
|
|
func TestHealthz_OKWhenDockerUp(t *testing.T) {
|
|
h := api.NewHandlers(&fakeFinder{}, &fakeSubmitter{}, &fakePinger{}, "v0.0.0", "abc", "now")
|
|
req := httptest.NewRequest(http.MethodGet, "/healthz", nil)
|
|
w := httptest.NewRecorder()
|
|
h.Healthz(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestHealthz_503WhenDockerDown(t *testing.T) {
|
|
h := api.NewHandlers(&fakeFinder{}, &fakeSubmitter{}, &fakePinger{err: errors.New("ping fail")}, "v0.0.0", "abc", "now")
|
|
req := httptest.NewRequest(http.MethodGet, "/healthz", nil)
|
|
w := httptest.NewRecorder()
|
|
h.Healthz(w, req)
|
|
require.Equal(t, http.StatusServiceUnavailable, w.Code)
|
|
}
|
|
|
|
func TestVersion(t *testing.T) {
|
|
h := api.NewHandlers(&fakeFinder{}, &fakeSubmitter{}, &fakePinger{}, "v1.2.3", "abcdef", "2026-05-22T00:00:00Z")
|
|
req := httptest.NewRequest(http.MethodGet, "/version", nil)
|
|
w := httptest.NewRecorder()
|
|
h.Version(w, req)
|
|
require.Equal(t, http.StatusOK, w.Code)
|
|
resp := decode[api.VersionResponse](t, w.Body)
|
|
require.Equal(t, "v1.2.3", resp.Version)
|
|
}
|