268 lines
8.3 KiB
Go
268 lines
8.3 KiB
Go
package core_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/syonad/clonepack/config"
|
|
"github.com/syonad/clonepack/internal/core"
|
|
"github.com/syonad/clonepack/internal/store"
|
|
)
|
|
|
|
// testEnv regroupe tous les stores + le SyncService câblé sur SQLite in-memory.
|
|
type testEnv struct {
|
|
db *sql.DB
|
|
repoStore *store.SQLiteRepoStore
|
|
pendingStore *store.SQLitePendingPackageStore
|
|
blockedStore *store.SQLiteBlockedPackageStore
|
|
snapshotStore *store.SQLiteSnapshotStore
|
|
cloneJobStore *store.SQLiteCloneJobStore
|
|
syncSvc *core.SyncService
|
|
snapshotSvc *core.SnapshotService
|
|
dataDir string
|
|
}
|
|
|
|
func newTestEnv(t *testing.T) *testEnv {
|
|
t.Helper()
|
|
db, err := store.Open(config.DBConfig{Path: ":memory:"})
|
|
if err != nil {
|
|
t.Fatalf("open db: %v", err)
|
|
}
|
|
t.Cleanup(func() { db.Close() })
|
|
|
|
dataDir := t.TempDir()
|
|
|
|
repoStore := store.NewRepoStore(db)
|
|
pendingStore := store.NewPendingPackageStore(db)
|
|
blockedStore := store.NewBlockedPackageStore(db)
|
|
snapshotStore := store.NewSnapshotStore(db)
|
|
cloneJobStore := store.NewCloneJobStore(db)
|
|
|
|
cloneSvc := core.NewCloneService(repoStore, cloneJobStore, dataDir)
|
|
snapshotSvc := core.NewSnapshotService(snapshotStore, repoStore, dataDir)
|
|
syncSvc := core.NewSyncService(repoStore, pendingStore, blockedStore, cloneSvc, snapshotSvc, dataDir)
|
|
|
|
return &testEnv{
|
|
db: db,
|
|
repoStore: repoStore,
|
|
pendingStore: pendingStore,
|
|
blockedStore: blockedStore,
|
|
snapshotStore: snapshotStore,
|
|
cloneJobStore: cloneJobStore,
|
|
syncSvc: syncSvc,
|
|
snapshotSvc: snapshotSvc,
|
|
dataDir: dataDir,
|
|
}
|
|
}
|
|
|
|
func (e *testEnv) createRepo(t *testing.T, name, repoType string) int64 {
|
|
t.Helper()
|
|
id, err := e.repoStore.CreateRepo(context.Background(), &store.Repo{
|
|
Name: name, Type: repoType, SourceURL: "https://example.com", SyncMode: "manual",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("createRepo %q: %v", name, err)
|
|
}
|
|
return id
|
|
}
|
|
|
|
func (e *testEnv) seedPending(t *testing.T, repoID int64, names ...string) []store.PendingPackage {
|
|
t.Helper()
|
|
pkgs := make([]store.PendingPackage, len(names))
|
|
for i, name := range names {
|
|
pkgs[i] = store.PendingPackage{
|
|
RepoID: repoID, Name: name, Version: "1.0", Arch: "amd64",
|
|
Location: "Packages/" + name + ".rpm", Checksum: "abc", ChecksumType: "sha256", Size: 100,
|
|
}
|
|
}
|
|
if err := e.pendingStore.UpsertPending(context.Background(), pkgs); err != nil {
|
|
t.Fatalf("seedPending: %v", err)
|
|
}
|
|
listed, _ := e.pendingStore.ListPending(context.Background(), repoID)
|
|
return listed
|
|
}
|
|
|
|
// --- ListPending ---
|
|
|
|
func TestSyncListPending(t *testing.T) {
|
|
env := newTestEnv(t)
|
|
repoID := env.createRepo(t, "rpm-repo", "rpm")
|
|
env.seedPending(t, repoID, "curl", "wget")
|
|
|
|
pkgs, err := env.syncSvc.ListPending(context.Background(), repoID)
|
|
if err != nil {
|
|
t.Fatalf("ListPending: %v", err)
|
|
}
|
|
if len(pkgs) != 2 {
|
|
t.Errorf("expected 2 pending, got %d", len(pkgs))
|
|
}
|
|
}
|
|
|
|
func TestSyncListPending_repoNotFound(t *testing.T) {
|
|
env := newTestEnv(t)
|
|
_, err := env.syncSvc.ListPending(context.Background(), 9999)
|
|
if err == nil {
|
|
t.Error("expected error for unknown repo")
|
|
}
|
|
}
|
|
|
|
// --- RejectPending ---
|
|
|
|
func TestRejectPending(t *testing.T) {
|
|
env := newTestEnv(t)
|
|
repoID := env.createRepo(t, "rpm-repo", "rpm")
|
|
listed := env.seedPending(t, repoID, "curl", "wget", "vim")
|
|
|
|
ids := []int64{listed[0].ID, listed[1].ID}
|
|
if err := env.syncSvc.RejectPending(context.Background(), repoID, ids); err != nil {
|
|
t.Fatalf("RejectPending: %v", err)
|
|
}
|
|
|
|
remaining, _ := env.pendingStore.ListPending(context.Background(), repoID)
|
|
if len(remaining) != 1 {
|
|
t.Fatalf("expected 1 remaining, got %d", len(remaining))
|
|
}
|
|
if remaining[0].Name != "vim" {
|
|
t.Errorf("expected vim remaining, got %q", remaining[0].Name)
|
|
}
|
|
}
|
|
|
|
// --- BlockPackages ---
|
|
|
|
func TestBlockPackages_removesFromPendingAndAddsToBlocked(t *testing.T) {
|
|
env := newTestEnv(t)
|
|
repoID := env.createRepo(t, "rpm-repo", "rpm")
|
|
listed := env.seedPending(t, repoID, "curl", "wget", "vim")
|
|
|
|
// Block curl and wget (by pending ID).
|
|
ids := []int64{listed[0].ID, listed[1].ID}
|
|
if err := env.syncSvc.BlockPackages(context.Background(), repoID, ids); err != nil {
|
|
t.Fatalf("BlockPackages: %v", err)
|
|
}
|
|
|
|
// Pending should only have vim left.
|
|
pending, _ := env.pendingStore.ListPending(context.Background(), repoID)
|
|
if len(pending) != 1 || pending[0].Name != "vim" {
|
|
t.Errorf("pending after block: expected [vim], got %v", pending)
|
|
}
|
|
|
|
// Blocked should have curl and wget.
|
|
blocked, _ := env.blockedStore.ListBlocked(context.Background(), repoID)
|
|
if len(blocked) != 2 {
|
|
t.Fatalf("blocked: expected 2, got %d", len(blocked))
|
|
}
|
|
names := map[string]bool{blocked[0].Name: true, blocked[1].Name: true}
|
|
if !names["curl"] || !names["wget"] {
|
|
t.Errorf("blocked names: got %v, want curl and wget", names)
|
|
}
|
|
}
|
|
|
|
// --- UnblockPackages ---
|
|
|
|
func TestUnblockPackages(t *testing.T) {
|
|
env := newTestEnv(t)
|
|
repoID := env.createRepo(t, "rpm-repo", "rpm")
|
|
listed := env.seedPending(t, repoID, "curl", "wget")
|
|
|
|
env.syncSvc.BlockPackages(context.Background(), repoID, []int64{listed[0].ID, listed[1].ID})
|
|
|
|
blocked, _ := env.blockedStore.ListBlocked(context.Background(), repoID)
|
|
if len(blocked) != 2 {
|
|
t.Fatalf("setup: expected 2 blocked, got %d", len(blocked))
|
|
}
|
|
|
|
if err := env.syncSvc.UnblockPackages(context.Background(), repoID, []int64{blocked[0].ID}); err != nil {
|
|
t.Fatalf("UnblockPackages: %v", err)
|
|
}
|
|
|
|
remaining, _ := env.blockedStore.ListBlocked(context.Background(), repoID)
|
|
if len(remaining) != 1 {
|
|
t.Fatalf("expected 1 blocked remaining, got %d", len(remaining))
|
|
}
|
|
}
|
|
|
|
// --- ApprovePending ---
|
|
|
|
func TestApprovePending_downloadsAndRemovesFromPending(t *testing.T) {
|
|
// Serve a fake package file.
|
|
const fileContent = "fake-rpm-content"
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte(fileContent))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
env := newTestEnv(t)
|
|
ctx := context.Background()
|
|
|
|
// Repo with source URL pointing to our test server.
|
|
repoID, _ := env.repoStore.CreateRepo(ctx, &store.Repo{
|
|
Name: "rpm-repo", Type: "rpm", SourceURL: srv.URL, SyncMode: "manual",
|
|
})
|
|
|
|
// Create the local repo dir so DownloadAndVerify can write.
|
|
localDir := filepath.Join(env.dataDir, "repos", "1", "rpm")
|
|
os.MkdirAll(localDir, 0o755)
|
|
|
|
// Seed one pending package. Checksum left empty so verify is skipped.
|
|
pkg := store.PendingPackage{
|
|
RepoID: repoID, Name: "curl", Version: "7.88", Arch: "amd64",
|
|
Location: "Packages/curl.rpm", Checksum: "", ChecksumType: "sha256", Size: int64(len(fileContent)),
|
|
}
|
|
env.pendingStore.UpsertPending(ctx, []store.PendingPackage{pkg})
|
|
listed, _ := env.pendingStore.ListPending(ctx, repoID)
|
|
|
|
if err := env.syncSvc.ApprovePending(ctx, repoID, []int64{listed[0].ID}); err != nil {
|
|
t.Fatalf("ApprovePending: %v", err)
|
|
}
|
|
|
|
// Package must be removed from pending.
|
|
remaining, _ := env.pendingStore.ListPending(ctx, repoID)
|
|
if len(remaining) != 0 {
|
|
t.Errorf("expected empty pending after approve, got %d", len(remaining))
|
|
}
|
|
|
|
// File must exist on disk.
|
|
dest := filepath.Join(localDir, "Packages", "curl.rpm")
|
|
if _, err := os.Stat(dest); err != nil {
|
|
t.Errorf("expected file on disk at %s: %v", dest, err)
|
|
}
|
|
}
|
|
|
|
func TestApprovePending_continuesOnDownloadError(t *testing.T) {
|
|
// Server returns 404 for everything.
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.NotFound(w, r)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
env := newTestEnv(t)
|
|
ctx := context.Background()
|
|
|
|
repoID, _ := env.repoStore.CreateRepo(ctx, &store.Repo{
|
|
Name: "rpm-repo", Type: "rpm", SourceURL: srv.URL, SyncMode: "manual",
|
|
})
|
|
|
|
env.pendingStore.UpsertPending(ctx, []store.PendingPackage{
|
|
{RepoID: repoID, Name: "curl", Version: "1.0", Arch: "amd64", Location: "pkg/curl.rpm", ChecksumType: "sha256"},
|
|
{RepoID: repoID, Name: "wget", Version: "1.0", Arch: "amd64", Location: "pkg/wget.rpm", ChecksumType: "sha256"},
|
|
})
|
|
listed, _ := env.pendingStore.ListPending(ctx, repoID)
|
|
ids := []int64{listed[0].ID, listed[1].ID}
|
|
|
|
// Should return errors but not panic.
|
|
err := env.syncSvc.ApprovePending(ctx, repoID, ids)
|
|
if err == nil {
|
|
t.Error("expected errors for 404 downloads, got nil")
|
|
}
|
|
|
|
// Nothing approved — pending should remain.
|
|
remaining, _ := env.pendingStore.ListPending(ctx, repoID)
|
|
if len(remaining) != 2 {
|
|
t.Errorf("expected 2 still pending, got %d", len(remaining))
|
|
}
|
|
}
|