355 lines
8.8 KiB
Go
355 lines
8.8 KiB
Go
package core_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
|
|
aptclone "github.com/syonad/clonepack/internal/clone/apt"
|
|
"github.com/syonad/clonepack/internal/core"
|
|
"github.com/syonad/clonepack/internal/store"
|
|
)
|
|
|
|
// mockRepoStore is an in-memory implementation of store.RepoStore for tests.
|
|
type mockRepoStore struct {
|
|
repos map[int64]*store.Repo
|
|
nextID int64
|
|
}
|
|
|
|
func newMockRepoStore() *mockRepoStore {
|
|
return &mockRepoStore{repos: make(map[int64]*store.Repo), nextID: 1}
|
|
}
|
|
|
|
func (m *mockRepoStore) CreateRepo(_ context.Context, r *store.Repo) (int64, error) {
|
|
for _, existing := range m.repos {
|
|
if existing.Name == r.Name {
|
|
return 0, errors.New("UNIQUE constraint failed: repos.name")
|
|
}
|
|
}
|
|
id := m.nextID
|
|
m.nextID++
|
|
clone := *r
|
|
clone.ID = id
|
|
m.repos[id] = &clone
|
|
return id, nil
|
|
}
|
|
|
|
func (m *mockRepoStore) GetRepo(_ context.Context, id int64) (*store.Repo, error) {
|
|
r, ok := m.repos[id]
|
|
if !ok {
|
|
return nil, store.ErrNotFound
|
|
}
|
|
clone := *r
|
|
return &clone, nil
|
|
}
|
|
|
|
func (m *mockRepoStore) GetRepoByName(_ context.Context, name string) (*store.Repo, error) {
|
|
for _, r := range m.repos {
|
|
if r.Name == name {
|
|
clone := *r
|
|
return &clone, nil
|
|
}
|
|
}
|
|
return nil, store.ErrNotFound
|
|
}
|
|
|
|
func (m *mockRepoStore) ListRepos(_ context.Context) ([]store.Repo, error) {
|
|
out := make([]store.Repo, 0, len(m.repos))
|
|
for _, r := range m.repos {
|
|
out = append(out, *r)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *mockRepoStore) DeleteRepo(_ context.Context, id int64) error {
|
|
if _, ok := m.repos[id]; !ok {
|
|
return store.ErrNotFound
|
|
}
|
|
delete(m.repos, id)
|
|
return nil
|
|
}
|
|
|
|
func (m *mockRepoStore) UpdateRepoSyncMode(_ context.Context, id int64, mode string) error {
|
|
r, ok := m.repos[id]
|
|
if !ok {
|
|
return store.ErrNotFound
|
|
}
|
|
r.SyncMode = mode
|
|
return nil
|
|
}
|
|
|
|
func (m *mockRepoStore) UpdateRepo(_ context.Context, id int64, sourceURL, syncMode, config string) error {
|
|
r, ok := m.repos[id]
|
|
if !ok {
|
|
return store.ErrNotFound
|
|
}
|
|
r.SourceURL = sourceURL
|
|
r.SyncMode = syncMode
|
|
r.Config = config
|
|
return nil
|
|
}
|
|
|
|
func newTestRepoService(t *testing.T) *core.RepoService {
|
|
t.Helper()
|
|
return core.NewRepoService(newMockRepoStore(), t.TempDir())
|
|
}
|
|
|
|
// --- Create tests ---
|
|
|
|
func TestCreate_validRPM(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
repo, err := svc.Create(context.Background(), core.CreateRepoInput{
|
|
Name: "rocky9",
|
|
Type: "rpm",
|
|
SourceURL: "https://download.rockylinux.org/pub/rocky/9/BaseOS/x86_64/os",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if repo.ID <= 0 {
|
|
t.Errorf("expected positive ID, got %d", repo.ID)
|
|
}
|
|
if repo.SyncMode != "auto" {
|
|
t.Errorf("default SyncMode: got %q, want auto", repo.SyncMode)
|
|
}
|
|
}
|
|
|
|
func TestCreate_validAPT(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
repo, err := svc.Create(context.Background(), core.CreateRepoInput{
|
|
Name: "debian-bookworm",
|
|
Type: "apt",
|
|
SourceURL: "https://deb.debian.org/debian",
|
|
AptSuite: "bookworm",
|
|
AptComponents: []string{"main", "contrib"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
// Config should be valid JSON with suite set.
|
|
var cfg aptclone.Config
|
|
if err := json.Unmarshal([]byte(repo.Config), &cfg); err != nil {
|
|
t.Fatalf("Config is not valid JSON: %v", err)
|
|
}
|
|
if cfg.Suite != "bookworm" {
|
|
t.Errorf("cfg.Suite: got %q, want bookworm", cfg.Suite)
|
|
}
|
|
if len(cfg.Components) != 2 {
|
|
t.Errorf("cfg.Components: got %v, want [main contrib]", cfg.Components)
|
|
}
|
|
}
|
|
|
|
func TestCreate_aptDefaultsComponents(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
repo, err := svc.Create(context.Background(), core.CreateRepoInput{
|
|
Name: "debian-minimal",
|
|
Type: "apt",
|
|
SourceURL: "https://deb.debian.org/debian",
|
|
AptSuite: "bookworm",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
var cfg aptclone.Config
|
|
json.Unmarshal([]byte(repo.Config), &cfg)
|
|
if len(cfg.Components) == 0 {
|
|
t.Error("expected default components, got empty")
|
|
}
|
|
if len(cfg.Architectures) == 0 {
|
|
t.Error("expected default architectures, got empty")
|
|
}
|
|
}
|
|
|
|
func TestCreate_missingName(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
_, err := svc.Create(context.Background(), core.CreateRepoInput{
|
|
Type: "rpm",
|
|
SourceURL: "https://example.com",
|
|
})
|
|
if err == nil {
|
|
t.Error("expected error for missing name, got nil")
|
|
}
|
|
}
|
|
|
|
func TestCreate_invalidType(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
_, err := svc.Create(context.Background(), core.CreateRepoInput{
|
|
Name: "bad-type",
|
|
Type: "maven",
|
|
SourceURL: "https://example.com",
|
|
})
|
|
if err == nil {
|
|
t.Error("expected error for invalid type, got nil")
|
|
}
|
|
}
|
|
|
|
func TestCreate_missingSourceURL(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
_, err := svc.Create(context.Background(), core.CreateRepoInput{
|
|
Name: "no-url",
|
|
Type: "rpm",
|
|
})
|
|
if err == nil {
|
|
t.Error("expected error for missing source_url, got nil")
|
|
}
|
|
}
|
|
|
|
func TestCreate_aptMissingSuite(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
_, err := svc.Create(context.Background(), core.CreateRepoInput{
|
|
Name: "apt-no-suite",
|
|
Type: "apt",
|
|
SourceURL: "https://deb.debian.org/debian",
|
|
})
|
|
if err == nil {
|
|
t.Error("expected error for missing apt_suite, got nil")
|
|
}
|
|
}
|
|
|
|
func TestCreate_invalidSyncMode(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
_, err := svc.Create(context.Background(), core.CreateRepoInput{
|
|
Name: "bad-mode",
|
|
Type: "rpm",
|
|
SourceURL: "https://example.com",
|
|
SyncMode: "cron",
|
|
})
|
|
if err == nil {
|
|
t.Error("expected error for invalid sync_mode, got nil")
|
|
}
|
|
}
|
|
|
|
// --- Update tests ---
|
|
|
|
func TestUpdate_changeSourceURL(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
ctx := context.Background()
|
|
|
|
repo, _ := svc.Create(ctx, core.CreateRepoInput{
|
|
Name: "rpm-repo",
|
|
Type: "rpm",
|
|
SourceURL: "https://old.example.com",
|
|
})
|
|
|
|
newURL := "https://new.example.com"
|
|
updated, err := svc.Update(ctx, repo.ID, core.UpdateRepoInput{SourceURL: &newURL})
|
|
if err != nil {
|
|
t.Fatalf("Update: %v", err)
|
|
}
|
|
if updated.SourceURL != newURL {
|
|
t.Errorf("SourceURL: got %q, want %q", updated.SourceURL, newURL)
|
|
}
|
|
}
|
|
|
|
func TestUpdate_changeSyncMode(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
ctx := context.Background()
|
|
|
|
repo, _ := svc.Create(ctx, core.CreateRepoInput{
|
|
Name: "rpm-repo-2",
|
|
Type: "rpm",
|
|
SourceURL: "https://example.com",
|
|
})
|
|
|
|
mode := "manual"
|
|
updated, err := svc.Update(ctx, repo.ID, core.UpdateRepoInput{SyncMode: &mode})
|
|
if err != nil {
|
|
t.Fatalf("Update: %v", err)
|
|
}
|
|
if updated.SyncMode != "manual" {
|
|
t.Errorf("SyncMode: got %q, want manual", updated.SyncMode)
|
|
}
|
|
}
|
|
|
|
func TestUpdate_aptComponents(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
ctx := context.Background()
|
|
|
|
repo, _ := svc.Create(ctx, core.CreateRepoInput{
|
|
Name: "apt-repo",
|
|
Type: "apt",
|
|
SourceURL: "https://deb.debian.org/debian",
|
|
AptSuite: "bookworm",
|
|
})
|
|
|
|
suite := "bookworm-backports"
|
|
updated, err := svc.Update(ctx, repo.ID, core.UpdateRepoInput{
|
|
AptSuite: &suite,
|
|
AptComponents: []string{"main", "contrib", "non-free"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Update: %v", err)
|
|
}
|
|
|
|
var cfg aptclone.Config
|
|
if err := json.Unmarshal([]byte(updated.Config), &cfg); err != nil {
|
|
t.Fatalf("Config JSON: %v", err)
|
|
}
|
|
if cfg.Suite != "bookworm-backports" {
|
|
t.Errorf("Suite: got %q, want bookworm-backports", cfg.Suite)
|
|
}
|
|
if len(cfg.Components) != 3 {
|
|
t.Errorf("Components: got %v", cfg.Components)
|
|
}
|
|
}
|
|
|
|
func TestUpdate_invalidSyncMode(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
ctx := context.Background()
|
|
|
|
repo, _ := svc.Create(ctx, core.CreateRepoInput{
|
|
Name: "rpm-repo-3",
|
|
Type: "rpm",
|
|
SourceURL: "https://example.com",
|
|
})
|
|
|
|
bad := "weekly"
|
|
_, err := svc.Update(ctx, repo.ID, core.UpdateRepoInput{SyncMode: &bad})
|
|
if err == nil {
|
|
t.Error("expected error for invalid sync_mode, got nil")
|
|
}
|
|
}
|
|
|
|
func TestUpdate_aptFieldsOnRPMRepo(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
ctx := context.Background()
|
|
|
|
repo, _ := svc.Create(ctx, core.CreateRepoInput{
|
|
Name: "rpm-repo-4",
|
|
Type: "rpm",
|
|
SourceURL: "https://example.com",
|
|
})
|
|
|
|
suite := "bookworm"
|
|
_, err := svc.Update(ctx, repo.ID, core.UpdateRepoInput{AptSuite: &suite})
|
|
if err == nil {
|
|
t.Error("expected error applying apt fields to rpm repo, got nil")
|
|
}
|
|
}
|
|
|
|
func TestUpdate_emptySourceURL(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
ctx := context.Background()
|
|
|
|
repo, _ := svc.Create(ctx, core.CreateRepoInput{
|
|
Name: "rpm-repo-5",
|
|
Type: "rpm",
|
|
SourceURL: "https://example.com",
|
|
})
|
|
|
|
empty := ""
|
|
_, err := svc.Update(ctx, repo.ID, core.UpdateRepoInput{SourceURL: &empty})
|
|
if err == nil {
|
|
t.Error("expected error for empty source_url, got nil")
|
|
}
|
|
}
|
|
|
|
func TestUpdate_notFound(t *testing.T) {
|
|
svc := newTestRepoService(t)
|
|
url := "https://example.com"
|
|
_, err := svc.Update(context.Background(), 9999, core.UpdateRepoInput{SourceURL: &url})
|
|
if !errors.Is(err, store.ErrNotFound) {
|
|
t.Errorf("expected ErrNotFound, got %v", err)
|
|
}
|
|
}
|