about summary refs log tree commit diff stats
path: root/registry/fetch_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'registry/fetch_test.go')
-rw-r--r--registry/fetch_test.go286
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)
+			}
+		})
+	}
+}