package web import ( "archive/zip" "bytes" "mime/multipart" "net/http" "net/http/httptest" "strings" "testing" "time" "pcap-web-tools/internal/pcap" ) func testPcap(t *testing.T, packets int) []byte { t.Helper() var buf bytes.Buffer w, err := pcap.NewWriter(&buf, pcap.LinkTypeEthernet) if err != nil { t.Fatal(err) } for i := range packets { if err := w.WritePacket(time.Unix(int64(i), 0), make([]byte, 100)); err != nil { t.Fatal(err) } } return buf.Bytes() } func postUpload(t *testing.T, srv *Server, target, filename string, file []byte, fields map[string]string) *httptest.ResponseRecorder { t.Helper() var body bytes.Buffer mw := multipart.NewWriter(&body) for k, v := range fields { mw.WriteField(k, v) } if file != nil { fw, err := mw.CreateFormFile("file", filename) if err != nil { t.Fatal(err) } fw.Write(file) } mw.Close() req := httptest.NewRequest("POST", target, &body) req.Header.Set("Content-Type", mw.FormDataContentType()) rec := httptest.NewRecorder() srv.ServeHTTP(rec, req) return rec } func TestSplitHandler(t *testing.T) { srv, err := New(Config{MaxUploadBytes: 1 << 20}) if err != nil { t.Fatal(err) } in := testPcap(t, 25) tests := []struct { name string fields map[string]string want []int // packets per part }{ {"by packets", map[string]string{"mode": "packets", "packets": "10"}, []int{10, 10, 5}}, // Each record is 116 bytes: 24 + 8*116 = 952 <= 1000, a ninth does not fit. {"by size", map[string]string{"mode": "size", "size": "1", "unit": "KB"}, []int{8, 8, 8, 1}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { rec := postUpload(t, srv, "/split", `C:\caps\my capture.pcap`, in, tt.fields) if rec.Code != http.StatusOK || rec.Header().Get("Content-Type") != "application/zip" { t.Fatalf("status %d %s: %s", rec.Code, rec.Header().Get("Content-Type"), rec.Body) } if cd := rec.Header().Get("Content-Disposition"); cd != `attachment; filename="my_capture_split.zip"` { t.Errorf("Content-Disposition %q", cd) } zr, err := zip.NewReader(bytes.NewReader(rec.Body.Bytes()), int64(rec.Body.Len())) if err != nil { t.Fatal(err) } if len(zr.File) != len(tt.want) { t.Fatalf("got %d files, want %d", len(zr.File), len(tt.want)) } for i, f := range zr.File { if want := "my_capture_00" + string(rune('1'+i)) + ".pcap"; f.Name != want { t.Errorf("file %d named %q, want %q", i, f.Name, want) } if want := int64(24 + tt.want[i]*116); int64(f.UncompressedSize64) != want { t.Errorf("%s: %d bytes, want %d", f.Name, f.UncompressedSize64, want) } } }) } } func TestSplitHandlerErrors(t *testing.T) { srv, err := New(Config{MaxUploadBytes: 4000}) if err != nil { t.Fatal(err) } defer func(n int) { maxSplitParts = n }(maxSplitParts) maxSplitParts = 5 ok := map[string]string{"mode": "packets", "packets": "1"} tests := []struct { name string file []byte fields map[string]string status int want string }{ {"no file", nil, ok, http.StatusBadRequest, "choose a pcap file"}, {"not pcap", []byte(strings.Repeat("x", 100)), ok, http.StatusBadRequest, "not a pcap file"}, {"too large", testPcap(t, 700), ok, http.StatusRequestEntityTooLarge, "file too large"}, {"too many parts", testPcap(t, 6), ok, http.StatusBadRequest, "more than 5 files"}, {"bad packets", testPcap(t, 1), map[string]string{"mode": "packets", "packets": "0"}, http.StatusBadRequest, "at least 1"}, {"bad size", testPcap(t, 1), map[string]string{"mode": "size", "size": "abc", "unit": "MB"}, http.StatusBadRequest, "positive number"}, {"bad unit", testPcap(t, 1), map[string]string{"mode": "size", "size": "1", "unit": "XB"}, http.StatusBadRequest, "unknown size unit"}, {"no mode", testPcap(t, 1), map[string]string{}, http.StatusBadRequest, "choose whether"}, } for _, tt := range tests { rec := postUpload(t, srv, "/split", "x.pcap", tt.file, tt.fields) if rec.Code != tt.status || !strings.Contains(rec.Body.String(), tt.want) { t.Errorf("%s: status %d, body %.300s", tt.name, rec.Code, rec.Body) } } }