181 lines
7.1 KiB
Go
181 lines
7.1 KiB
Go
//go:build integration
|
|
|
|
package builder
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"context"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
func TestAnArchivePinnedByURLIsFetchedCheckedAndPackedWithoutItsOwnDirectory(t *testing.T) {
|
|
body := release(t)
|
|
v := &vendor{files: map[string][]byte{"/release.tar.gz": body}}
|
|
base := serveVendor(t, v)
|
|
r, workspace := aRepository(t, pinnedManifest(base, sumHex(body)), nil)
|
|
got, err := Build(context.Background(), r.run, r, "https://forge.invalid/catalogue.git", "", "", workspace, nil,
|
|
Npmrc{}, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
digest, _ := got.Manifest.Resources[0]["digest"].(string)
|
|
if !strings.HasPrefix(digest, "sha256:") || r.archives["betterbird/archive"] != digest {
|
|
t.Fatalf("the release was not published and pinned as the module's archive: %v, published %v",
|
|
got.Manifest.Resources[0], r.archives)
|
|
}
|
|
|
|
packed, err := upstreamArchive(context.Background(), workspace, catalogue.Artifact{Name: "archive", Kind: "archive",
|
|
URL: base + "/release.tar.gz", SHA256: sumHex(body), Version: "153.4.0esr-bb10", Within: "betterbird"},
|
|
func(string, string, ...any) {})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if "sha256:"+sumHex(packed) != digest {
|
|
t.Fatalf("a second packing of one pin gave another digest: %s and %s", digest, sumHex(packed))
|
|
}
|
|
entries := entriesOf(t, packed)
|
|
want := map[string]string{
|
|
"application.ini": "[App]\nVersion=153.4.0\n -rw-r--r--",
|
|
"betterbird": "#!/bin/sh\n -rwxr-xr-x",
|
|
"defaults/pref/channel-prefs.js": "pref(\"app.update.channel\", \"default\");\n -rw-r--r--",
|
|
}
|
|
if len(entries) != len(want) {
|
|
t.Fatalf("the archive holds %v, want %v", entries, want)
|
|
}
|
|
for name, w := range want {
|
|
if entries[name] != w {
|
|
t.Errorf("%s is %q in the archive, want %q", name, entries[name], w)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAnUpstreamFileIsFetchedOnceAndKeptByItsDigest(t *testing.T) {
|
|
// The vendor of an add-on serves only its newest version at one address: a pin that was fetched once
|
|
// must still build after the address moved on.
|
|
body := release(t)
|
|
v := &vendor{files: map[string][]byte{"/release.tar.gz": body}}
|
|
base := serveVendor(t, v)
|
|
r, workspace := aRepository(t, pinnedManifest(base, sumHex(body)), nil)
|
|
first, err := Build(context.Background(), r.run, r, "https://forge.invalid/catalogue.git", "", "", workspace, nil,
|
|
Npmrc{}, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v.files["/release.tar.gz"] = []byte("the vendor's next release")
|
|
second, err := Build(context.Background(), r.run, r, "https://forge.invalid/catalogue.git", "", "", workspace, nil,
|
|
Npmrc{}, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("a pin fetched before failed to build once the address served something else: %v", err)
|
|
}
|
|
if v.asked != 1 {
|
|
t.Fatalf("the vendor was asked %d times for one pin, want once", v.asked)
|
|
}
|
|
if first.Manifest.Resources[0]["digest"] != second.Manifest.Resources[0]["digest"] {
|
|
t.Fatal("two builds of one pin gave two digests")
|
|
}
|
|
}
|
|
|
|
func TestAnUpstreamFileWhoseDownloadDoesNotMatchItsPinFailsAndNamesBothDigests(t *testing.T) {
|
|
pinned := release(t)
|
|
served := gzipped(t, tarOf(t, []tarEntry{{name: "betterbird/betterbird", body: "not what was reviewed", mode: 0o755}}))
|
|
v := &vendor{files: map[string][]byte{"/release.tar.gz": served}}
|
|
base := serveVendor(t, v)
|
|
r, workspace := aRepository(t, pinnedManifest(base, sumHex(pinned)), nil)
|
|
_, err := Build(context.Background(), r.run, r, "https://forge.invalid/catalogue.git", "", "", workspace, nil,
|
|
Npmrc{}, GitCredential{}, nil)
|
|
if err == nil {
|
|
t.Fatal("a download that is not the pinned file was built")
|
|
}
|
|
for _, digest := range []string{sumHex(pinned), sumHex(served)} {
|
|
if !strings.Contains(err.Error(), "sha256:"+digest) {
|
|
t.Errorf("the refusal does not name sha256:%s: %v", digest, err)
|
|
}
|
|
}
|
|
if len(r.archives) != 0 {
|
|
t.Fatalf("something was published: %v", r.archives)
|
|
}
|
|
kept, _ := filepath.Glob(filepath.Join(upstreamDir(workspace), "*"))
|
|
if len(kept) != 0 {
|
|
t.Fatalf("the refused download was kept: %v", kept)
|
|
}
|
|
}
|
|
|
|
func TestAnUpstreamArchiveCompressedWithXzIsRead(t *testing.T) {
|
|
// Betterbird publishes its Linux release as .tar.xz; the build seat's image carries xz for it.
|
|
if _, err := exec.LookPath("xz"); err != nil {
|
|
t.Skip("NOT TESTED here: no xz on this machine (the build seat's image carries it)")
|
|
}
|
|
plain := tarOf(t, []tarEntry{{name: "betterbird/betterbird", body: "#!/bin/sh\n", mode: 0o755}})
|
|
cmd := exec.Command("xz", "-zc")
|
|
cmd.Stdin = bytes.NewReader(plain)
|
|
compressed, err := cmd.Output()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v := &vendor{files: map[string][]byte{"/r.tar.xz": compressed}}
|
|
base := serveVendor(t, v)
|
|
packed, err := upstreamArchive(context.Background(), t.TempDir(), catalogue.Artifact{Name: "archive", Kind: "archive",
|
|
URL: base + "/r.tar.xz", SHA256: sumHex(compressed), Version: "1", Within: "betterbird"}, func(string, string, ...any) {})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var names []string
|
|
for n := range entriesOf(t, packed) {
|
|
names = append(names, n)
|
|
}
|
|
sort.Strings(names)
|
|
if strings.Join(names, ",") != "betterbird" {
|
|
t.Fatalf("the xz release unpacked to %v", names)
|
|
}
|
|
}
|
|
|
|
func TestAnUpstreamTarWithAGlobalHeaderNestedWithinAndTrailingDataIsRead(t *testing.T) {
|
|
// git archive writes a pax global header; a release may unpack under a/b with a/ listed first; xz may be
|
|
// followed by bytes after the tar's end, which must be read before xz is waited for.
|
|
var raw bytes.Buffer
|
|
w := tar.NewWriter(&raw)
|
|
w.WriteHeader(&tar.Header{Typeflag: tar.TypeXGlobalHeader, Name: "pax_global_header", PAXRecords: map[string]string{"comment": "abc"}})
|
|
w.WriteHeader(&tar.Header{Name: "a/", Typeflag: tar.TypeDir, Mode: 0o755})
|
|
w.WriteHeader(&tar.Header{Name: "a/b/", Typeflag: tar.TypeDir, Mode: 0o755})
|
|
w.WriteHeader(&tar.Header{Name: "a/b/f", Typeflag: tar.TypeReg, Mode: 0o644, Size: 1})
|
|
w.Write([]byte("x"))
|
|
w.Close()
|
|
body := gzipped(t, raw.Bytes())
|
|
v := &vendor{files: map[string][]byte{"/r.tar.gz": body}}
|
|
base := serveVendor(t, v)
|
|
packed, err := upstreamArchive(context.Background(), t.TempDir(), catalogue.Artifact{Name: "a", Kind: "archive",
|
|
URL: base + "/r.tar.gz", SHA256: sumHex(body), Version: "1", Within: "a/b"}, func(string, string, ...any) {})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if e := entriesOf(t, packed); len(e) != 1 || e["f"] == "" {
|
|
t.Fatalf("%v", e)
|
|
}
|
|
if _, err := exec.LookPath("xz"); err != nil {
|
|
t.Skip("NOT TESTED here: no xz")
|
|
}
|
|
// A megabyte after the tar's end: more than a pipe holds.
|
|
trailing := append(append([]byte{}, raw.Bytes()...), bytes.Repeat([]byte{0}, 1<<20)...)
|
|
cmd := exec.Command("xz", "-zc")
|
|
cmd.Stdin = bytes.NewReader(trailing)
|
|
compressed, err := cmd.Output()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v.files["/r.tar.xz"] = compressed
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
if _, err := upstreamArchive(ctx, t.TempDir(), catalogue.Artifact{Name: "a", Kind: "archive", URL: base + "/r.tar.xz",
|
|
SHA256: sumHex(compressed), Version: "1", Within: "a/b"}, func(string, string, ...any) {}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|