This repository has been archived on 2022-08-18. You can view files and clone it, but cannot push or open issues or pull requests.
rageshake/submit_test.go

504 lines
13 KiB
Go
Raw Normal View History

/*
Copyright 2017 Vector Creations Ltd
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"bytes"
"compress/gzip"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
)
// testParsePayload builds a /submit request with the given body, and calls
// parseRequest with it.
//
// if tempDir is empty, a new temp dir is created, and deleted when the test
// completes.
func testParsePayload(t *testing.T, body, contentType string, tempDir string) (*parsedPayload, *http.Response) {
req, err := http.NewRequest("POST", "/api/submit", strings.NewReader(body))
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Length", strconv.Itoa(len(body)))
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
// temporary dir for the uploaded files
if tempDir == "" {
tempDir = mkTempDir(t)
defer os.RemoveAll(tempDir)
}
rr := httptest.NewRecorder()
p := parseRequest(rr, req, tempDir)
return p, rr.Result()
}
func TestEmptyJson(t *testing.T) {
body := "{}"
// we just test it is parsed without errors for now
p, _ := testParsePayload(t, body, "application/json", "")
if p == nil {
t.Fatal("parseRequest returned nil")
}
if len(p.Labels) != 0 {
t.Errorf("Labels: got %#v, want []", p.Labels)
}
}
func TestJsonUpload(t *testing.T) {
reportDir := mkTempDir(t)
defer os.RemoveAll(reportDir)
body := `{
"app": "riot-web",
"logs": [
{
"id": "instance-0.99152119701215051494400738905",
"lines": "line1\nline2"
}
],
"text": "test message",
"user_agent": "Mozilla",
"version": "0.9.9"
}`
p, _ := testParsePayload(t, body, "application/json", reportDir)
if p == nil {
t.Fatal("parseRequest returned nil")
}
wanted := "test message"
if p.UserText != wanted {
t.Errorf("user text: got %s, want %s", p.UserText, wanted)
}
wanted = "riot-web"
if p.AppName != wanted {
t.Errorf("appname: got %s, want %s", p.AppName, wanted)
}
wanted = "0.9.9"
if p.Data["Version"] != wanted {
t.Errorf("version: got %s, want %s", p.Data["Version"], wanted)
}
checkUploadedFile(t, reportDir, "logs-0000.log.gz", true, "line1\nline2")
}
// check that we can unpick the json submitted by the android clients
func TestUnpickAndroidMangling(t *testing.T) {
body := `{"text": "test ylc 001",
"version": "User : @ylc8001:matrix.org\nPhone : Lenovo P2a42\nVector version: 0:6:9\n",
"user_agent": "Android"
}`
p, _ := testParsePayload(t, body, "", "")
if p == nil {
t.Fatal("parseRequest returned nil")
}
if p.UserText != "test ylc 001" {
t.Errorf("user text: got %s, want %s", p.UserText, "test ylc 001")
}
if p.AppName != "riot-android" {
t.Errorf("appname: got %s, want %s", p.AppName, "riot-android")
}
if p.Data["Version"] != "" {
t.Errorf("version: got %s, want ''", p.Data["Version"])
}
if p.Data["User"] != "@ylc8001:matrix.org" {
t.Errorf("data.user: got %s, want %s", p.Data["User"], "@ylc8001:matrix.org")
}
if p.Data["Phone"] != "Lenovo P2a42" {
t.Errorf("data.phone: got %s, want %s", p.Data["Phone"], "Lenovo P2a42")
}
if p.Data["Vector version"] != "0:6:9" {
t.Errorf("data.version: got %s, want %s", p.Data["Version"], "0:6:9")
}
}
func TestMultipartUpload(t *testing.T) {
reportDir := mkTempDir(t)
defer os.RemoveAll(reportDir)
p, _ := testParsePayload(t, multipartBody(),
"multipart/form-data; boundary=----WebKitFormBoundarySsdgl8Nq9voFyhdO",
reportDir,
)
if p == nil {
t.Fatal("parseRequest returned nil")
}
checkParsedMultipartUpload(t, p)
// check logs uploaded correctly
checkUploadedFile(t, reportDir, "logs-0000.log.gz", true, "log\nlog\nlog")
checkUploadedFile(t, reportDir, "console.0.log.gz", true, "log")
checkUploadedFile(t, reportDir, "logs-0002.log.gz", true, "test\n")
// check file uploaded correctly
checkUploadedFile(t, reportDir, "passwd.txt", false, "bibblybobbly")
}
func multipartBody() (body string) {
body = `------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="text"
test words.
------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="app"
riot-web
------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="version"
UNKNOWN
------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="user_agent"
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="test-field"
Test data
------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="log"; filename="instance-0.215954445471346461492087122412"
Content-Type: text/plain
log
log
log
------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="log"; filename="console.0.log"
Content-Type: text/plain
log
`
body += `------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="compressed-log"; filename="instance-0.0109372050779190651492004373866"
Content-Type: application/octet-stream
`
body += string([]byte{
0x1f, 0x8b, 0x08, 0x00, 0xbf, 0xd8, 0xf5, 0x58, 0x00, 0x03,
0x2b, 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00,
0xc6, 0x35, 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00,
0x0a,
})
body += `------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="file"; filename="passwd.txt"
Content-Type: application/octet-stream
bibblybobbly
`
body += "------WebKitFormBoundarySsdgl8Nq9voFyhdO--\n"
return
}
func checkParsedMultipartUpload(t *testing.T, p *parsedPayload) {
wanted := "test words."
if p.UserText != wanted {
t.Errorf("User text: got %s, want %s", p.UserText, wanted)
}
if len(p.Logs) != 3 {
t.Errorf("Log length: got %d, want 3", len(p.Logs))
}
if len(p.Data) != 3 {
t.Errorf("Data length: got %d, want 3", len(p.Data))
}
if len(p.Labels) != 0 {
t.Errorf("Labels: got %#v, want []", p.Labels)
}
wanted = "Test data"
if p.Data["test-field"] != wanted {
t.Errorf("test-field: got %s, want %s", p.Data["test-field"], wanted)
}
wanted = "logs-0000.log.gz"
if p.Logs[0] != wanted {
t.Errorf("Log 0: got %s, want %s", p.Logs[0], wanted)
}
wanted = "console.0.log.gz"
if p.Logs[1] != wanted {
t.Errorf("Log 1: got %s, want %s", p.Logs[1], wanted)
}
wanted = "logs-0002.log.gz"
if p.Logs[2] != wanted {
t.Errorf("Log 2: got %s, want %s", p.Logs[2], wanted)
}
}
func TestLabels(t *testing.T) {
body := `------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="label"
label1
------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="label"
label2
------WebKitFormBoundarySsdgl8Nq9voFyhdO--
`
p, _ := testParsePayload(t, body,
"multipart/form-data; boundary=----WebKitFormBoundarySsdgl8Nq9voFyhdO",
"",
)
if p == nil {
t.Fatal("parseRequest returned nil")
}
wantedLabels := []string{"label1", "label2"}
if !stringSlicesEqual(p.Labels, wantedLabels) {
t.Errorf("Labels: got %v, want %v", p.Labels, wantedLabels)
}
}
func stringSlicesEqual(got, want []string) bool {
if len(got) != len(want) {
return false
}
for i := range got {
if got[i] != want[i] {
return false
}
}
return true
}
/* FIXME these should just give a message in the details file now
func TestEmptyFilename(t *testing.T) {
body := `------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="file"
file
------WebKitFormBoundarySsdgl8Nq9voFyhdO--
`
p, resp := testParsePayload(t, body, "multipart/form-data; boundary=----WebKitFormBoundarySsdgl8Nq9voFyhdO", "")
if p != nil {
t.Error("parsePayload accepted upload with no filename")
}
if resp.StatusCode != 400 {
t.Errorf("response code: got %v, want %v", resp.StatusCode, 400)
}
}
func TestBadFilename(t *testing.T) {
body := `------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="file"; filename="etc/passwd"
file
------WebKitFormBoundarySsdgl8Nq9voFyhdO--
`
p, resp := testParsePayload(t, body, "multipart/form-data; boundary=----WebKitFormBoundarySsdgl8Nq9voFyhdO", "")
if p != nil {
t.Error("parsePayload accepted upload with bad filename")
}
if resp.StatusCode != 400 {
t.Errorf("response code: got %v, want %v", resp.StatusCode, 400)
}
}
*/
func checkUploadedFile(t *testing.T, reportDir, leafName string, gzipped bool, wanted string) {
fi, err := os.Open(filepath.Join(reportDir, leafName))
if err != nil {
t.Errorf("unable to open uploaded file %s: %v", leafName, err)
return
}
defer fi.Close()
var rdr io.Reader
if !gzipped {
rdr = fi
} else {
gz, err2 := gzip.NewReader(fi)
if err2 != nil {
t.Errorf("unable to ungzip uploaded file %s: %v", leafName, err2)
return
}
defer gz.Close()
rdr = gz
}
dat, err := ioutil.ReadAll(rdr)
if err != nil {
t.Errorf("unable to read uploaded file %s: %v", leafName, err)
return
}
datstr := string(dat)
if datstr != wanted {
t.Errorf("File %s: got %s, want %s", leafName, datstr, wanted)
}
}
func mkTempDir(t *testing.T) string {
td, err := ioutil.TempDir("", "rageshake_test")
if err != nil {
t.Fatal(err)
}
return td
}
/*****************************************************************************
*
* buildGithubIssueRequest tests
*/
func TestBuildGithubIssueLeadingNewline(t *testing.T) {
body := `------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="text"
test words.
------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="app"
riot-web
------WebKitFormBoundarySsdgl8Nq9voFyhdO--
`
p, _ := testParsePayload(t, body,
"multipart/form-data; boundary=----WebKitFormBoundarySsdgl8Nq9voFyhdO",
"",
)
if p == nil {
t.Fatal("parseRequest returned nil")
}
issueReq := buildGithubIssueRequest(*p, "http://test/listing/foo")
if *issueReq.Title != "test words." {
t.Errorf("Title: got %s, want %s", *issueReq.Title, "test words.")
}
expectedBody := "User message:\n\n\ntest words.\n"
if !strings.HasPrefix(*issueReq.Body, expectedBody) {
t.Errorf("Body: got %s, want %s", *issueReq.Body, expectedBody)
}
}
func TestBuildGithubIssueEmptyBody(t *testing.T) {
body := `------WebKitFormBoundarySsdgl8Nq9voFyhdO
Content-Disposition: form-data; name="text"
------WebKitFormBoundarySsdgl8Nq9voFyhdO--
`
p, _ := testParsePayload(t, body,
"multipart/form-data; boundary=----WebKitFormBoundarySsdgl8Nq9voFyhdO",
"",
)
if p == nil {
t.Fatal("parseRequest returned nil")
}
issueReq := buildGithubIssueRequest(*p, "http://test/listing/foo")
if *issueReq.Title != "Untitled report" {
t.Errorf("Title: got %s, want %s", *issueReq.Title, "Untitled report")
}
expectedBody := "User message:\n\n\n"
if !strings.HasPrefix(*issueReq.Body, expectedBody) {
t.Errorf("Body: got %s, want %s", *issueReq.Body, expectedBody)
}
}
func TestTestSortDataKeys(t *testing.T) {
expect := `
Number of logs: 0
Application:
Labels:
User-Agent: xxx
Version: 1
device_id: id
user_id: id
`
expect = strings.TrimSpace(expect)
sample := []struct {
data map[string]string
}{
{
map[string]string{
"Version": "1",
"User-Agent": "xxx",
"user_id": "id",
"device_id": "id",
},
},
{
map[string]string{
"user_id": "id",
"device_id": "id",
"Version": "1",
"User-Agent": "xxx",
},
},
}
var buf bytes.Buffer
for _, v := range sample {
p := parsedPayload{Data: v.data}
buf.Reset()
p.WriteTo(&buf)
got := strings.TrimSpace(buf.String())
if got != expect {
t.Errorf("expected %s got %s", expect, got)
}
}
for k, v := range sample {
p := parsedPayload{Data: v.data}
res := buildGithubIssueRequest(p, "")
got := *res.Body
if k == 0 {
expect = got
continue
}
if got != expect {
t.Errorf("expected %s got %s", expect, got)
}
}
}
func TestAutocompleteIssueReferences(t *testing.T) {
tests := map[string]string{
"Testing #123 Foobar": "Testing owner/repo#123 Foobar", // standard
"#123": "owner/repo#123", // first/last word
"test (#123) bar": "test (owner/repo#123) bar", // brackets
"Start #123. Now": "Start owner/repo#123. Now", // followed by punctuation
"#123foo": "#123foo", // ignore
}
for text, expect := range tests {
got := replaceAmbiguousIssueReferences("owner/repo", text)
if expect != got {
t.Errorf("expected %s got %s", expect, got)
}
}
}