diff options
Diffstat (limited to 'registry/fetch_test.go')
-rw-r--r-- | registry/fetch_test.go | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/registry/fetch_test.go b/registry/fetch_test.go new file mode 100644 index 0000000..4eab2a4 --- /dev/null +++ b/registry/fetch_test.go @@ -0,0 +1,286 @@ +/* +Copyright (c) 2019 Ben Morrison (gbmor) + +This file is part of Registry. + +Registry is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Registry is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Registry. If not, see <https://www.gnu.org/licenses/>. +*/ + +package registry + +import ( + "bufio" + "fmt" + "net/http" + "os" + "strings" + "testing" + "time" +) + +func constructTwtxt() []byte { + registry := initTestEnv() + var resp []byte + // iterates through each mock user's mock statuses + for _, v := range registry.Users { + for _, e := range v.Status { + split := strings.Split(e, "\t") + status := []byte(split[2] + "\t" + split[3] + "\n") + resp = append(resp, status...) + } + } + return resp +} + +// this is just dumping all the mock statuses. +// it'll be served under fake paths as +// "remote" twtxt.txt files +func twtxtHandler(w http.ResponseWriter, _ *http.Request) { + // prepare the response + resp := constructTwtxt() + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + n, err := w.Write(resp) + if err != nil || n == 0 { + fmt.Printf("Got error or wrote zero bytes: %v bytes, %v\n", n, err) + } +} + +var getTwtxtCases = []struct { + name string + url string + wantErr bool + localOnly bool +}{ + { + name: "Constructed Local twtxt.txt", + url: "http://localhost:8080/twtxt.txt", + wantErr: false, + localOnly: true, + }, + { + name: "Inaccessible Site With twtxt.txt", + url: "https://example33333333333.com/twtxt.txt", + wantErr: true, + localOnly: false, + }, + { + name: "Inaccessible Site Without twtxt.txt", + url: "https://example333333333333.com", + wantErr: true, + localOnly: false, + }, + { + name: "Local File Inclusion 1", + url: "file://init_test.go", + wantErr: true, + localOnly: false, + }, + { + name: "Local File Inclusion 2", + url: "/etc/passwd", + wantErr: true, + localOnly: false, + }, + { + name: "Remote File Inclusion", + url: "https://example.com/file.cgi", + wantErr: true, + localOnly: false, + }, + { + name: "Remote Registry", + url: "https://twtxt.tilde.institute/api/plain/tweets/", + wantErr: false, + localOnly: false, + }, + { + name: "Garbage Data", + url: "this will be replaced with garbage data", + wantErr: true, + localOnly: true, + }, +} + +// Test the function that yoinks the /twtxt.txt file +// for a given user. +func Test_GetTwtxt(t *testing.T) { + var buf = make([]byte, 256) + // read random data into case 4 + rando, _ := os.Open("/dev/random") + reader := bufio.NewReader(rando) + n, err := reader.Read(buf) + if err != nil || n == 0 { + t.Errorf("Couldn't set up test: %v\n", err) + } + getTwtxtCases[7].url = string(buf) + + if !getTwtxtCases[0].localOnly { + http.Handle("/twtxt.txt", http.HandlerFunc(twtxtHandler)) + go fmt.Println(http.ListenAndServe(":8080", nil)) + } + + for _, tt := range getTwtxtCases { + t.Run(tt.name, func(t *testing.T) { + if tt.localOnly { + t.Skipf("Local-only test. Skipping ... \n") + } + out, _, err := GetTwtxt(tt.url, nil) + if tt.wantErr && err == nil { + t.Errorf("Expected error: %v\n", tt.url) + } + if !tt.wantErr && err != nil { + t.Errorf("Unexpected error: %v %v\n", tt.url, err) + } + if !tt.wantErr && out == nil { + t.Errorf("Incorrect data received: %v\n", out) + } + }) + } + +} + +// running the benchmarks separately for each case +// as they have different properties (allocs, time) +func Benchmark_GetTwtxt(b *testing.B) { + + for i := 0; i < b.N; i++ { + _, _, err := GetTwtxt("https://gbmor.dev/twtxt.txt", nil) + if err != nil { + continue + } + } +} + +var parseTwtxtCases = []struct { + name string + data []byte + wantErr bool + localOnly bool +}{ + { + name: "Constructed twtxt file", + data: constructTwtxt(), + wantErr: false, + localOnly: false, + }, + { + name: "Incorrectly formatted date", + data: []byte("2019 April 23rd\tI love twtxt!!!11"), + wantErr: true, + localOnly: false, + }, + { + name: "No data", + data: []byte{}, + wantErr: true, + localOnly: false, + }, + { + name: "Variant rfc3339 datestamp", + data: []byte("2020-02-04T21:28:21.868659+00:00\tWill this work?"), + wantErr: false, + localOnly: false, + }, + { + name: "Random/garbage data", + wantErr: true, + localOnly: true, + }, +} + +// See if we can break ParseTwtxt or get it +// to throw an unexpected error +func Test_ParseUserTwtxt(t *testing.T) { + var buf = make([]byte, 256) + // read random data into case 4 + rando, _ := os.Open("/dev/random") + reader := bufio.NewReader(rando) + n, err := reader.Read(buf) + if err != nil || n == 0 { + t.Errorf("Couldn't set up test: %v\n", err) + } + parseTwtxtCases[4].data = buf + + for _, tt := range parseTwtxtCases { + if tt.localOnly { + t.Skipf("Local-only test: Skipping ... \n") + } + t.Run(tt.name, func(t *testing.T) { + timemap, errs := ParseUserTwtxt(tt.data, "testuser", "testurl") + if errs == nil && tt.wantErr { + t.Errorf("Expected error(s), received none.\n") + } + + if !tt.wantErr { + if errs != nil { + t.Errorf("Unexpected error: %v\n", errs) + } + + for k, v := range timemap { + if k == (time.Time{}) || v == "" { + t.Errorf("Empty status or empty timestamp: %v, %v\n", k, v) + } + } + } + }) + } +} + +func Benchmark_ParseUserTwtxt(b *testing.B) { + var buf = make([]byte, 256) + // read random data into case 4 + rando, _ := os.Open("/dev/random") + reader := bufio.NewReader(rando) + n, err := reader.Read(buf) + if err != nil || n == 0 { + b.Errorf("Couldn't set up benchmark: %v\n", err) + } + parseTwtxtCases[3].data = buf + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for _, tt := range parseTwtxtCases { + _, _ = ParseUserTwtxt(tt.data, "testuser", "testurl") + } + } +} + +var timestampCases = []struct { + name string + orig string + expected string +}{ + { + name: "Timezone appended", + orig: "2020-01-13T16:08:25.544735+00:00", + expected: "2020-01-13T16:08:25.544735Z", + }, + { + name: "It's fine already", + orig: "2020-01-14T00:19:45.092344Z", + expected: "2020-01-14T00:19:45.092344Z", + }, +} + +func Test_fixTimestamp(t *testing.T) { + for _, tt := range timestampCases { + t.Run(tt.name, func(t *testing.T) { + tsout := fixTimestamp(tt.orig) + if tsout != tt.expected { + t.Errorf("Failed :: %s :: got %s expected %s", tt.name, tsout, tt.expected) + } + }) + } +} |