add test for the wall app
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
parent
c4776d81dd
commit
9e287e4e6a
15 changed files with 3022 additions and 1 deletions
354
internal/api/handler_test.go
Normal file
354
internal/api/handler_test.go
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/syonad/clonepack/config"
|
||||
"github.com/syonad/clonepack/internal/api"
|
||||
"github.com/syonad/clonepack/internal/core"
|
||||
"github.com/syonad/clonepack/internal/store"
|
||||
)
|
||||
|
||||
// testServer wires up a full in-memory stack and returns an httptest.Server.
|
||||
func newTestServer(t *testing.T) (*httptest.Server, *sql.DB) {
|
||||
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)
|
||||
jobStore := store.NewCloneJobStore(db)
|
||||
pendingStore := store.NewPendingPackageStore(db)
|
||||
blockedStore := store.NewBlockedPackageStore(db)
|
||||
snapshotStore := store.NewSnapshotStore(db)
|
||||
|
||||
repoSvc := core.NewRepoService(repoStore, dataDir)
|
||||
cloneSvc := core.NewCloneService(repoStore, jobStore, dataDir)
|
||||
snapshotSvc := core.NewSnapshotService(snapshotStore, repoStore, dataDir)
|
||||
syncSvc := core.NewSyncService(repoStore, pendingStore, blockedStore, cloneSvc, snapshotSvc, dataDir)
|
||||
|
||||
router := api.NewRouter(
|
||||
api.NewRepoHandler(repoSvc),
|
||||
api.NewCloneHandler(cloneSvc),
|
||||
api.NewSyncHandler(syncSvc),
|
||||
api.NewSnapshotHandler(snapshotSvc),
|
||||
api.NewProxyHandler(repoSvc, dataDir),
|
||||
)
|
||||
|
||||
srv := httptest.NewServer(router)
|
||||
t.Cleanup(srv.Close)
|
||||
return srv, db
|
||||
}
|
||||
|
||||
func do(t *testing.T, method, url string, body any) *http.Response {
|
||||
t.Helper()
|
||||
var buf bytes.Buffer
|
||||
if body != nil {
|
||||
json.NewEncoder(&buf).Encode(body)
|
||||
}
|
||||
req, err := http.NewRequest(method, url, &buf)
|
||||
if err != nil {
|
||||
t.Fatalf("new request: %v", err)
|
||||
}
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("%s %s: %v", method, url, err)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func decodeJSON(t *testing.T, resp *http.Response, v any) {
|
||||
t.Helper()
|
||||
defer resp.Body.Close()
|
||||
if err := json.NewDecoder(resp.Body).Decode(v); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Health ---
|
||||
|
||||
func TestHealth(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
resp := do(t, http.MethodGet, srv.URL+"/health", nil)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("health: got %d, want 200", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Create repo ---
|
||||
|
||||
func TestCreateRepo_rpm(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
|
||||
payload := map[string]any{
|
||||
"name": "rocky9",
|
||||
"type": "rpm",
|
||||
"source_url": "https://download.rockylinux.org/pub/rocky/9/BaseOS/x86_64/os",
|
||||
}
|
||||
resp := do(t, http.MethodPost, srv.URL+"/api/v1/repos", payload)
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("create rpm: got %d, want 201", resp.StatusCode)
|
||||
}
|
||||
|
||||
var repo api.RepoResponse
|
||||
decodeJSON(t, resp, &repo)
|
||||
if repo.ID <= 0 {
|
||||
t.Errorf("expected positive ID, got %d", repo.ID)
|
||||
}
|
||||
if repo.Name != "rocky9" {
|
||||
t.Errorf("Name: got %q, want rocky9", repo.Name)
|
||||
}
|
||||
if repo.SyncMode != "auto" {
|
||||
t.Errorf("SyncMode default: got %q, want auto", repo.SyncMode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRepo_apt(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
|
||||
payload := map[string]any{
|
||||
"name": "debian-bookworm",
|
||||
"type": "apt",
|
||||
"source_url": "https://deb.debian.org/debian",
|
||||
"apt_suite": "bookworm",
|
||||
"apt_components": []string{"main", "contrib"},
|
||||
"apt_architectures": []string{"amd64"},
|
||||
}
|
||||
resp := do(t, http.MethodPost, srv.URL+"/api/v1/repos", payload)
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("create apt: got %d, want 201", resp.StatusCode)
|
||||
}
|
||||
|
||||
var repo api.RepoResponse
|
||||
decodeJSON(t, resp, &repo)
|
||||
if repo.AptSuite != "bookworm" {
|
||||
t.Errorf("AptSuite: got %q, want bookworm", repo.AptSuite)
|
||||
}
|
||||
if len(repo.AptComponents) != 2 {
|
||||
t.Errorf("AptComponents: got %v", repo.AptComponents)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRepo_invalidType(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
resp := do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "bad", "type": "maven", "source_url": "https://x.com",
|
||||
})
|
||||
if resp.StatusCode != http.StatusUnprocessableEntity {
|
||||
t.Errorf("invalid type: got %d, want 422", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
func TestCreateRepo_aptMissingSuite(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
resp := do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "apt-no-suite", "type": "apt", "source_url": "https://deb.debian.org/debian",
|
||||
})
|
||||
if resp.StatusCode != http.StatusUnprocessableEntity {
|
||||
t.Errorf("missing suite: got %d, want 422", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
func TestCreateRepo_invalidJSON(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
req, _ := http.NewRequest(http.MethodPost, srv.URL+"/api/v1/repos", bytes.NewBufferString("not-json"))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, _ := http.DefaultClient.Do(req)
|
||||
if resp.StatusCode != http.StatusBadRequest {
|
||||
t.Errorf("bad json: got %d, want 400", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
// --- List repos ---
|
||||
|
||||
func TestListRepos_empty(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
resp := do(t, http.MethodGet, srv.URL+"/api/v1/repos", nil)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("list: got %d, want 200", resp.StatusCode)
|
||||
}
|
||||
var body api.ListReposResponse
|
||||
decodeJSON(t, resp, &body)
|
||||
if body.Total != 0 {
|
||||
t.Errorf("expected 0 repos, got %d", body.Total)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRepos_afterCreate(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
|
||||
do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "repo-a", "type": "rpm", "source_url": "https://a.com",
|
||||
}).Body.Close()
|
||||
do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "repo-b", "type": "rpm", "source_url": "https://b.com",
|
||||
}).Body.Close()
|
||||
|
||||
resp := do(t, http.MethodGet, srv.URL+"/api/v1/repos", nil)
|
||||
var body api.ListReposResponse
|
||||
decodeJSON(t, resp, &body)
|
||||
if body.Total != 2 {
|
||||
t.Errorf("expected 2 repos, got %d", body.Total)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Get repo ---
|
||||
|
||||
func TestGetRepo(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
|
||||
createResp := do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "rocky9", "type": "rpm", "source_url": "https://example.com",
|
||||
})
|
||||
var created api.RepoResponse
|
||||
decodeJSON(t, createResp, &created)
|
||||
|
||||
resp := do(t, http.MethodGet, srv.URL+"/api/v1/repos/1", nil)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("get: got %d, want 200", resp.StatusCode)
|
||||
}
|
||||
var repo api.RepoResponse
|
||||
decodeJSON(t, resp, &repo)
|
||||
if repo.ID != created.ID {
|
||||
t.Errorf("ID: got %d, want %d", repo.ID, created.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRepo_notFound(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
resp := do(t, http.MethodGet, srv.URL+"/api/v1/repos/999", nil)
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("not found: got %d, want 404", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
// --- Update repo ---
|
||||
|
||||
func TestUpdateRepo_sourceURL(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
|
||||
do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "rocky9", "type": "rpm", "source_url": "https://old.example.com",
|
||||
}).Body.Close()
|
||||
|
||||
resp := do(t, http.MethodPatch, srv.URL+"/api/v1/repos/1", map[string]any{
|
||||
"source_url": "https://new.example.com",
|
||||
})
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("update: got %d, want 200", resp.StatusCode)
|
||||
}
|
||||
var repo api.RepoResponse
|
||||
decodeJSON(t, resp, &repo)
|
||||
if repo.SourceURL != "https://new.example.com" {
|
||||
t.Errorf("SourceURL: got %q", repo.SourceURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRepo_aptConfig(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
|
||||
do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "debian", "type": "apt", "source_url": "https://deb.debian.org/debian",
|
||||
"apt_suite": "bookworm",
|
||||
}).Body.Close()
|
||||
|
||||
resp := do(t, http.MethodPatch, srv.URL+"/api/v1/repos/1", map[string]any{
|
||||
"apt_components": []string{"main", "contrib", "non-free"},
|
||||
})
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("update apt: got %d, want 200", resp.StatusCode)
|
||||
}
|
||||
var repo api.RepoResponse
|
||||
decodeJSON(t, resp, &repo)
|
||||
if len(repo.AptComponents) != 3 {
|
||||
t.Errorf("AptComponents: got %v", repo.AptComponents)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRepo_notFound(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
resp := do(t, http.MethodPatch, srv.URL+"/api/v1/repos/999", map[string]any{
|
||||
"source_url": "https://x.com",
|
||||
})
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("not found: got %d, want 404", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
// --- Delete repo ---
|
||||
|
||||
func TestDeleteRepo(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
|
||||
do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "to-delete", "type": "rpm", "source_url": "https://example.com",
|
||||
}).Body.Close()
|
||||
|
||||
resp := do(t, http.MethodDelete, srv.URL+"/api/v1/repos/1", nil)
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
t.Fatalf("delete: got %d, want 204", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
resp = do(t, http.MethodGet, srv.URL+"/api/v1/repos/1", nil)
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("after delete: got %d, want 404", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
func TestDeleteRepo_notFound(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
resp := do(t, http.MethodDelete, srv.URL+"/api/v1/repos/999", nil)
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("not found: got %d, want 404", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
// --- Sync ---
|
||||
|
||||
func TestSyncTrigger_notFound(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
resp := do(t, http.MethodPost, srv.URL+"/api/v1/repos/999/sync/trigger", nil)
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("sync trigger not found: got %d, want 404", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
func TestSyncListPending_empty(t *testing.T) {
|
||||
srv, _ := newTestServer(t)
|
||||
do(t, http.MethodPost, srv.URL+"/api/v1/repos", map[string]any{
|
||||
"name": "rpm-repo", "type": "rpm", "source_url": "https://example.com",
|
||||
}).Body.Close()
|
||||
|
||||
resp := do(t, http.MethodGet, srv.URL+"/api/v1/repos/1/sync/pending", nil)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("sync pending: got %d, want 200", resp.StatusCode)
|
||||
}
|
||||
var body struct {
|
||||
Total int `json:"total"`
|
||||
}
|
||||
decodeJSON(t, resp, &body)
|
||||
if body.Total != 0 {
|
||||
t.Errorf("expected empty pending list, got total=%d", body.Total)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue