feat: complete dockerization (optional)
This commit is contained in:
1 parent
3e01c29657
commit
d275ba3c14
38 files changed
+1244
-20
No files matched your search
@@ -0,0 +1,198 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"RenewCMS/api"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
"RenewCMS/internal/domain/update"
|
||||
"RenewCMS/internal/infrastructure/config"
|
||||
infraupdate "RenewCMS/internal/infrastructure/update"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
type containerHarness struct {
|
||||
*updateHarness
|
||||
fetcher *fakeAssetFetcher
|
||||
requests *fakeRestartRequestStore
|
||||
jobs *fakeJobStore
|
||||
}
|
||||
|
||||
func newContainerHarness(t *testing.T, container bool, helperInstalled bool) *containerHarness {
|
||||
t.Helper()
|
||||
validator.InitValidator()
|
||||
rig := newDownloadRig(t)
|
||||
rig.helper.installed = helperInstalled
|
||||
helper := infraupdate.NewContainerHelperDetector(rig.helper, container)
|
||||
now := func() time.Time { return updateTestCheckedAt }
|
||||
token := jwtauth.New("HS256", []byte("test-secret"), nil)
|
||||
feed := &fakeReleaseFeed{release: update.Release{
|
||||
Tag: "v2.3.0-beta",
|
||||
URL: "https://git.example/fsylvain/RenewCMS/releases/tag/v2.3.0-beta",
|
||||
PublishedAt: time.Date(2026, 9, 24, 8, 0, 0, 0, time.UTC),
|
||||
Notes: update.ReleaseNotes{Version: "v2.3.0-beta", Items: []string{"Media library"}},
|
||||
}}
|
||||
rig.store.found = true
|
||||
start := useCases.NewStartUpdateDownloadUseCase(useCases.StartUpdateDownloadDependencies{
|
||||
Store: rig.store,
|
||||
Jobs: rig.jobs,
|
||||
Helper: helper,
|
||||
Results: rig.results,
|
||||
Requests: rig.requests,
|
||||
Fetcher: rig.fetcher,
|
||||
Verifier: nil,
|
||||
Runner: rig.runner,
|
||||
Backup: rig.backup,
|
||||
Staging: rig.staging,
|
||||
Logger: rig.logger,
|
||||
CurrentVersion: updateRunning,
|
||||
Platform: rig.platform,
|
||||
FeedURL: downloadFeedURL,
|
||||
Now: now,
|
||||
})
|
||||
routes := &api.UseCases{
|
||||
Token: token,
|
||||
GetUserUseCase: useCases.NewGetUserUseCase(newUpdateUserRepo()),
|
||||
GetUpdateStatusUseCase: useCases.NewGetUpdateStatusUseCase(rig.store, rig.jobs, helper, rig.results, rig.requests, updateRunning, container, now),
|
||||
CheckForUpdateUseCase: useCases.NewCheckForUpdateUseCase(feed, rig.store, updateRunning, now),
|
||||
StartUpdateDownloadUseCase: start,
|
||||
RequestRestartUseCase: useCases.NewRequestRestartUseCase(rig.store, rig.jobs, helper, rig.results, rig.requests, updateRunning, now),
|
||||
}
|
||||
return &containerHarness{
|
||||
updateHarness: &updateHarness{router: api.NewSystemRouter(routes), token: token, feed: feed, store: rig.store},
|
||||
fetcher: rig.fetcher,
|
||||
requests: rig.requests,
|
||||
jobs: rig.jobs,
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerUpdateGuidanceHTTP(t *testing.T) {
|
||||
t.Run("The status carries the container flag", func(t *testing.T) {
|
||||
cases := []struct {
|
||||
value string
|
||||
flag bool
|
||||
}{{"true", true}, {"TRUE", true}, {"false", false}, {"", false}, {"yes", false}}
|
||||
for _, tc := range cases {
|
||||
t.Run("value "+tc.value, func(t *testing.T) {
|
||||
t.Setenv("SMTP_PORT", "1")
|
||||
t.Setenv("AUTH_RATE_LIMIT_MAX_ATTEMPTS", "1")
|
||||
t.Setenv("AUTH_RATE_LIMIT_WINDOW_SECONDS", "1")
|
||||
t.Setenv("RENEWCMS_CONTAINER", tc.value)
|
||||
settings, err := config.NewSettingsFromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if settings.Container != tc.flag {
|
||||
t.Fatalf("Container = %v, want %v", settings.Container, tc.flag)
|
||||
}
|
||||
h := newContainerHarness(t, settings.Container, false)
|
||||
|
||||
body := decodeUpdateBody(t, h.sendAs(t, http.MethodGet, "/update", updateOwnerId))
|
||||
|
||||
assertUpdateKeys(t, body)
|
||||
if body["container"] != tc.flag {
|
||||
t.Errorf("container = %v, want %v", body["container"], tc.flag)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Container mode disables apply even with a helper", func(t *testing.T) {
|
||||
h := newContainerHarness(t, true, true)
|
||||
h.store.result = newStoredUpdateResult()
|
||||
|
||||
for name, userId := range map[string]uint32{"owner": updateOwnerId, "admin": updateAdminId} {
|
||||
body := decodeUpdateBody(t, h.sendAs(t, http.MethodGet, "/update", userId))
|
||||
|
||||
if body["can_apply"] != false || body["container"] != true {
|
||||
t.Errorf("%s: body = %v", name, body)
|
||||
}
|
||||
if body["available"] != true || body["latest"] != "v2.3.0-beta" || body["notes"] == nil {
|
||||
t.Errorf("%s: body = %v", name, body)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Host installs are unchanged", func(t *testing.T) {
|
||||
h := newContainerHarness(t, false, true)
|
||||
h.store.result = newStoredUpdateResult()
|
||||
|
||||
body := decodeUpdateBody(t, h.sendAs(t, http.MethodGet, "/update", updateOwnerId))
|
||||
|
||||
if body["container"] != false || body["can_apply"] != true {
|
||||
t.Errorf("body = %v", body)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("The check still works in a container", func(t *testing.T) {
|
||||
h := newContainerHarness(t, true, true)
|
||||
h.store.found = false
|
||||
|
||||
rec := h.sendAs(t, http.MethodPost, "/update/check", updateOwnerId)
|
||||
|
||||
body := decodeUpdateBody(t, rec)
|
||||
if rec.Code != http.StatusOK || body["available"] != true || body["container"] != true {
|
||||
t.Fatalf("status = %d body = %v", rec.Code, body)
|
||||
}
|
||||
if h.store.saves != 1 || h.store.result.Latest != "v2.3.0-beta" {
|
||||
t.Errorf("saves = %d result = %+v", h.store.saves, h.store.result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Download and restart are refused in a container", func(t *testing.T) {
|
||||
for _, path := range []string{"/update/download", "/update/restart"} {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
h := newContainerHarness(t, true, true)
|
||||
h.store.result = newStoredUpdateResult()
|
||||
|
||||
rec := h.sendAs(t, http.MethodPost, path, updateOwnerId)
|
||||
|
||||
if rec.Code != http.StatusConflict {
|
||||
t.Fatalf("status = %d: %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
if _, found := h.jobs.Load(); found {
|
||||
t.Error("no job may start")
|
||||
}
|
||||
if len(h.fetcher.calls) != 0 {
|
||||
t.Errorf("fetcher calls = %v", h.fetcher.calls)
|
||||
}
|
||||
if h.requests.writes != 0 {
|
||||
t.Errorf("restart request writes = %d", h.requests.writes)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestContainerHelperDetectorTruthTable(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
container, inner, want bool
|
||||
}{{false, false, false}, {false, true, true}, {true, false, false}, {true, true, false}} {
|
||||
got := infraupdate.NewContainerHelperDetector(&fakeHelperDetector{installed: tc.inner}, tc.container).Installed()
|
||||
if got != tc.want {
|
||||
t.Errorf("container=%v inner=%v: Installed() = %v, want %v", tc.container, tc.inner, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerStatusUseCaseCanApplyTruthTable(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
isOwner, container, helper, want bool
|
||||
}{
|
||||
{true, false, true, true},
|
||||
{false, false, true, false},
|
||||
{true, true, true, false},
|
||||
{true, false, false, false},
|
||||
{false, true, true, false},
|
||||
} {
|
||||
rig := newDownloadRig(t)
|
||||
rig.helper.installed = tc.helper
|
||||
status := useCases.NewGetUpdateStatusUseCase(rig.store, rig.jobs, infraupdate.NewContainerHelperDetector(rig.helper, tc.container), rig.results, rig.requests, updateRunning, tc.container, func() time.Time { return updateTestCheckedAt }).GetStatus(tc.isOwner)
|
||||
if status.CanApply != tc.want || status.Container != tc.container {
|
||||
t.Errorf("%+v: CanApply=%v Container=%v", tc, status.CanApply, status.Container)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user