repo-sync/mirror/resolve_test.go
James Coleman 5af1dd8928
Some checks failed
Go package / build (push) Has been cancelled
first commit
2026-07-28 19:22:20 -05:00

123 lines
4 KiB
Go

package mirror
import (
"context"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
"github.com/grmrgecko/repo-sync/fetch"
"github.com/grmrgecko/repo-sync/internal/testrepos"
)
// TestResolveSource verifies direct repositories are used as-is and
// mirrorlist bodies expand into multi-mirror sources.
func TestResolveSource(t *testing.T) {
www := t.TempDir()
testrepos.BuildRPMRepo(t, filepath.Join(www, "el9"))
srv := testrepos.ServeDir(t, www)
// The mirrorlist needs the real server URL, which is only known now
// that the listener is up; files are read per-request.
testrepos.WriteFile(t, filepath.Join(www, "ml.txt"),
[]byte("http://127.0.0.1:1/el9\n"+srv.URL+"/el9\n"))
direct, layoutURL, err := resolveSource(context.Background(), srv.URL+"/el9", RepoRPM)
if err != nil {
t.Fatal(err)
}
if len(direct.Bases()) != 1 {
t.Errorf("direct bases = %v", direct.Bases())
}
if layoutURL != srv.URL+"/el9" {
t.Errorf("direct layout URL = %q", layoutURL)
}
ml, layoutURL, err := resolveSource(context.Background(), srv.URL+"/ml.txt", RepoRPM)
if err != nil {
t.Fatal(err)
}
if len(ml.Bases()) != 2 {
t.Errorf("mirrorlist bases = %v", ml.Bases())
}
if layoutURL != "http://127.0.0.1:1/el9" {
t.Errorf("mirrorlist layout URL = %q", layoutURL)
}
if _, _, err := resolveSource(context.Background(), srv.URL+"/el9/Packages", RepoRPM); err == nil {
t.Error("expected error for a non-repository URL")
}
}
// TestResolveSourceQueryMirrorlist verifies a mirrorlist URL carrying a
// query string is read as a mirrorlist rather than probed as a repository.
// A repository path cannot be joined onto such a URL: it would land inside
// the query, and endpoints that answer 200 with an error page for an
// unrecognized parameter would then look like a repository.
func TestResolveSourceQueryMirrorlist(t *testing.T) {
www := t.TempDir()
testrepos.BuildRPMRepo(t, filepath.Join(www, "el9"))
repo := testrepos.ServeDir(t, www)
var probed []string
list := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
probed = append(probed, r.URL.String())
if r.URL.Query().Get("repo") != "os" {
// Several real mirrorlist services answer 200 with a message.
w.Write([]byte("Invalid release/repo/arch combination\n"))
return
}
w.Write([]byte(repo.URL + "/el9\n"))
}))
t.Cleanup(list.Close)
src, layoutURL, err := resolveSource(context.Background(), list.URL+"/?release=9&repo=os", RepoRPM)
if err != nil {
t.Fatal(err)
}
if bases := src.Bases(); len(bases) != 1 || bases[0] != repo.URL+"/el9" {
t.Errorf("bases = %v, want [%s]", bases, repo.URL+"/el9")
}
if layoutURL != repo.URL+"/el9" {
t.Errorf("layout URL = %q, want %q", layoutURL, repo.URL+"/el9")
}
for _, u := range probed {
if strings.Contains(u, "repodata") {
t.Errorf("probe folded a repository path into the query: %s", u)
}
}
}
// TestResolveSourceMetalink verifies metalink documents expand into
// multi-mirror sources for RPM repositories.
func TestResolveSourceMetalink(t *testing.T) {
www := t.TempDir()
testrepos.BuildRPMRepo(t, filepath.Join(www, "el9"))
srv := testrepos.ServeDir(t, www)
metalink := `<?xml version="1.0"?><metalink><files><file name="repomd.xml"><resources>` +
`<url protocol="http">http://127.0.0.1:1/dead/repodata/repomd.xml</url>` +
`<url protocol="http">` + srv.URL + `/el9/repodata/repomd.xml</url>` +
`</resources></file></files></metalink>`
testrepos.WriteFile(t, filepath.Join(www, "metalink.xml"), []byte(metalink))
src, layoutURL, err := resolveSource(context.Background(), srv.URL+"/metalink.xml", RepoRPM)
if err != nil {
t.Fatal(err)
}
if len(src.Bases()) != 2 || src.Bases()[1] != srv.URL+"/el9" {
t.Errorf("bases = %v", src.Bases())
}
if layoutURL != "http://127.0.0.1:1/dead" {
t.Errorf("layout URL = %q", layoutURL)
}
// The dead first mirror must fail over to the live one.
resp, err := src.Get(context.Background(), "repodata/repomd.xml", fetch.GetOptions{})
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
}