diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 375807f..fee048f 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -1,6 +1,7 @@ package server import ( + "bytes" "context" "fmt" "io" @@ -235,6 +236,99 @@ func TestServerProbe(t *testing.T) { } } +func put(t *testing.T, path string, body []byte) (*http.Response, []byte) { + t.Helper() + rq, _ := http.NewRequest("PUT", testTS.URL+path, bytes.NewReader(body)) + resp, err := http.DefaultClient.Do(rq) + if err != nil { + t.Fatalf("PUT %s: %v", path, err) + } + defer resp.Body.Close() + b, _ := io.ReadAll(resp.Body) + return resp, b +} + +func TestServerLocalPyPI(t *testing.T) { + requireStack(t) + if resp, b := req(t, "POST", "/api/v2/remotes", `{"name":"srv-pypi","package_type":"pypi","repo_type":"local"}`); resp.StatusCode != 201 { + t.Fatalf("create pypi local: %d %s", resp.StatusCode, b) + } + defer req(t, "DELETE", "/api/v2/remotes/srv-pypi", "") + + if resp, b := put(t, "/api/v2/remotes/srv-pypi/files/foo-1.0-py3-none-any.whl", []byte("wheel bytes")); resp.StatusCode != 201 { + t.Fatalf("upload wheel: %d %s", resp.StatusCode, b) + } + // Re-uploading the same file is rejected. + if resp, _ := put(t, "/api/v2/remotes/srv-pypi/files/foo-1.0-py3-none-any.whl", []byte("again")); resp.StatusCode != 409 { + t.Errorf("expected 409 on overwrite, got %d", resp.StatusCode) + } + // Invalid pypi filename rejected. + if resp, _ := put(t, "/api/v2/remotes/srv-pypi/files/not-a-package.txt", []byte("x")); resp.StatusCode != 400 { + t.Errorf("expected 400 for bad filename, got %d", resp.StatusCode) + } + + if resp, b := req(t, "GET", "/api/v1/local/srv-pypi/simple/", ""); resp.StatusCode != 200 || !strings.Contains(string(b), "foo") { + t.Errorf("simple index: %d %s", resp.StatusCode, b) + } + if resp, b := req(t, "GET", "/api/v1/local/srv-pypi/simple/foo/", ""); resp.StatusCode != 200 || !strings.Contains(string(b), "foo-1.0-py3-none-any.whl") { + t.Errorf("package index: %d %s", resp.StatusCode, b) + } +} + +func TestServerLocalRPMRepodata(t *testing.T) { + requireStack(t) + rpm, err := os.ReadFile("../provider/rpm/testdata/e2e-testpkg-1.0-1.noarch.rpm") + if err != nil { + t.Skipf("rpm fixture unavailable: %v", err) + } + if resp, b := req(t, "POST", "/api/v2/remotes", `{"name":"srv-rpm","package_type":"rpm","repo_type":"local"}`); resp.StatusCode != 201 { + t.Fatalf("create rpm local: %d %s", resp.StatusCode, b) + } + defer req(t, "DELETE", "/api/v2/remotes/srv-rpm", "") + + if resp, b := put(t, "/api/v2/remotes/srv-rpm/files/e2e-testpkg-1.0-1.noarch.rpm", rpm); resp.StatusCode != 201 { + t.Fatalf("upload rpm: %d %s", resp.StatusCode, b) + } + + // repodata is generated asynchronously; poll for it. + var body []byte + for i := 0; i < 40; i++ { + var resp *http.Response + resp, body = req(t, "GET", "/api/v1/local/srv-rpm/repodata/repomd.xml", "") + if resp.StatusCode == 200 && strings.Contains(string(body), "= 400 { + t.Errorf("evict object: %d", resp.StatusCode) + } +} + +func TestServerValidationErrors(t *testing.T) { + requireStack(t) + if resp, _ := req(t, "POST", "/api/v2/remotes", `{"name":"bad","package_type":"bogus","base_url":"https://x"}`); resp.StatusCode != 400 { + t.Errorf("invalid package type: %d", resp.StatusCode) + } + if resp, _ := req(t, "POST", "/api/v2/remotes", `{"name":"bad","package_type":"generic","repo_type":"remote"}`); resp.StatusCode != 400 { + t.Errorf("missing base_url: %d", resp.StatusCode) + } + if resp, _ := req(t, "POST", "/api/v2/remotes", `not json`); resp.StatusCode != 400 { + t.Errorf("invalid json: %d", resp.StatusCode) + } +} + func TestServerNotFound(t *testing.T) { requireStack(t) if resp, _ := req(t, "GET", "/api/v2/remotes/does-not-exist", ""); resp.StatusCode != 404 {