Skip to content

Commit f5661c8

Browse files
committed
Add unit tests for commands
1 parent 7b6f134 commit f5661c8

16 files changed

+538
-4
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@
1414

1515
# Output of the go coverage tool, specifically when used with LiteIDE
1616
*.out
17+
18+
# Test Debian package
19+
/testdata/nullpkg-1.0/debian/.debhelper/
20+
/testdata/nullpkg-1.0/debian/debhelper-build-stamp
21+
/testdata/nullpkg-1.0/debian/files
22+
/testdata/nullpkg-1.0/debian/nullpkg.substvars
23+
/testdata/nullpkg-1.0/debian/nullpkg/

aptblob.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"io"
2324
"os"
2425
"path/filepath"
2526
"strings"
@@ -34,7 +35,7 @@ import (
3435
"zombiezen.com/go/aptblob/internal/deb"
3536
)
3637

37-
func cmdInit(ctx context.Context, bucket *blob.Bucket, dist distribution, keyID string) error {
38+
func cmdInit(ctx context.Context, bucket *blob.Bucket, stdin io.Reader, stderr io.Writer, dist distribution, keyID string) error {
3839
if keyID == "" {
3940
if signed, err := isDistributionSigned(ctx, bucket, dist); err != nil {
4041
return err
@@ -43,8 +44,8 @@ func cmdInit(ctx context.Context, bucket *blob.Bucket, dist distribution, keyID
4344
}
4445
}
4546

46-
fmt.Fprintln(os.Stderr, "aptblob: reading Release from stdin...")
47-
newRelease, err := deb.ParseReleaseIndex(os.Stdin)
47+
fmt.Fprintln(stderr, "aptblob: reading Release from stdin...")
48+
newRelease, err := deb.ParseReleaseIndex(stdin)
4849
if err != nil {
4950
return fmt.Errorf("read stdin: %w", err)
5051
}
@@ -359,7 +360,7 @@ func main() {
359360
if err != nil {
360361
return err
361362
}
362-
return cmdInit(cmd.Context(), bucket, distribution(args[1]), *keyID)
363+
return cmdInit(cmd.Context(), bucket, os.Stdin, os.Stderr, distribution(args[1]), *keyID)
363364
},
364365
})
365366
uploadCmd := &cobra.Command{

aptblob_test.go

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,235 @@
1717
package main
1818

1919
import (
20+
"bytes"
21+
"context"
22+
"crypto/md5"
23+
"crypto/sha1"
24+
"crypto/sha256"
25+
"fmt"
26+
"hash"
27+
"io"
28+
"io/ioutil"
29+
"path/filepath"
30+
"strings"
2031
"testing"
2132

2233
"github.com/google/go-cmp/cmp"
34+
"github.com/google/go-cmp/cmp/cmpopts"
35+
"gocloud.dev/blob"
36+
"gocloud.dev/blob/memblob"
2337
"zombiezen.com/go/aptblob/internal/deb"
2438
)
2539

40+
const testReleaseKey = "dists/stable/Release"
41+
42+
func TestInit(t *testing.T) {
43+
want := deb.Paragraph{
44+
{Name: "Origin", Value: "stable"},
45+
{Name: "Label", Value: "stable"},
46+
{Name: "Codename", Value: "stable"},
47+
{Name: "Architectures", Value: "amd64"},
48+
{Name: "Description", Value: "Some apt repository"},
49+
}
50+
ctx := context.Background()
51+
bucket := memblob.OpenBucket(nil)
52+
stdin := strings.NewReader(want.String())
53+
err := cmdInit(ctx, bucket, stdin, ioutil.Discard, "stable", "")
54+
if err != nil {
55+
t.Error("init:", err)
56+
}
57+
got, _, err := listParagraphs(ctx, bucket, testReleaseKey, deb.ReleaseFields)
58+
if err != nil {
59+
t.Error(err)
60+
}
61+
ignoreOtherFields := cmpopts.IgnoreSliceElements(func(f deb.Field) bool {
62+
return want.Get(f.Name) == ""
63+
})
64+
if diff := cmp.Diff([]deb.Paragraph{want}, got, sortFields, ignoreOtherFields); diff != "" {
65+
t.Errorf("%s (-want +got)\n%s", testReleaseKey, diff)
66+
}
67+
}
68+
69+
func TestUpload(t *testing.T) {
70+
ctx := context.Background()
71+
bucket := memblob.OpenBucket(nil)
72+
err := cmdUpload(ctx, bucket, component{dist: "stable", name: "main"}, "", []string{
73+
filepath.Join("testdata", "nullpkg_1.0-1.dsc"),
74+
filepath.Join("testdata", "nullpkg_1.0-1_amd64.deb"),
75+
})
76+
if err != nil {
77+
t.Error("upload:", err)
78+
}
79+
80+
const packagesFilename = "main/binary-amd64/Packages"
81+
const packagesKey = "dists/stable/" + packagesFilename
82+
gotPackages, packagesData, err := listParagraphs(ctx, bucket, packagesKey, deb.ControlFields)
83+
if err != nil {
84+
t.Error(err)
85+
}
86+
wantPackages := []deb.Paragraph{
87+
{
88+
{Name: "Package", Value: "nullpkg"},
89+
{Name: "Version", Value: "1.0-1"},
90+
{Name: "Architecture", Value: "amd64"},
91+
{Name: "Section", Value: "misc"},
92+
{Name: "Priority", Value: "optional"},
93+
{Name: "Maintainer", Value: "Ross Light <ross@zombiezen.com>"},
94+
{Name: "Description", Value: "Do nothing\n Totally here just to do nothing"},
95+
{Name: "Installed-Size", Value: "7"},
96+
{Name: "Size", Value: "1124"},
97+
{Name: "MD5sum", Value: "bfd9f5f13f02ee8624835719d31ebdb7"},
98+
{Name: "SHA1", Value: "946b761b186ffcce7e457d6a75355c1450238859"},
99+
{Name: "SHA256", Value: "6def2db1420e3fc5528a3e1672af87158619ae27d82562c4c155154e68b393b7"},
100+
},
101+
}
102+
ignoreFilename := cmpopts.IgnoreSliceElements(func(f deb.Field) bool {
103+
return f.Name == "Filename"
104+
})
105+
if diff := cmp.Diff(wantPackages, gotPackages, sortFields, ignoreFilename); diff != "" {
106+
t.Errorf("%s (-want +got):\n%s", packagesKey, diff)
107+
}
108+
if len(gotPackages) == 1 {
109+
file := gotPackages[0].Get("Filename")
110+
if err := checkFile(ctx, bucket, file, "nullpkg_1.0-1_amd64.deb"); err != nil {
111+
t.Error(err)
112+
}
113+
}
114+
115+
const sourcesFilename = "main/source/Sources"
116+
const sourcesKey = "dists/stable/" + sourcesFilename
117+
gotSources, sourcesData, err := listParagraphs(ctx, bucket, sourcesKey, deb.SourceControlFields)
118+
if err != nil {
119+
t.Error(err)
120+
}
121+
wantSources := []deb.Paragraph{
122+
{
123+
{Name: "Package", Value: "nullpkg"},
124+
{Name: "Format", Value: "3.0 (quilt)"},
125+
{Name: "Binary", Value: "nullpkg"},
126+
{Name: "Architecture", Value: "any"},
127+
{Name: "Version", Value: "1.0-1"},
128+
{Name: "Maintainer", Value: "Ross Light <ross@zombiezen.com>"},
129+
{Name: "Standards-Version", Value: "3.9.2"},
130+
{Name: "Build-Depends", Value: "debhelper (>= 11)"},
131+
{Name: "Package-List", Value: "\n nullpkg deb misc optional arch=any"},
132+
{
133+
Name: "Checksums-Sha1",
134+
Value: "\n e77b8cd6a21289abd88d8195273a692b7a67cab5 121 nullpkg_1.0.orig.tar.gz" +
135+
"\n f949ed3c2b430378c6ba676a21c986c953c90521 640 nullpkg_1.0-1.debian.tar.xz",
136+
},
137+
{
138+
Name: "Checksums-Sha256",
139+
Value: "\n 5bf21f0c62248b89a88ef2f00296ef744c29e589dfb53e50eba55e928ad06e0c 121 nullpkg_1.0.orig.tar.gz" +
140+
"\n 35dc7a93ca59195bc7a897e785aaaba00935943a16815bdb263f348da3042bf5 640 nullpkg_1.0-1.debian.tar.xz",
141+
},
142+
{
143+
Name: "Files",
144+
Value: "\n 553a0f3ac9d12929fe08c01c3211fdb1 121 nullpkg_1.0.orig.tar.gz" +
145+
"\n bbd67ff58e0e03cc699ad83778e87081 640 nullpkg_1.0-1.debian.tar.xz",
146+
},
147+
},
148+
}
149+
ignoreDir := cmpopts.IgnoreSliceElements(func(f deb.Field) bool {
150+
return f.Name == "Directory"
151+
})
152+
if diff := cmp.Diff(wantSources, gotSources, sortFields, ignoreDir); diff != "" {
153+
t.Errorf("%s (-want +got):\n%s", sourcesKey, diff)
154+
}
155+
if len(gotSources) == 1 {
156+
dir := gotSources[0].Get("Directory")
157+
if err := checkFile(ctx, bucket, dir+"/nullpkg_1.0.orig.tar.gz", "nullpkg_1.0.orig.tar.gz"); err != nil {
158+
t.Error(err)
159+
}
160+
if err := checkFile(ctx, bucket, dir+"/nullpkg_1.0-1.debian.tar.xz", "nullpkg_1.0-1.debian.tar.xz"); err != nil {
161+
t.Error(err)
162+
}
163+
}
164+
165+
releaseData, err := bucket.ReadAll(ctx, testReleaseKey)
166+
release, err := deb.ParseReleaseIndex(bytes.NewReader(releaseData))
167+
if err != nil {
168+
t.Error(err)
169+
}
170+
ignoreOtherFiles := cmpopts.IgnoreSliceElements(func(sig deb.IndexSignature) bool {
171+
return sig.Filename != packagesFilename && sig.Filename != sourcesFilename
172+
})
173+
signatureTests := []struct {
174+
fieldName string
175+
newHash func() hash.Hash
176+
}{
177+
{"MD5Sum", md5.New},
178+
{"SHA1", sha1.New},
179+
{"SHA256", sha256.New},
180+
}
181+
for _, test := range signatureTests {
182+
want := []deb.IndexSignature{
183+
newIndexSignature(test.newHash(), sourcesData, sourcesFilename),
184+
newIndexSignature(test.newHash(), packagesData, packagesFilename),
185+
}
186+
got, err := deb.ParseIndexSignatures(release.Get(test.fieldName), test.newHash().Size())
187+
if err != nil {
188+
t.Errorf("%s: %v", test.fieldName, err)
189+
continue
190+
}
191+
if diff := cmp.Diff(want, got, sortSignatures, ignoreOtherFiles); diff != "" {
192+
t.Errorf("%s (-want +got):\n%s", test.fieldName, diff)
193+
}
194+
}
195+
}
196+
197+
func listParagraphs(ctx context.Context, b *blob.Bucket, key string, fields map[string]deb.FieldType) ([]deb.Paragraph, []byte, error) {
198+
r, err := b.NewReader(ctx, key, nil)
199+
if err != nil {
200+
return nil, nil, err
201+
}
202+
defer r.Close()
203+
buf := new(bytes.Buffer)
204+
tr := io.TeeReader(r, buf)
205+
p := deb.NewParser(tr)
206+
p.Fields = fields
207+
var got []deb.Paragraph
208+
for p.Next() {
209+
got = append(got, append(deb.Paragraph(nil), p.Paragraph()...))
210+
}
211+
if err := p.Err(); err != nil {
212+
return got, buf.Bytes(), fmt.Errorf("%s: %w", key, err)
213+
}
214+
return got, buf.Bytes(), nil
215+
}
216+
217+
func checkFile(ctx context.Context, b *blob.Bucket, key string, testdataPath string) error {
218+
got, err := b.ReadAll(ctx, key)
219+
if err != nil {
220+
return err
221+
}
222+
want, err := ioutil.ReadFile(filepath.Join("testdata", filepath.FromSlash(testdataPath)))
223+
if err != nil {
224+
return err
225+
}
226+
if !bytes.Equal(got, want) {
227+
return fmt.Errorf("%s does not match testdata/%s", key, testdataPath)
228+
}
229+
return nil
230+
}
231+
232+
var sortFields = cmpopts.SortSlices(func(f1, f2 deb.Field) bool {
233+
return f1.Name < f2.Name
234+
})
235+
236+
func newIndexSignature(h hash.Hash, data []byte, name string) deb.IndexSignature {
237+
h.Write(data)
238+
return deb.IndexSignature{
239+
Filename: name,
240+
Size: int64(len(data)),
241+
Checksum: h.Sum(nil),
242+
}
243+
}
244+
245+
var sortSignatures = cmpopts.SortSlices(func(sig1, sig2 deb.IndexSignature) bool {
246+
return sig1.Filename < sig2.Filename
247+
})
248+
26249
func TestDedupePackages(t *testing.T) {
27250
tests := []struct {
28251
name string
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
nullpkg (1.0-1) UNRELEASED; urgency=medium
2+
3+
* Initial release. (Closes: #XXXXXX)
4+
5+
-- Ross Light <ross@zombiezen.com> Sat, 05 Sep 2020 18:17:14 -0700

testdata/nullpkg-1.0/debian/compat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Source: nullpkg
2+
Maintainer: Ross Light <ross@zombiezen.com>
3+
Section: misc
4+
Priority: optional
5+
Standards-Version: 3.9.2
6+
Build-Depends: debhelper (>= 11)
7+
8+
Package: nullpkg
9+
Architecture: any
10+
Depends: ${shlibs:Depends}, ${misc:Depends}
11+
Description: Do nothing
12+
Totally here just to do nothing

testdata/nullpkg-1.0/debian/copyright

Whitespace-only changes.

testdata/nullpkg-1.0/debian/rules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/make -f
2+
%:
3+
dh $@
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.0 (quilt)
640 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)