test: probe branches, local upload errors, upstream errors, rpm/gc error paths
This commit is contained in:
@@ -94,3 +94,21 @@ func TestSweepNoOrphans(t *testing.T) {
|
|||||||
// A sweep with nothing to collect should be a clean no-op.
|
// A sweep with nothing to collect should be a clean no-op.
|
||||||
New(testDB, testStore, time.Hour).sweep(context.Background())
|
New(testDB, testStore, time.Hour).sweep(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunStopsOnContextCancel(t *testing.T) {
|
||||||
|
if testDB == nil {
|
||||||
|
t.Skip("Docker unavailable")
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
New(testDB, testStore, time.Hour).Run(ctx)
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
cancel()
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("Run did not return after context cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -105,6 +105,27 @@ func TestRPMAfterUpload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type errBlobReader struct{}
|
||||||
|
|
||||||
|
func (errBlobReader) Download(_ context.Context, _ string) (io.ReadCloser, int64, error) {
|
||||||
|
return nil, 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRPMAfterUploadErrors(t *testing.T) {
|
||||||
|
// Download failure: no metadata inserted, no panic.
|
||||||
|
store := &fakeMetaStore{}
|
||||||
|
(&Provider{}).AfterUpload(context.Background(), "r", "p", "sha256:x", errBlobReader{}, store)
|
||||||
|
if store.inserted != nil {
|
||||||
|
t.Error("no metadata should be inserted on download error")
|
||||||
|
}
|
||||||
|
// Parse failure: garbage bytes are not a valid RPM.
|
||||||
|
store2 := &fakeMetaStore{}
|
||||||
|
(&Provider{}).AfterUpload(context.Background(), "r", "p", "sha256:x", fakeBlobReader{data: []byte("not an rpm")}, store2)
|
||||||
|
if store2.inserted != nil {
|
||||||
|
t.Error("no metadata should be inserted on parse error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRPMServeRepodata(t *testing.T) {
|
func TestRPMServeRepodata(t *testing.T) {
|
||||||
p := &Provider{}
|
p := &Provider{}
|
||||||
reader := fakeRPMReader{metas: []provider.RPMMetadata{{
|
reader := fakeRPMReader{metas: []provider.RPMMetadata{{
|
||||||
|
|||||||
@@ -293,6 +293,21 @@ func TestBearerTokenFlow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFetchUpstreamError(t *testing.T) {
|
||||||
|
requireStack(t)
|
||||||
|
r := seed(t, genericRemote("eng-404"))
|
||||||
|
// Upstream 404 (no cached copy, stale-on-error can't help) -> ProxyError.
|
||||||
|
_, err := testEngine.Fetch(context.Background(), r, "missing", prov(t, models.PackageGeneric))
|
||||||
|
var pe *ProxyError
|
||||||
|
if err == nil || !asProxyError(err, &pe) || pe.Status != http.StatusNotFound {
|
||||||
|
t.Errorf("expected 404 ProxyError, got %v", err)
|
||||||
|
}
|
||||||
|
// HEAD of a missing upstream path also errors.
|
||||||
|
if _, err := testEngine.Head(context.Background(), r, "missing", prov(t, models.PackageGeneric)); err == nil {
|
||||||
|
t.Error("expected head error for missing path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBearerTokenParsing(t *testing.T) {
|
func TestBearerTokenParsing(t *testing.T) {
|
||||||
// Non-Bearer challenges and missing realms are rejected.
|
// Non-Bearer challenges and missing realms are rejected.
|
||||||
if _, _, err := fetchBearerToken(context.Background(), "Basic realm=x", models.Remote{}); err == nil {
|
if _, _, err := fetchBearerToken(context.Background(), "Basic realm=x", models.Remote{}); err == nil {
|
||||||
|
|||||||
@@ -227,12 +227,28 @@ func TestServerVirtualMerge(t *testing.T) {
|
|||||||
|
|
||||||
func TestServerProbe(t *testing.T) {
|
func TestServerProbe(t *testing.T) {
|
||||||
requireStack(t)
|
requireStack(t)
|
||||||
body := fmt.Sprintf(`{"package_type":"generic","base_url":%q,"path":"data/file.bin"}`, upstream.URL)
|
create := fmt.Sprintf(`{"name":"srv-probe","package_type":"generic","repo_type":"remote","base_url":%q,"stale_on_error":true}`, upstream.URL)
|
||||||
resp, _ := req(t, "POST", "/api/v2/probe", body)
|
req(t, "POST", "/api/v2/remotes", create)
|
||||||
// Probe should reach the mock upstream and report reachable (200) or a
|
defer req(t, "DELETE", "/api/v2/remotes/srv-probe", "")
|
||||||
// structured result; either way not a server error.
|
|
||||||
if resp.StatusCode >= 500 {
|
// Reachable path -> status 200 in the probe body.
|
||||||
t.Errorf("probe server error: %d", resp.StatusCode)
|
if resp, b := req(t, "POST", "/api/v2/probe", `{"remote":"srv-probe","path":"data/file.bin"}`); resp.StatusCode != 200 || !strings.Contains(string(b), `"status":200`) {
|
||||||
|
t.Errorf("probe reachable: %d %s", resp.StatusCode, b)
|
||||||
|
}
|
||||||
|
// Missing upstream path -> upstream error reported (502) in the body.
|
||||||
|
if resp, b := req(t, "POST", "/api/v2/probe", `{"remote":"srv-probe","path":"missing"}`); resp.StatusCode != 200 || !strings.Contains(string(b), `"status":502`) {
|
||||||
|
t.Errorf("probe missing: %d %s", resp.StatusCode, b)
|
||||||
|
}
|
||||||
|
// Unknown remote -> 404 in the body.
|
||||||
|
if resp, b := req(t, "POST", "/api/v2/probe", `{"remote":"nope","path":"x"}`); resp.StatusCode != 200 || !strings.Contains(string(b), `"status":404`) {
|
||||||
|
t.Errorf("probe unknown: %d %s", resp.StatusCode, b)
|
||||||
|
}
|
||||||
|
// Bad requests.
|
||||||
|
if resp, _ := req(t, "POST", "/api/v2/probe", `{}`); resp.StatusCode != 400 {
|
||||||
|
t.Errorf("probe missing fields: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
if resp, _ := req(t, "POST", "/api/v2/probe", `not json`); resp.StatusCode != 400 {
|
||||||
|
t.Errorf("probe invalid json: %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,11 +402,31 @@ func TestServerLocalRemoveAndMissing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerProbeUnreachable(t *testing.T) {
|
func TestServerLocalUploadErrors(t *testing.T) {
|
||||||
requireStack(t)
|
requireStack(t)
|
||||||
resp, _ := req(t, "POST", "/api/v2/probe", `{"package_type":"generic","base_url":"http://127.0.0.1:1","path":"x"}`)
|
// Uploading to a remote-type repo is rejected.
|
||||||
if resp.StatusCode >= 500 {
|
create := fmt.Sprintf(`{"name":"srv-uerr","package_type":"generic","repo_type":"remote","base_url":%q,"stale_on_error":true}`, upstream.URL)
|
||||||
t.Errorf("probe of unreachable upstream should not 500: %d", resp.StatusCode)
|
req(t, "POST", "/api/v2/remotes", create)
|
||||||
|
defer req(t, "DELETE", "/api/v2/remotes/srv-uerr", "")
|
||||||
|
if resp, _ := put(t, "/api/v2/remotes/srv-uerr/files/x.bin", []byte("x")); resp.StatusCode != 400 {
|
||||||
|
t.Errorf("upload to remote repo should be 400, got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate generic upload is a conflict.
|
||||||
|
req(t, "POST", "/api/v2/remotes", `{"name":"srv-dup","package_type":"generic","repo_type":"local"}`)
|
||||||
|
defer req(t, "DELETE", "/api/v2/remotes/srv-dup", "")
|
||||||
|
put(t, "/api/v2/remotes/srv-dup/files/dup.bin", []byte("one"))
|
||||||
|
if resp, _ := put(t, "/api/v2/remotes/srv-dup/files/dup.bin", []byte("two")); resp.StatusCode != 409 {
|
||||||
|
t.Errorf("duplicate upload should be 409, got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download of a missing local file is 404.
|
||||||
|
if resp, _ := req(t, "GET", "/api/v1/local/srv-dup/does/not/exist", ""); resp.StatusCode != 404 {
|
||||||
|
t.Errorf("missing local download should be 404, got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
// Unknown virtual is 404.
|
||||||
|
if resp, _ := req(t, "GET", "/api/v1/virtual/nope/index.yaml", ""); resp.StatusCode != 404 {
|
||||||
|
t.Errorf("unknown virtual should be 404, got %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user