Skip to content

Commit bb5ee30

Browse files
authored
Merge pull request #4 from NextronSystems/test/collect-and-upload
Test: collect and upload
2 parents ec73cde + 8471c74 commit bb5ee30

File tree

9 files changed

+298
-1
lines changed

9 files changed

+298
-1
lines changed

Diff for: .github/workflows/go.yml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Go
2+
on: [push]
3+
4+
jobs:
5+
test:
6+
runs-on: ubuntu-latest
7+
strategy:
8+
matrix:
9+
go-version: [ '1.15.x', 'stable' ]
10+
defaults:
11+
run:
12+
working-directory: ./go
13+
steps:
14+
- uses: actions/checkout@v4
15+
- name: Setup Go ${{ matrix.go-version }}
16+
uses: actions/setup-go@v5
17+
with:
18+
go-version: ${{ matrix.go-version }}
19+
cache-dependency-path: ./go/go.sum
20+
- name: Display Go version
21+
run: go version
22+
- name: Download dependencies
23+
run: go mod download
24+
- name: Test with Go
25+
run: go test -v -cover ./...

Diff for: go/Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ BUILD_ENV=GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0
1818
bin/%: $(wildcard *.go)
1919
$(BUILD_ENV) go build -v -o $@ -ldflags "-w -s"
2020

21+
test:
22+
go test -cover ./...
23+
2124
clean:
2225
rm -r bin
2326

24-
.PHONY: all clean
27+
.PHONY: all clean

Diff for: go/collector_test.go

+261
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"log"
9+
"mime"
10+
"mime/multipart"
11+
"net/http"
12+
"net/http/httptest"
13+
"os"
14+
"path/filepath"
15+
"reflect"
16+
"testing"
17+
"time"
18+
19+
"github.com/google/go-cmp/cmp"
20+
"github.com/google/uuid"
21+
)
22+
23+
type filedata struct {
24+
path string
25+
content []byte
26+
}
27+
28+
func (f filedata) String() string {
29+
return fmt.Sprintf("%s: %s", f.path, PrettyPrintBytes(f.content))
30+
}
31+
32+
func PrettyPrintBytes(b []byte) string {
33+
const maxLen = 40
34+
var curLen = len(b)
35+
ellipsis := ""
36+
if len(b) > maxLen {
37+
ellipsis = "..."
38+
curLen = maxLen
39+
}
40+
return fmt.Sprintf("%[1]q%[2]s (%[1]v%[2]s)", b[:curLen], ellipsis)
41+
}
42+
43+
func getFileName(file *multipart.Part) string {
44+
var fileName string
45+
disposition := file.Header.Get("Content-Disposition")
46+
_, dispositionParams, err := mime.ParseMediaType(disposition)
47+
if err == nil {
48+
fileName = dispositionParams["filename"]
49+
}
50+
return fileName
51+
}
52+
func collectSendAndReceive(cc CollectorConfig, t *testing.T) ([]filedata, CollectionStatistics) {
53+
receivedFiles := make([]filedata, 0)
54+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
55+
reader, err := r.MultipartReader()
56+
if err != nil {
57+
t.Fatalf("No multipart form received")
58+
http.Error(w, err.Error(), http.StatusBadRequest)
59+
return
60+
}
61+
part, err := reader.NextPart()
62+
if err == io.EOF {
63+
t.Fatalf("No file received")
64+
http.Error(w, "No file received", http.StatusBadRequest)
65+
return
66+
} else if err != nil {
67+
t.Fatalf("Invalid multipart form")
68+
http.Error(w, err.Error(), http.StatusBadRequest)
69+
return
70+
}
71+
if part.FormName() != "file" {
72+
t.Fatalf("Received upload with form name other than file")
73+
http.Error(w, "Please use \"file\" as name for your upload", http.StatusBadRequest)
74+
return
75+
}
76+
receivedFile := filedata{path: getFileName(part)}
77+
receivedFile.content, err = ioutil.ReadAll(part)
78+
if err != nil {
79+
t.Fatalf("Failed to read multipart file")
80+
http.Error(w, err.Error(), http.StatusBadRequest)
81+
return
82+
}
83+
receivedFiles = append(receivedFiles, receivedFile)
84+
85+
if err := json.NewEncoder(w).Encode(map[string]interface{}{"id": uuid.New().String()}); err != nil {
86+
t.Fatalf("Failed to write JSON response")
87+
http.Error(w, err.Error(), http.StatusInternalServerError)
88+
return
89+
}
90+
}))
91+
defer ts.Close()
92+
93+
logger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
94+
cc.Server = ts.URL
95+
cc.Threads = 1
96+
cc.Debug = true
97+
c := NewCollector(cc, logger)
98+
c.StartWorkers()
99+
c.Collect()
100+
c.Stop()
101+
102+
return receivedFiles, *c.Statistics
103+
}
104+
105+
func TestUpload(t *testing.T) {
106+
testStartTime := time.Now()
107+
testRoot := "testdata"
108+
109+
_ = os.Chtimes(filepath.Join(testRoot, "foo.txt"), time.Time{}, time.Now().Local()) // Touch
110+
111+
testCases := []struct {
112+
name string
113+
cc CollectorConfig
114+
expectedFilesFound []filedata
115+
expectedStats CollectionStatistics
116+
}{
117+
{
118+
"with excludes",
119+
CollectorConfig{
120+
RootPaths: []string{testRoot},
121+
ExcludeGlobs: []string{"**/sub?", "**/*.jpg"},
122+
},
123+
[]filedata{
124+
{"foo.txt", []byte("foo\n")},
125+
},
126+
CollectionStatistics{
127+
uploadedFiles: 1,
128+
skippedFiles: 1,
129+
skippedDirectories: 2,
130+
},
131+
},
132+
{
133+
"with extensions",
134+
CollectorConfig{
135+
RootPaths: []string{testRoot},
136+
FileExtensions: []string{".nfo"},
137+
},
138+
[]filedata{
139+
{"sub2/bar.nfo", []byte("bar\n")},
140+
},
141+
CollectionStatistics{
142+
uploadedFiles: 1,
143+
skippedFiles: 3,
144+
},
145+
},
146+
{
147+
"with magic",
148+
CollectorConfig{
149+
RootPaths: []string{testRoot},
150+
MagicHeaders: [][]byte{{0xff, 0xd8, 0xff}},
151+
},
152+
[]filedata{
153+
{"nextron250.jpg", func() []byte { b, _ := ioutil.ReadFile("testdata/nextron250.jpg"); return b }()},
154+
},
155+
CollectionStatistics{
156+
uploadedFiles: 1,
157+
skippedFiles: 3,
158+
},
159+
},
160+
{
161+
"with max age",
162+
CollectorConfig{
163+
RootPaths: []string{testRoot},
164+
ThresholdTime: testStartTime,
165+
},
166+
[]filedata{
167+
{"foo.txt", []byte("foo\n")},
168+
},
169+
CollectionStatistics{
170+
uploadedFiles: 1,
171+
skippedFiles: 3,
172+
},
173+
},
174+
{
175+
"with max size",
176+
CollectorConfig{
177+
RootPaths: []string{testRoot},
178+
FileExtensions: []string{".jpg"},
179+
MaxFileSize: 14670,
180+
},
181+
[]filedata{},
182+
CollectionStatistics{
183+
skippedFiles: 4,
184+
},
185+
},
186+
}
187+
188+
for _, tc := range testCases {
189+
t.Run(tc.name, func(t *testing.T) {
190+
receivedFiles, stats := collectSendAndReceive(tc.cc, t)
191+
192+
if len(receivedFiles) != len(tc.expectedFilesFound) {
193+
t.Fatalf("Expected to receive %d files, but received %d", len(tc.expectedFilesFound), len(receivedFiles))
194+
}
195+
if diff := cmp.Diff(tc.expectedStats, stats, cmp.Exporter(func(_ reflect.Type) bool { return true })); diff != "" {
196+
t.Fatalf("Statistics mismatch: %s", diff)
197+
}
198+
for _, expected := range tc.expectedFilesFound {
199+
t.Run(fmt.Sprintf("File %s", expected.path), func(t *testing.T) {
200+
found := false
201+
expectedPathRel := filepath.Join(testRoot, expected.path)
202+
expectedPathAbs, _ := filepath.Abs(expectedPathRel)
203+
for _, received := range receivedFiles {
204+
if received.path == expectedPathAbs {
205+
if !cmp.Equal(received.content, expected.content) {
206+
t.Fatalf("Content mismatch for file %s.\nExpected: %s\nGot: %s", received.path, PrettyPrintBytes(expected.content), PrettyPrintBytes(received.content))
207+
}
208+
found = true
209+
break
210+
}
211+
}
212+
if !found {
213+
t.Fatalf("Expected file %s not found in: %s", expectedPathAbs, receivedFiles)
214+
}
215+
})
216+
}
217+
})
218+
}
219+
}
220+
221+
func TestCollect(t *testing.T) {
222+
testRoot := "testdata"
223+
cc := CollectorConfig{
224+
RootPaths: []string{testRoot},
225+
ExcludeGlobs: []string{"**/sub1", "**/*.jpg"},
226+
}
227+
expectedFilesFound := []string{
228+
"foo.txt",
229+
"sub2/bar.nfo",
230+
}
231+
logger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
232+
c := NewCollector(cc, logger)
233+
c.filesToUpload = make(chan infoWithPath)
234+
235+
var listenerThreadClosed = make(chan struct{})
236+
var collectedFiles []infoWithPath
237+
go func() {
238+
for incFile := range c.filesToUpload {
239+
collectedFiles = append(collectedFiles, incFile)
240+
}
241+
close(listenerThreadClosed)
242+
}()
243+
c.Collect()
244+
close(c.filesToUpload)
245+
<-listenerThreadClosed
246+
247+
numFound := 0
248+
for _, collected := range collectedFiles {
249+
for _, expected := range expectedFilesFound {
250+
if collected.path == filepath.Join(testRoot, expected) {
251+
numFound++
252+
}
253+
}
254+
}
255+
if numFound != len(collectedFiles) {
256+
t.Fatalf("Expected to collect %d files, but collected %d. Expected: %v. Collected: %v", len(expectedFilesFound), len(collectedFiles), expectedFilesFound, collectedFiles)
257+
}
258+
if numFound != len(expectedFilesFound) {
259+
t.Fatalf("Expected to find %d files, but found %d. Expected: %v. Found: %v", len(expectedFilesFound), numFound, expectedFilesFound, collectedFiles)
260+
}
261+
}

Diff for: go/go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ go 1.15
44

55
require (
66
github.com/bmatcuk/doublestar/v3 v3.0.0
7+
github.com/google/go-cmp v0.6.0
8+
github.com/google/uuid v1.6.0
79
github.com/spf13/pflag v1.0.5
810
gopkg.in/yaml.v3 v3.0.0
911
)

Diff for: go/go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
github.com/bmatcuk/doublestar/v3 v3.0.0 h1:TQtVPlDnAYwcrVNB2JiGuMc++H5qzWZd9PhkNo5WyHI=
22
github.com/bmatcuk/doublestar/v3 v3.0.0/go.mod h1:6PcTVMw80pCY1RVuoqu3V++99uQB3vsSYKPTd8AWA0k=
3+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
4+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
5+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
6+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
37
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
48
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
59
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

Diff for: go/testdata/foo.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo

Diff for: go/testdata/nextron250.jpg

14.3 KB
Loading

Diff for: go/testdata/sub1/word.docx

Whitespace-only changes.

Diff for: go/testdata/sub2/bar.nfo

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bar

0 commit comments

Comments
 (0)