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