gitlink-cli/shortcuts/repo/batch_delete_test.go

221 lines
6.5 KiB
Go

package repo
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gitlink-org/gitlink-cli/internal/client"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
func runBatchDeleteShortcut(t *testing.T, server *httptest.Server, owner string, args map[string]string) error {
t.Helper()
s := findShortcut(t, "batch-delete")
ctx := &common.RuntimeContext{
Client: &client.Client{HTTP: server.Client(), BaseURL: server.URL},
Owner: owner,
Format: "json",
Args: args,
}
return s.Run(ctx)
}
func TestBatchDelete_DryRun(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("no API calls expected in dry-run mode, got %s %s", r.Method, r.URL.Path)
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "testuser", map[string]string{
"names": "repo1,repo2",
"dry-run": "true",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestBatchDelete_FromNames(t *testing.T) {
var deletedPaths []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "DELETE" {
t.Fatalf("expected DELETE, got %s", r.Method)
}
deletedPaths = append(deletedPaths, r.URL.Path)
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"ok":true}`))
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{
"names": "test-batch-1,test-batch-2,test-batch-3",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(deletedPaths) != 3 {
t.Fatalf("expected 3 DELETE calls, got %d", len(deletedPaths))
}
expected := []string{"/zzx-coder/test-batch-1.json", "/zzx-coder/test-batch-2.json", "/zzx-coder/test-batch-3.json"}
for i, p := range deletedPaths {
if p != expected[i] {
t.Fatalf("path[%d]: got %q, want %q", i, p, expected[i])
}
}
}
func TestBatchDelete_NoNames(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{})
if err == nil {
t.Fatal("expected error, got nil")
}
if !strings.Contains(err.Error(), "no repository names provided") {
t.Fatalf("error should mention no names, got: %v", err)
}
}
func TestBatchDelete_NoOwner(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "", map[string]string{
"names": "repo1,repo2",
})
if err == nil {
t.Fatal("expected error, got nil")
}
if !strings.Contains(err.Error(), "--owner is required") {
t.Fatalf("error should mention --owner required, got: %v", err)
}
}
func TestBatchDelete_DryRunNoNamesErrors(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{"dry-run": "true"})
if err == nil {
t.Fatal("expected error for no names even in dry-run, got nil")
}
}
func TestBatchDelete_FromCSV(t *testing.T) {
csvPath := writeTempCSV(t, "name\ncsv-repo1\ncsv-repo2\n")
var deletedPaths []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "DELETE" {
t.Fatalf("expected DELETE, got %s", r.Method)
}
deletedPaths = append(deletedPaths, r.URL.Path)
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"ok":true}`))
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{"from": csvPath})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(deletedPaths) != 2 {
t.Fatalf("expected 2 DELETE calls, got %d", len(deletedPaths))
}
if deletedPaths[0] != "/zzx-coder/csv-repo1.json" {
t.Fatalf("first path: got %q", deletedPaths[0])
}
if deletedPaths[1] != "/zzx-coder/csv-repo2.json" {
t.Fatalf("second path: got %q", deletedPaths[1])
}
}
func TestBatchDelete_CSVAndNamesCombined(t *testing.T) {
csvPath := writeTempCSV(t, "name\ncsv-repo\n")
var deletedPaths []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
deletedPaths = append(deletedPaths, r.URL.Path)
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"ok":true}`))
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{
"names": "inline-repo",
"from": csvPath,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(deletedPaths) != 2 {
t.Fatalf("expected 2 deletes (inline + csv), got %d", len(deletedPaths))
}
if deletedPaths[0] != "/zzx-coder/inline-repo.json" {
t.Fatalf("first: got %q", deletedPaths[0])
}
if deletedPaths[1] != "/zzx-coder/csv-repo.json" {
t.Fatalf("second: got %q", deletedPaths[1])
}
}
func TestBatchDelete_PartialFailure(t *testing.T) {
callCount := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
callCount++
if callCount == 2 {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(`{"message":"repo not found"}`))
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"ok":true}`))
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{
"names": "ok1,fail1,ok2",
})
if err == nil {
t.Fatal("expected error from partial failure, got nil")
}
if !strings.Contains(err.Error(), "failed to delete") {
t.Fatalf("error should mention failed count, got: %v", err)
}
}
func TestBatchDelete_TrimsWhitespace(t *testing.T) {
var deletedPaths []string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
deletedPaths = append(deletedPaths, r.URL.Path)
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"ok":true}`))
}))
defer server.Close()
err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{
"names": " repo-a , repo-b ,",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(deletedPaths) != 2 {
t.Fatalf("expected 2 deletes after trimming, got %d", len(deletedPaths))
}
if deletedPaths[0] != "/zzx-coder/repo-a.json" {
t.Fatalf("first: got %q", deletedPaths[0])
}
if deletedPaths[1] != "/zzx-coder/repo-b.json" {
t.Fatalf("second: got %q", deletedPaths[1])
}
}