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 }