8870cef95a
Signed-off-by: gernest <geofreyernest@live.com>
485 lines
12 KiB
Go
485 lines
12 KiB
Go
/*
|
|
Copyright 2017 Vector Creations Ltd
|
|
|
|
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)
|
|
}
|
|
}
|
|
}
|