178 lines
6.3 KiB
Go
178 lines
6.3 KiB
Go
package test
|
|
|
|
import (
|
|
"RenewCMS/api/pkg/apperr"
|
|
domainPurge "RenewCMS/internal/domain/purge"
|
|
"RenewCMS/internal/domain/user"
|
|
"RenewCMS/internal/infrastructure/persistence/models"
|
|
persistence "RenewCMS/internal/infrastructure/persistence/repositories"
|
|
"net/http"
|
|
"os/exec"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestPurgeRoleGuardIsAnAllowlist(t *testing.T) {
|
|
for _, role := range []string{"OWNER", "Admin", "superuser"} {
|
|
for _, route := range []struct{ method, path string }{
|
|
{http.MethodGet, "/v1/media/purge-settings"},
|
|
{http.MethodPatch, "/v1/media/purge-settings"},
|
|
{http.MethodGet, "/v1/media/purge-preview"},
|
|
{http.MethodPost, "/v1/media/purge"},
|
|
} {
|
|
t.Run(role+" "+route.method+" "+route.path, func(t *testing.T) {
|
|
env := newPurgeEnv(t)
|
|
env.users.users[20] = user.User{ID: 20, Username: "other", IsVerified: true, Role: user.Role(role)}
|
|
|
|
rec := env.send(t, route.method, route.path, "", 20)
|
|
|
|
expectRaw(t, rec, 403, apperr.OwnerOrAdminRequiredErrorMessage+"\n")
|
|
if env.purgeSettings.getCalls != 0 || env.purgeSettings.saveCalls != 0 {
|
|
t.Errorf("expected no purge setting access")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPurgeLayering(t *testing.T) {
|
|
list := func(t *testing.T, format string, pkg string) []string {
|
|
t.Helper()
|
|
output, err := exec.Command("go", "list", "-tags", "nofrontend", "-f", format, pkg).CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("go list failed: %v: %s", err, output)
|
|
}
|
|
return strings.Fields(string(output))
|
|
}
|
|
|
|
t.Run("internal/domain/purge imports only the stdlib and other domain packages", func(t *testing.T) {
|
|
for _, imported := range list(t, `{{join .Imports "\n"}}`, "RenewCMS/internal/domain/purge") {
|
|
if strings.Contains(imported, ".") && !strings.HasPrefix(imported, "RenewCMS/internal/domain/") {
|
|
t.Errorf("unexpected import %s", imported)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("the use cases import no http, chi, gorm, persistence, mailer, models or api package", func(t *testing.T) {
|
|
for _, imported := range list(t, `{{join .Imports "\n"}}`, "RenewCMS/internal/infrastructure/useCases") {
|
|
for _, forbidden := range []string{"net/http", "go-chi", "gorm", "persistence", "mailer", "RenewCMS/api"} {
|
|
if strings.Contains(imported, forbidden) {
|
|
t.Errorf("useCases must not import %s", imported)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("the scheduler imports only the stdlib and domain packages", func(t *testing.T) {
|
|
imports := list(t, `{{join .Imports "\n"}}`, "RenewCMS/internal/infrastructure/scheduler")
|
|
if len(imports) == 0 {
|
|
t.Fatal("expected imports")
|
|
}
|
|
for _, imported := range imports {
|
|
if strings.Contains(imported, ".") && !strings.HasPrefix(imported, "RenewCMS/internal/domain/") {
|
|
t.Errorf("unexpected import %s", imported)
|
|
}
|
|
if strings.HasPrefix(imported, "RenewCMS/") && !strings.HasPrefix(imported, "RenewCMS/internal/domain/") {
|
|
t.Errorf("scheduler must not import %s", imported)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPurgeSettingsRepositoryRoundTrip(t *testing.T) {
|
|
newRepo := func(t *testing.T) *persistence.PurgeSettingsRepository {
|
|
t.Helper()
|
|
db := openInMemoryDb(t)
|
|
if err := db.AutoMigrate(&models.PurgeSettings{}); err != nil {
|
|
t.Fatalf("failed to migrate: %v", err)
|
|
}
|
|
return persistence.NewPurgeSettingsRepository(db)
|
|
}
|
|
|
|
t.Run("Nothing saved yields the defaults", func(t *testing.T) {
|
|
got, err := newRepo(t).Get()
|
|
|
|
if err != nil || !reflect.DeepEqual(got, domainPurge.DefaultSettings()) {
|
|
t.Errorf("expected defaults, got %+v, %v", got, err)
|
|
}
|
|
})
|
|
|
|
t.Run("Null and set timestamps survive a save", func(t *testing.T) {
|
|
repo := newRepo(t)
|
|
withNulls := domainPurge.Settings{AutoPurgeEnabled: true, AutoPurgeIntervalDays: 7}
|
|
if err := repo.Save(withNulls); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := repo.Get()
|
|
if err != nil || !reflect.DeepEqual(got, withNulls) {
|
|
t.Errorf("expected %+v, got %+v, %v", withNulls, got, err)
|
|
}
|
|
|
|
last := time.Date(2026, 9, 1, 9, 0, 0, 0, time.UTC)
|
|
enabled := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
|
full := domainPurge.Settings{AutoPurgeEnabled: true, AutoPurgeIntervalDays: 365, LastPurgeAt: &last, LastPurgeMerged: 3, LastPurgeArticlesUpdated: 1, EnabledAt: &enabled}
|
|
if err := repo.Save(full); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err = repo.Get()
|
|
if err != nil || got.LastPurgeAt == nil || !got.LastPurgeAt.Equal(last) || got.EnabledAt == nil || !got.EnabledAt.Equal(enabled) || got.LastPurgeMerged != 3 || got.LastPurgeArticlesUpdated != 1 || got.AutoPurgeIntervalDays != 365 {
|
|
t.Errorf("unexpected round trip %+v, %v", got, err)
|
|
}
|
|
})
|
|
|
|
t.Run("SaveLastRun writes only the last_purge_* columns", func(t *testing.T) {
|
|
repo := newRepo(t)
|
|
enabled := time.Date(2026, 10, 5, 9, 0, 0, 0, time.UTC)
|
|
if err := repo.Save(domainPurge.Settings{AutoPurgeEnabled: true, AutoPurgeIntervalDays: 9, EnabledAt: &enabled}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
at := time.Date(2026, 10, 6, 9, 0, 0, 0, time.UTC)
|
|
|
|
if err := repo.SaveLastRun(at, 4, 2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := repo.Get()
|
|
if err != nil || !got.AutoPurgeEnabled || got.AutoPurgeIntervalDays != 9 || got.EnabledAt == nil || !got.EnabledAt.Equal(enabled) || got.LastPurgeAt == nil || !got.LastPurgeAt.Equal(at) || got.LastPurgeMerged != 4 || got.LastPurgeArticlesUpdated != 2 {
|
|
t.Errorf("unexpected settings %+v, %v", got, err)
|
|
}
|
|
})
|
|
|
|
t.Run("SaveLastRun with no saved row keeps the defaults", func(t *testing.T) {
|
|
repo := newRepo(t)
|
|
at := time.Date(2026, 10, 6, 9, 0, 0, 0, time.UTC)
|
|
|
|
if err := repo.SaveLastRun(at, 0, 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := repo.Get()
|
|
defaults := domainPurge.DefaultSettings()
|
|
if err != nil || got.AutoPurgeEnabled != defaults.AutoPurgeEnabled || got.AutoPurgeIntervalDays != defaults.AutoPurgeIntervalDays || got.LastPurgeAt == nil || !got.LastPurgeAt.Equal(at) {
|
|
t.Errorf("unexpected settings %+v, %v", got, err)
|
|
}
|
|
})
|
|
|
|
t.Run("Repeated saves keep a single row", func(t *testing.T) {
|
|
db := openInMemoryDb(t)
|
|
if err := db.AutoMigrate(&models.PurgeSettings{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
repo := persistence.NewPurgeSettingsRepository(db)
|
|
for days := 1; days <= 3; days++ {
|
|
if err := repo.Save(domainPurge.Settings{AutoPurgeIntervalDays: days}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
var count int64
|
|
db.Model(&models.PurgeSettings{}).Count(&count)
|
|
|
|
if count != 1 {
|
|
t.Errorf("expected a single row, got %d", count)
|
|
}
|
|
})
|
|
}
|