278 lines
7.8 KiB
Go
278 lines
7.8 KiB
Go
package apt
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
var testCfg = Config{
|
|
Suite: "bookworm",
|
|
Components: []string{"main"},
|
|
Architectures: []string{"amd64"},
|
|
}
|
|
|
|
// buildPackagesGz creates a gzipped Packages file from a list of stanzas.
|
|
func buildPackagesGz(t *testing.T, stanzas []string) []byte {
|
|
t.Helper()
|
|
plain := []byte(strings.Join(stanzas, "\n\n") + "\n")
|
|
|
|
var buf bytes.Buffer
|
|
gzw := gzip.NewWriter(&buf)
|
|
gzw.Write(plain)
|
|
gzw.Close()
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func makeStanza(pkg, version, filename, sha256 string, size int) string {
|
|
return strings.Join([]string{
|
|
"Package: " + pkg,
|
|
"Version: " + version,
|
|
"Architecture: amd64",
|
|
"Filename: " + filename,
|
|
"SHA256: " + sha256,
|
|
"Size: " + itoa(size),
|
|
}, "\n")
|
|
}
|
|
|
|
func itoa(n int) string {
|
|
if n == 0 {
|
|
return "0"
|
|
}
|
|
buf := make([]byte, 0, 10)
|
|
for n > 0 {
|
|
buf = append([]byte{byte('0' + n%10)}, buf...)
|
|
n /= 10
|
|
}
|
|
return string(buf)
|
|
}
|
|
|
|
// setupAptRepo creates the directory structure and writes the upstream
|
|
// Packages.gz for component/arch. It creates the given deb files on disk.
|
|
func setupAptRepo(t *testing.T, stanzas []string, presentOnDisk []string) string {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
|
|
binDir := filepath.Join(dir, "dists", testCfg.Suite, "main", "binary-amd64")
|
|
os.MkdirAll(binDir, 0o755)
|
|
os.MkdirAll(filepath.Join(dir, "pool"), 0o755)
|
|
|
|
// Write upstream Packages.gz.
|
|
gzData := buildPackagesGz(t, stanzas)
|
|
if err := os.WriteFile(filepath.Join(binDir, upstreamPackagesGz), gzData, 0o644); err != nil {
|
|
t.Fatalf("write upstream Packages.gz: %v", err)
|
|
}
|
|
|
|
// Create selected deb files on disk.
|
|
for _, filename := range presentOnDisk {
|
|
dest := filepath.Join(dir, filepath.FromSlash(filename))
|
|
os.MkdirAll(filepath.Dir(dest), 0o755)
|
|
if err := os.WriteFile(dest, []byte("fake-deb"), 0o644); err != nil {
|
|
t.Fatalf("write deb %s: %v", filename, err)
|
|
}
|
|
}
|
|
|
|
return dir
|
|
}
|
|
|
|
// readLocalPackages reads and parses the local Packages file (plain text).
|
|
func readLocalPackages(t *testing.T, dir string) []DebPackage {
|
|
t.Helper()
|
|
path := filepath.Join(dir, "dists", testCfg.Suite, "main", "binary-amd64", "Packages")
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read Packages: %v", err)
|
|
}
|
|
pkgs, err := ParsePackages(data)
|
|
if err != nil {
|
|
t.Fatalf("parse Packages: %v", err)
|
|
}
|
|
return pkgs
|
|
}
|
|
|
|
// --- InitEmptyRepo ---
|
|
|
|
func TestInitEmptyRepo_createsStructure(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := InitEmptyRepo(dir, testCfg); err != nil {
|
|
t.Fatalf("InitEmptyRepo: %v", err)
|
|
}
|
|
|
|
for _, path := range []string{
|
|
filepath.Join(dir, "pool"),
|
|
filepath.Join(dir, "dists", testCfg.Suite),
|
|
filepath.Join(dir, "dists", testCfg.Suite, "main", "binary-amd64"),
|
|
filepath.Join(dir, "dists", testCfg.Suite, "Release"),
|
|
} {
|
|
if _, err := os.Stat(path); err != nil {
|
|
t.Errorf("expected %s to exist: %v", path, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInitEmptyRepo_releaseIsParseable(t *testing.T) {
|
|
dir := t.TempDir()
|
|
InitEmptyRepo(dir, testCfg)
|
|
|
|
data, _ := os.ReadFile(filepath.Join(dir, "dists", testCfg.Suite, "Release"))
|
|
rf, err := ParseRelease(data)
|
|
if err != nil {
|
|
t.Fatalf("ParseRelease on generated file: %v", err)
|
|
}
|
|
if rf.Suite != testCfg.Suite {
|
|
t.Errorf("Suite: got %q, want %q", rf.Suite, testCfg.Suite)
|
|
}
|
|
}
|
|
|
|
func TestInitEmptyRepo_idempotent(t *testing.T) {
|
|
dir := t.TempDir()
|
|
InitEmptyRepo(dir, testCfg)
|
|
|
|
releaseBefore, _ := os.ReadFile(filepath.Join(dir, "dists", testCfg.Suite, "Release"))
|
|
InitEmptyRepo(dir, testCfg) // second call
|
|
releaseAfter, _ := os.ReadFile(filepath.Join(dir, "dists", testCfg.Suite, "Release"))
|
|
|
|
if !bytes.Equal(releaseBefore, releaseAfter) {
|
|
t.Error("second InitEmptyRepo modified Release file")
|
|
}
|
|
}
|
|
|
|
// --- RegenerateMetadata ---
|
|
|
|
func TestRegenerateMetadata_keepsOnlyPresentFiles(t *testing.T) {
|
|
stanzas := []string{
|
|
makeStanza("curl", "7.88", "pool/main/c/curl/curl_7.88_amd64.deb", "aaa", 100),
|
|
makeStanza("wget", "1.21", "pool/main/w/wget/wget_1.21_amd64.deb", "bbb", 200),
|
|
makeStanza("vim", "9.0", "pool/main/v/vim/vim_9.0_amd64.deb", "ccc", 300),
|
|
}
|
|
// Only curl and vim are present.
|
|
present := []string{
|
|
"pool/main/c/curl/curl_7.88_amd64.deb",
|
|
"pool/main/v/vim/vim_9.0_amd64.deb",
|
|
}
|
|
dir := setupAptRepo(t, stanzas, present)
|
|
|
|
if err := RegenerateMetadata(dir, testCfg); err != nil {
|
|
t.Fatalf("RegenerateMetadata: %v", err)
|
|
}
|
|
|
|
pkgs := readLocalPackages(t, dir)
|
|
if len(pkgs) != 2 {
|
|
t.Fatalf("expected 2 packages, got %d", len(pkgs))
|
|
}
|
|
names := map[string]bool{pkgs[0].Package: true, pkgs[1].Package: true}
|
|
if !names["curl"] {
|
|
t.Error("expected curl in output")
|
|
}
|
|
if !names["vim"] {
|
|
t.Error("expected vim in output")
|
|
}
|
|
if names["wget"] {
|
|
t.Error("wget should be filtered out (not on disk)")
|
|
}
|
|
}
|
|
|
|
func TestRegenerateMetadata_allAbsent(t *testing.T) {
|
|
stanzas := []string{
|
|
makeStanza("curl", "7.88", "pool/main/c/curl/curl_7.88_amd64.deb", "aaa", 100),
|
|
}
|
|
dir := setupAptRepo(t, stanzas, nil) // nothing on disk
|
|
|
|
if err := RegenerateMetadata(dir, testCfg); err != nil {
|
|
t.Fatalf("RegenerateMetadata: %v", err)
|
|
}
|
|
|
|
pkgs := readLocalPackages(t, dir)
|
|
if len(pkgs) != 0 {
|
|
t.Errorf("expected 0 packages, got %d", len(pkgs))
|
|
}
|
|
}
|
|
|
|
func TestRegenerateMetadata_writesRelease(t *testing.T) {
|
|
stanzas := []string{
|
|
makeStanza("curl", "7.88", "pool/main/c/curl/curl_7.88_amd64.deb", "aaa", 100),
|
|
}
|
|
dir := setupAptRepo(t, stanzas, []string{"pool/main/c/curl/curl_7.88_amd64.deb"})
|
|
|
|
if err := RegenerateMetadata(dir, testCfg); err != nil {
|
|
t.Fatalf("RegenerateMetadata: %v", err)
|
|
}
|
|
|
|
releasePath := filepath.Join(dir, "dists", testCfg.Suite, "Release")
|
|
data, err := os.ReadFile(releasePath)
|
|
if err != nil {
|
|
t.Fatalf("Release not written: %v", err)
|
|
}
|
|
|
|
rf, err := ParseRelease(data)
|
|
if err != nil {
|
|
t.Fatalf("ParseRelease: %v", err)
|
|
}
|
|
if rf.Suite != testCfg.Suite {
|
|
t.Errorf("Suite: got %q, want %q", rf.Suite, testCfg.Suite)
|
|
}
|
|
// Release must reference both Packages and Packages.gz.
|
|
paths := map[string]bool{}
|
|
for _, e := range rf.SHA256 {
|
|
paths[e.Path] = true
|
|
}
|
|
if !paths["main/binary-amd64/Packages"] {
|
|
t.Error("Release missing main/binary-amd64/Packages entry")
|
|
}
|
|
if !paths["main/binary-amd64/Packages.gz"] {
|
|
t.Error("Release missing main/binary-amd64/Packages.gz entry")
|
|
}
|
|
}
|
|
|
|
func TestRegenerateMetadata_writesPackagesGz(t *testing.T) {
|
|
stanzas := []string{
|
|
makeStanza("curl", "7.88", "pool/main/c/curl/curl_7.88_amd64.deb", "aaa", 100),
|
|
}
|
|
dir := setupAptRepo(t, stanzas, []string{"pool/main/c/curl/curl_7.88_amd64.deb"})
|
|
|
|
RegenerateMetadata(dir, testCfg)
|
|
|
|
gzPath := filepath.Join(dir, "dists", testCfg.Suite, "main", "binary-amd64", "Packages.gz")
|
|
if _, err := os.Stat(gzPath); err != nil {
|
|
t.Errorf("Packages.gz not written: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRegenerateMetadata_noUpstream_skips(t *testing.T) {
|
|
// No .upstream-Packages.gz — should return nil (skip silently).
|
|
dir := t.TempDir()
|
|
os.MkdirAll(filepath.Join(dir, "dists", testCfg.Suite, "main", "binary-amd64"), 0o755)
|
|
|
|
if err := RegenerateMetadata(dir, testCfg); err != nil {
|
|
t.Errorf("expected no error when upstream missing, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRegenerateMetadata_alwaysFiltersFromUpstream(t *testing.T) {
|
|
// Two successive regens must always start from the upstream list.
|
|
stanzas := []string{
|
|
makeStanza("curl", "7.88", "pool/main/c/curl/curl_7.88_amd64.deb", "aaa", 100),
|
|
makeStanza("wget", "1.21", "pool/main/w/wget/wget_1.21_amd64.deb", "bbb", 200),
|
|
}
|
|
dir := setupAptRepo(t, stanzas, []string{"pool/main/c/curl/curl_7.88_amd64.deb"})
|
|
|
|
// First regen: only curl.
|
|
RegenerateMetadata(dir, testCfg)
|
|
if len(readLocalPackages(t, dir)) != 1 {
|
|
t.Fatal("first regen: expected 1 package")
|
|
}
|
|
|
|
// Add wget, regen again.
|
|
dest := filepath.Join(dir, "pool/main/w/wget/wget_1.21_amd64.deb")
|
|
os.MkdirAll(filepath.Dir(dest), 0o755)
|
|
os.WriteFile(dest, []byte("fake"), 0o644)
|
|
|
|
RegenerateMetadata(dir, testCfg)
|
|
pkgs := readLocalPackages(t, dir)
|
|
if len(pkgs) != 2 {
|
|
t.Fatalf("second regen: expected 2 packages, got %d", len(pkgs))
|
|
}
|
|
}
|