github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/integrations/apt/build_test.go (about)

     1  package apt_test
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"io/fs"
     7  	"maps"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"strings"
    12  	"testing"
    13  	"testing/fstest"
    14  	"time"
    15  
    16  	"github.com/google/go-cmp/cmp"
    17  
    18  	"github.com/kubri/kubri/integrations/apt"
    19  	"github.com/kubri/kubri/internal/test"
    20  	"github.com/kubri/kubri/pkg/crypto/pgp"
    21  	source "github.com/kubri/kubri/source/file"
    22  	"github.com/kubri/kubri/target"
    23  	ftarget "github.com/kubri/kubri/target/file"
    24  )
    25  
    26  func TestBuild(t *testing.T) {
    27  	want := readTestData(t, ".gz", ".xz")
    28  	now := time.Date(2023, 11, 19, 23, 37, 12, 0, time.UTC)
    29  
    30  	dir := t.TempDir() + "/apt"
    31  	src, _ := source.New(source.Config{Path: "../../testdata"})
    32  	tgt, _ := ftarget.New(ftarget.Config{Path: dir})
    33  
    34  	test.Golden(t, "testdata", dir, test.Ignore("*.deb", "*.gz", "*.xz"))
    35  
    36  	t.Run("New", func(t *testing.T) {
    37  		c := &apt.Config{Source: src, Target: tgt}
    38  		testBuild(t, c, want, now)
    39  	})
    40  
    41  	// Should be no-op as nothing changed so timestamp should still be valid.
    42  	t.Run("NoChange", func(t *testing.T) {
    43  		c := &apt.Config{Source: src, Target: tgt}
    44  		testBuild(t, c, want, now.Add(time.Hour))
    45  	})
    46  
    47  	t.Run("PGP", func(t *testing.T) {
    48  		dir := t.TempDir()
    49  		pgpKey, _ := pgp.NewPrivateKey("test", "test@example.com")
    50  		tgt, _ := ftarget.New(ftarget.Config{Path: dir})
    51  
    52  		c := &apt.Config{
    53  			Source: src,
    54  			Target: tgt,
    55  			PGPKey: pgpKey,
    56  		}
    57  
    58  		// Remove InRelease and test that separately below.
    59  		wantPGP := maps.Clone(want)
    60  		delete(wantPGP, "dists/stable/InRelease")
    61  
    62  		testBuild(t, c, wantPGP, now)
    63  
    64  		data, _ := os.ReadFile(filepath.Join(dir, "dists", "stable", "Release"))
    65  		sig, _ := os.ReadFile(filepath.Join(dir, "dists", "stable", "Release.gpg"))
    66  		if !pgp.Verify(pgp.Public(pgpKey), data, sig) {
    67  			t.Error("should pass pgp verification")
    68  		}
    69  
    70  		in, _ := os.ReadFile(filepath.Join(dir, "dists", "stable", "InRelease"))
    71  		data, sig, err := pgp.Split(in)
    72  		if err != nil {
    73  			t.Fatal(err)
    74  		}
    75  		if !pgp.Verify(pgp.Public(c.PGPKey), data, sig) {
    76  			t.Error("failed to verify InRelease signature")
    77  		}
    78  		if diff := cmp.Diff(want["dists/stable/Release"], string(data)); diff != "" {
    79  			t.Error("dists/stable/InRelease", diff)
    80  		}
    81  	})
    82  
    83  	t.Run("CustomCompress", func(t *testing.T) {
    84  		dir := t.TempDir()
    85  		tgt, _ := ftarget.New(ftarget.Config{Path: dir})
    86  
    87  		c := &apt.Config{
    88  			Source:   src,
    89  			Target:   tgt,
    90  			Compress: apt.BZIP2 | apt.ZSTD,
    91  		}
    92  
    93  		err := apt.Build(context.Background(), c)
    94  		if err != nil {
    95  			t.Fatal(err)
    96  		}
    97  
    98  		err = fstest.TestFS(os.DirFS(dir),
    99  			"dists/stable/main/binary-amd64/Packages",
   100  			"dists/stable/main/binary-amd64/Packages.bz2",
   101  			"dists/stable/main/binary-amd64/Packages.zst",
   102  			"dists/stable/main/binary-i386/Packages",
   103  			"dists/stable/main/binary-i386/Packages.bz2",
   104  			"dists/stable/main/binary-i386/Packages.zst",
   105  		)
   106  		if err != nil {
   107  			t.Error(err)
   108  		}
   109  
   110  		err = fstest.TestFS(os.DirFS(dir),
   111  			"dists/stable/main/binary-amd64/Packages.gz",
   112  			"dists/stable/main/binary-i386/Packages.gz",
   113  		)
   114  		if err == nil {
   115  			t.Error("should not have gzip files")
   116  		}
   117  	})
   118  }
   119  
   120  func readTestData(t *testing.T, compress ...string) map[string]string {
   121  	t.Helper()
   122  
   123  	want := make(map[string]string)
   124  
   125  	err := fs.WalkDir(os.DirFS("testdata"), ".", func(path string, d fs.DirEntry, err error) error {
   126  		if err != nil || d.IsDir() {
   127  			return err
   128  		}
   129  		b, err := fs.ReadFile(os.DirFS("testdata"), path)
   130  		if err != nil {
   131  			return err
   132  		}
   133  		want[path] = string(b)
   134  		if d.Name() == "Packages" {
   135  			for _, ext := range compress {
   136  				want[path+ext] = string(b)
   137  			}
   138  		}
   139  		return nil
   140  	})
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  
   145  	return want
   146  }
   147  
   148  func testBuild(t *testing.T, c *apt.Config, want map[string]string, now time.Time) { //nolint:thelper
   149  	apt.SetTime(now)
   150  
   151  	err := apt.Build(context.Background(), c)
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	for name, data := range want {
   157  		got := readFile(t, c.Target, name)
   158  
   159  		ext := path.Ext(name)
   160  		base := strings.TrimSuffix(path.Base(name), ext)
   161  		if base == "Packages" {
   162  			data = want[strings.TrimSuffix(name, ext)]
   163  		}
   164  
   165  		if diff := cmp.Diff(data, string(got)); diff != "" {
   166  			t.Error(name, diff)
   167  		}
   168  	}
   169  }
   170  
   171  func readFile(t *testing.T, tgt target.Target, name string) []byte {
   172  	t.Helper()
   173  
   174  	r, err := tgt.NewReader(context.Background(), name)
   175  	if err != nil {
   176  		t.Fatal(name, err)
   177  	}
   178  	defer r.Close()
   179  
   180  	r, err = apt.Decompress(path.Ext(name))(r)
   181  	if err != nil {
   182  		t.Fatal(name, err)
   183  	}
   184  	defer r.Close()
   185  
   186  	b, err := io.ReadAll(r)
   187  	if err != nil {
   188  		t.Fatal(name, err)
   189  	}
   190  
   191  	return b
   192  }