package httpapi import ( "bytes" "encoding/json" "io" "mime/multipart" "net/http" "net/http/httptest" "testing" "scout/internal/registry" ) func TestProjectRoutesKeepSamePackageNamesIsolated(t *testing.T) { root := t.TempDir() legacy, err := registry.New(root) if err != nil { t.Fatal(err) } projects, err := registry.NewProjectRegistry(root) if err != nil { t.Fatal(err) } if _, _, err := projects.Ensure("ssl", "SSL", "证书项目"); err != nil { t.Fatal(err) } if _, _, err := projects.Ensure("smail", "Smail", "邮件项目"); err != nil { t.Fatal(err) } server := httptest.NewServer((&Server{ Registry: legacy, Projects: projects, DeveloperToken: "test-token", AllowLegacyToken: true, }).Handler()) defer server.Close() uploadProjectPackage(t, server.URL, "ssl", "shared", "1.0.0", "ssl-content") uploadProjectPackage(t, server.URL, "smail", "shared", "1.0.0", "smail-content") assertProjectPackageContent(t, server.URL+"/api/v1/projects/ssl/packages/shared/1.0.0/download?platform=linux&architecture=amd64", "ssl-content") assertProjectPackageContent(t, server.URL+"/api/v1/projects/smail/packages/shared/1.0.0/download?platform=linux&architecture=amd64", "smail-content") response, err := http.Get(server.URL + "/api/v1/projects") if err != nil { t.Fatal(err) } defer response.Body.Close() var listed struct { Projects []registry.Project `json:"projects"` } if err := json.NewDecoder(response.Body).Decode(&listed); err != nil { t.Fatal(err) } if response.StatusCode != http.StatusOK || len(listed.Projects) != 3 { t.Fatalf("项目列表错误:%d %+v", response.StatusCode, listed.Projects) } } func uploadProjectPackage(t *testing.T, baseURL, project, name, version, content string) { t.Helper() body := &bytes.Buffer{} writer := multipart.NewWriter(body) for key, value := range map[string]string{ "name": name, "version": version, "platform": "linux", "architecture": "amd64", "status": "published", } { if err := writer.WriteField(key, value); err != nil { t.Fatal(err) } } file, err := writer.CreateFormFile("file", name+"-"+version+".pkg") if err != nil { t.Fatal(err) } if _, err := file.Write([]byte(content)); err != nil { t.Fatal(err) } if err := writer.Close(); err != nil { t.Fatal(err) } request, err := http.NewRequest(http.MethodPost, baseURL+"/api/v1/projects/"+project+"/packages/upload", body) if err != nil { t.Fatal(err) } request.Header.Set("Content-Type", writer.FormDataContentType()) request.Header.Set("Authorization", "Bearer test-token") response, err := http.DefaultClient.Do(request) if err != nil { t.Fatal(err) } defer response.Body.Close() if response.StatusCode != http.StatusCreated { data, _ := io.ReadAll(response.Body) t.Fatalf("项目上传失败:%d %s", response.StatusCode, data) } } func assertProjectPackageContent(t *testing.T, endpoint, expected string) { t.Helper() response, err := http.Get(endpoint) if err != nil { t.Fatal(err) } defer response.Body.Close() data, err := io.ReadAll(response.Body) if err != nil { t.Fatal(err) } if response.StatusCode != http.StatusOK || string(data) != expected { t.Fatalf("项目包内容错误:%d %q", response.StatusCode, data) } }