zsvo/pkg/fetcher/fetcher_test.go

147 lines
4 KiB
Go

package fetcher
import (
"archive/tar"
"bytes"
"compress/gzip"
"crypto/sha256"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"testing"
)
func TestDownloadAndExtractDebianDSCUsesOnlyOrig(t *testing.T) {
origArchive := buildTarGz(t, map[string]string{
"demo-1.0/README": "hello upstream\n",
})
debianPatch := []byte("pretend debian patch content")
origHash := hashHex(origArchive)
debianHash := hashHex(debianPatch)
dsc := fmt.Sprintf(`Format: 3.0 (quilt)
Source: demo
Checksums-Sha256:
%s %d demo_1.0.orig.tar.gz
%s %d demo_1.0-1.debian.tar.xz
`, origHash, len(origArchive), debianHash, len(debianPatch))
hits := map[string]int{}
withHTTPTransport(t, roundTripFunc(func(req *http.Request) (*http.Response, error) {
hits[req.URL.Path]++
switch req.URL.Path {
case "/pool/main/d/demo/demo_1.0-1.dsc":
return httpResponse(req, http.StatusOK, []byte(dsc)), nil
case "/pool/main/d/demo/demo_1.0.orig.tar.gz":
return httpResponse(req, http.StatusOK, origArchive), nil
case "/pool/main/d/demo/demo_1.0-1.debian.tar.xz":
return httpResponse(req, http.StatusOK, debianPatch), nil
default:
return httpResponse(req, http.StatusNotFound, []byte("not found")), nil
}
}))
workDir := t.TempDir()
f := NewFetcher(filepath.Join(workDir, "cache"))
destDir := filepath.Join(workDir, "src")
err := f.DownloadAndExtract("https://deb.example/pool/main/d/demo/demo_1.0-1.dsc", "", destDir)
if err != nil {
t.Fatalf("DownloadAndExtract() error = %v", err)
}
readmePath := filepath.Join(destDir, "demo-1.0", "README")
content, err := os.ReadFile(readmePath)
if err != nil {
t.Fatalf("failed to read extracted file %s: %v", readmePath, err)
}
if string(content) != "hello upstream\n" {
t.Fatalf("unexpected extracted file content: %q", string(content))
}
debianHits := hits["/pool/main/d/demo/demo_1.0-1.debian.tar.xz"]
origHits := hits["/pool/main/d/demo/demo_1.0.orig.tar.gz"]
dscHits := hits["/pool/main/d/demo/demo_1.0-1.dsc"]
if dscHits == 0 || origHits == 0 {
t.Fatalf("expected dsc and orig to be downloaded, got dsc=%d orig=%d", dscHits, origHits)
}
if debianHits != 0 {
t.Fatalf("expected debian patch archive to be ignored, got %d requests", debianHits)
}
}
func TestDownloadAndExtractDebianDSCChecksumMismatch(t *testing.T) {
withHTTPTransport(t, roundTripFunc(func(req *http.Request) (*http.Response, error) {
return httpResponse(req, http.StatusOK, []byte("Format: 3.0 (quilt)\nSource: demo\n")), nil
}))
f := NewFetcher(filepath.Join(t.TempDir(), "cache"))
err := f.DownloadAndExtract("https://deb.example/demo_1.0-1.dsc", "deadbeef", t.TempDir())
if err == nil {
t.Fatalf("expected checksum mismatch error")
}
}
type roundTripFunc func(*http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}
func withHTTPTransport(t *testing.T, transport http.RoundTripper) {
t.Helper()
orig := http.DefaultTransport
http.DefaultTransport = transport
t.Cleanup(func() {
http.DefaultTransport = orig
})
}
func httpResponse(req *http.Request, code int, body []byte) *http.Response {
return &http.Response{
StatusCode: code,
Body: io.NopCloser(bytes.NewReader(body)),
Header: make(http.Header),
Request: req,
}
}
func buildTarGz(t *testing.T, files map[string]string) []byte {
t.Helper()
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
tw := tar.NewWriter(gz)
for name, content := range files {
hdr := &tar.Header{
Name: name,
Mode: 0o644,
Size: int64(len(content)),
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatalf("failed to write tar header: %v", err)
}
if _, err := tw.Write([]byte(content)); err != nil {
t.Fatalf("failed to write tar body: %v", err)
}
}
if err := tw.Close(); err != nil {
t.Fatalf("failed to close tar writer: %v", err)
}
if err := gz.Close(); err != nil {
t.Fatalf("failed to close gzip writer: %v", err)
}
return buf.Bytes()
}
func hashHex(data []byte) string {
sum := sha256.Sum256(data)
return fmt.Sprintf("%x", sum)
}