//go:build dockere2e package e2edocker import ( "archive/tar" "bytes" "compress/gzip" "io" "net/http" "strings" "testing" "time" "git.unkin.net/unkin/artifactapi/internal/testsupport" ) // uploadFile PUTs with Basic credentials, as `curl -u ci:$TOKEN` does. func uploadFile(t *testing.T, repo, filePath string, body []byte, contentType string) { t.Helper() url := api("/api/v2/remotes/" + repo + "/files/" + filePath) resp, respBody := doRequestAuth(t, http.MethodPut, url, body, contentType, basicAuth(authToken)) if resp.StatusCode != http.StatusCreated { t.Fatalf("upload %s: status %d: %s", filePath, resp.StatusCode, respBody) } } // TestLocalGenericUpload uploads a generic file and downloads it back. func TestLocalGenericUpload(t *testing.T) { createRepo(t, `{"name":"local-generic","package_type":"generic","repo_type":"local"}`) defer deleteRepo(t, "local-generic") content := []byte("artifactapi local generic upload payload") uploadFile(t, "local-generic", "data/hello.bin", content, "application/octet-stream") assertWriteGated(t, http.MethodPut, api("/api/v2/remotes/local-generic/files/data/other.bin"), content, "application/octet-stream", api("/api/v1/local/local-generic/data/hello.bin")) resp, body := doRequest(t, http.MethodGet, api("/api/v1/local/local-generic/data/hello.bin"), nil, "") if resp.StatusCode != http.StatusOK { t.Fatalf("download: status %d: %s", resp.StatusCode, body) } if !bytes.Equal(body, content) { t.Fatalf("downloaded content mismatch") } } // TestLocalTerraformUpload uploads a provider zip the way a release pipeline // does and downloads it back anonymously. func TestLocalTerraformUpload(t *testing.T) { createRepo(t, `{"name":"local-terraform","package_type":"terraform","repo_type":"local"}`) defer deleteRepo(t, "local-terraform") const path = "unkin/e2e/terraform-provider-e2e_1.0.0_linux_amd64.zip" zip := []byte("PK\x05\x06" + strings.Repeat("\x00", 18)) uploadFile(t, "local-terraform", path, zip, "application/zip") assertWriteGated(t, http.MethodPut, api("/api/v2/remotes/local-terraform/files/unkin/e2e/terraform-provider-e2e_1.1.0_linux_amd64.zip"), zip, "application/zip", api("/api/v2/remotes/local-terraform/files/"+path)) } // TestLocalPyPIUpload uploads a wheel and validates the generated simple index. func TestLocalPyPIUpload(t *testing.T) { createRepo(t, `{"name":"local-pypi","package_type":"pypi","repo_type":"local"}`) defer deleteRepo(t, "local-pypi") wheel := fixtureBytes(t, "packages/foo-1.0-py3-none-any.whl") uploadFile(t, "local-pypi", "foo-1.0-py3-none-any.whl", wheel, "application/zip") assertWriteGated(t, http.MethodPut, api("/api/v2/remotes/local-pypi/files/foo-2.0-py3-none-any.whl"), wheel, "application/zip", api("/api/v1/local/local-pypi/simple/foo/")) // Root index lists the package. resp, body := doRequest(t, http.MethodGet, api("/api/v1/local/local-pypi/simple/"), nil, "") if resp.StatusCode != http.StatusOK { t.Fatalf("simple index: status %d: %s", resp.StatusCode, body) } if !strings.Contains(string(body), "foo") { t.Fatalf("simple index missing package 'foo': %s", body) } // Per-package index lists the wheel file. resp, body = doRequest(t, http.MethodGet, api("/api/v1/local/local-pypi/simple/foo/"), nil, "") if resp.StatusCode != http.StatusOK { t.Fatalf("package index: status %d: %s", resp.StatusCode, body) } if !strings.Contains(string(body), "foo-1.0-py3-none-any.whl") { t.Fatalf("package index missing wheel: %s", body) } // The wheel downloads back byte-identical. resp, body = doRequest(t, http.MethodGet, api("/api/v1/local/local-pypi/foo/foo-1.0-py3-none-any.whl"), nil, "") if resp.StatusCode != http.StatusOK { t.Fatalf("download wheel: status %d: %s", resp.StatusCode, body) } if !bytes.Equal(body, wheel) { t.Fatalf("wheel content mismatch") } } // TestLocalRPMRepodata uploads a real RPM and validates that repodata is // generated automatically (the special rpm-local feature). func TestLocalRPMRepodata(t *testing.T) { createRepo(t, `{"name":"local-rpm","package_type":"rpm","repo_type":"local"}`) defer deleteRepo(t, "local-rpm") rpm := fixtureBytes(t, "rpmrepo/Packages/e2e-testpkg-1.0-1.noarch.rpm") uploadFile(t, "local-rpm", "e2e-testpkg-1.0-1.noarch.rpm", rpm, "application/x-rpm") // repodata is generated asynchronously after upload; poll for it. resp, body := getEventually(t, api("/api/v1/local/local-rpm/repodata/repomd.xml"), 15*time.Second) if resp.StatusCode != http.StatusOK { t.Fatalf("repomd.xml: status %d: %s", resp.StatusCode, body) } s := string(body) if !strings.Contains(s, "