about summary refs log tree commit diff stats
path: root/registry/query_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'registry/query_test.go')
-rw-r--r--registry/query_test.go459
1 files changed, 459 insertions, 0 deletions
diff --git a/registry/query_test.go b/registry/query_test.go
new file mode 100644
index 0000000..7eed2cd
--- /dev/null
+++ b/registry/query_test.go
@@ -0,0 +1,459 @@
+/*
+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"
+	"os"
+	"strings"
+	"testing"
+	"time"
+)
+
+var queryUserCases = []struct {
+	name    string
+	term    string
+	wantErr bool
+}{
+	{
+		name:    "Valid User",
+		term:    "foo",
+		wantErr: false,
+	},
+	{
+		name:    "Empty Query",
+		term:    "",
+		wantErr: false,
+	},
+	{
+		name:    "Nonexistent User",
+		term:    "doesntexist",
+		wantErr: true,
+	},
+	{
+		name:    "Garbage Data",
+		term:    "will be replaced with garbage data",
+		wantErr: true,
+	},
+}
+
+// Checks if Registry.QueryUser() returns users that
+// match the provided substring.
+func Test_Registry_QueryUser(t *testing.T) {
+	registry := initTestEnv()
+	var buf = make([]byte, 256)
+	// read random data into case 8
+	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)
+	}
+	queryUserCases[3].term = string(buf)
+
+	for n, tt := range queryUserCases {
+
+		t.Run(tt.name, func(t *testing.T) {
+			out, err := registry.QueryUser(tt.term)
+
+			if out == nil && err != nil && !tt.wantErr {
+				t.Errorf("Received nil output or an error when unexpected. Case %v, %v, %v\n", n, tt.term, err)
+			}
+
+			if out != nil && tt.wantErr {
+				t.Errorf("Received unexpected nil output when an error was expected. Case %v, %v\n", n, tt.term)
+			}
+
+			for _, e := range out {
+				one := strings.Split(e, "\t")
+
+				if !strings.Contains(one[0], tt.term) && !strings.Contains(one[1], tt.term) {
+					t.Errorf("Received incorrect output: %v != %v\n", tt.term, e)
+				}
+			}
+		})
+	}
+}
+func Benchmark_Registry_QueryUser(b *testing.B) {
+	registry := initTestEnv()
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		for _, tt := range queryUserCases {
+			_, err := registry.QueryUser(tt.term)
+			if err != nil {
+				b.Errorf("%v\n", err)
+			}
+		}
+	}
+}
+
+var queryInStatusCases = []struct {
+	name    string
+	substr  string
+	wantNil bool
+	wantErr bool
+}{
+	{
+		name:    "Tag in Status",
+		substr:  "twtxt",
+		wantNil: false,
+		wantErr: false,
+	},
+	{
+		name:    "Valid URL",
+		substr:  "https://example.com/twtxt.txt",
+		wantNil: false,
+		wantErr: false,
+	},
+	{
+		name:    "Multiple Words in Status",
+		substr:  "next programming",
+		wantNil: false,
+		wantErr: false,
+	},
+	{
+		name:    "Multiple Words, Not in Status",
+		substr:  "explosive bananas from antarctica",
+		wantNil: true,
+		wantErr: false,
+	},
+	{
+		name:    "Empty Query",
+		substr:  "",
+		wantNil: true,
+		wantErr: true,
+	},
+	{
+		name:    "Nonsense",
+		substr:  "ahfiurrenkhfkajdhfao",
+		wantNil: true,
+		wantErr: false,
+	},
+	{
+		name:    "Invalid URL",
+		substr:  "https://doesnt.exist/twtxt.txt",
+		wantNil: true,
+		wantErr: false,
+	},
+	{
+		name:    "Garbage Data",
+		substr:  "will be replaced with garbage data",
+		wantNil: true,
+		wantErr: false,
+	},
+}
+
+// This tests whether we can find a substring in all of
+// the known status messages, disregarding the metadata
+// stored with each status.
+func Test_Registry_QueryInStatus(t *testing.T) {
+	registry := initTestEnv()
+	var buf = make([]byte, 256)
+	// read random data into case 8
+	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)
+	}
+	queryInStatusCases[7].substr = string(buf)
+
+	for _, tt := range queryInStatusCases {
+
+		t.Run(tt.name, func(t *testing.T) {
+
+			out, err := registry.QueryInStatus(tt.substr)
+			if err != nil && !tt.wantErr {
+				t.Errorf("Caught unexpected error: %v\n", err)
+			}
+
+			if !tt.wantErr && out == nil && !tt.wantNil {
+				t.Errorf("Got nil when expecting output\n")
+			}
+
+			if err == nil && tt.wantErr {
+				t.Errorf("Expecting error, got nil.\n")
+			}
+
+			for _, e := range out {
+				split := strings.Split(strings.ToLower(e), "\t")
+
+				if e != "" {
+					if !strings.Contains(split[3], strings.ToLower(tt.substr)) {
+						t.Errorf("Status without substring returned\n")
+					}
+				}
+			}
+		})
+	}
+
+}
+func Benchmark_Registry_QueryInStatus(b *testing.B) {
+	registry := initTestEnv()
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		for _, tt := range queryInStatusCases {
+			_, err := registry.QueryInStatus(tt.substr)
+			if err != nil {
+				continue
+			}
+		}
+	}
+}
+
+// Tests whether we can retrieve the 20 most
+// recent statuses in the registry
+func Test_QueryAllStatuses(t *testing.T) {
+	registry := initTestEnv()
+	t.Run("Latest Statuses", func(t *testing.T) {
+		out, err := registry.QueryAllStatuses()
+		if out == nil || err != nil {
+			t.Errorf("Got no statuses, or more than 20: %v, %v\n", len(out), err)
+		}
+	})
+}
+func Benchmark_QueryAllStatuses(b *testing.B) {
+	registry := initTestEnv()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		_, err := registry.QueryAllStatuses()
+		if err != nil {
+			continue
+		}
+	}
+}
+
+var get20cases = []struct {
+	name    string
+	page    int
+	wantErr bool
+}{
+	{
+		name:    "First Page",
+		page:    1,
+		wantErr: false,
+	},
+	{
+		name:    "High Page Number",
+		page:    256,
+		wantErr: false,
+	},
+	{
+		name:    "Illegal Page Number",
+		page:    -23,
+		wantErr: false,
+	},
+}
+
+func Test_ReduceToPage(t *testing.T) {
+	registry := initTestEnv()
+	for _, tt := range get20cases {
+		t.Run(tt.name, func(t *testing.T) {
+			out, err := registry.QueryAllStatuses()
+			if err != nil && !tt.wantErr {
+				t.Errorf("%v\n", err.Error())
+			}
+			out = ReduceToPage(tt.page, out)
+			if len(out) > 20 || len(out) == 0 {
+				t.Errorf("Page-Reduce Malfunction: length of data %v\n", len(out))
+			}
+		})
+	}
+}
+
+func Benchmark_ReduceToPage(b *testing.B) {
+	registry := initTestEnv()
+	out, _ := registry.QueryAllStatuses()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		for _, tt := range get20cases {
+			ReduceToPage(tt.page, out)
+		}
+	}
+}
+
+// This tests whether we can find a substring in the
+// given user's status messages, disregarding the metadata
+// stored with each status.
+func Test_User_FindInStatus(t *testing.T) {
+	registry := initTestEnv()
+	var buf = make([]byte, 256)
+	// read random data into case 8
+	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)
+	}
+	queryInStatusCases[7].substr = string(buf)
+
+	data := make([]*User, 0)
+
+	for _, v := range registry.Users {
+		data = append(data, v)
+	}
+
+	for _, tt := range queryInStatusCases {
+		t.Run(tt.name, func(t *testing.T) {
+			for _, e := range data {
+
+				tag := e.FindInStatus(tt.substr)
+				if tag == nil && !tt.wantNil {
+					t.Errorf("Got nil tag\n")
+				}
+			}
+		})
+	}
+
+}
+func Benchmark_User_FindInStatus(b *testing.B) {
+	registry := initTestEnv()
+	data := make([]*User, 0)
+
+	for _, v := range registry.Users {
+		data = append(data, v)
+	}
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		for _, tt := range data {
+			for _, v := range queryInStatusCases {
+				tt.FindInStatus(v.substr)
+			}
+		}
+	}
+}
+
+func Test_SortByTime_Slice(t *testing.T) {
+	registry := initTestEnv()
+
+	statusmap, err := registry.GetStatuses()
+	if err != nil {
+		t.Errorf("Failed to finish test initialization: %v\n", err)
+	}
+
+	t.Run("Sort By Time ([]TimeMap)", func(t *testing.T) {
+		sorted, err := SortByTime(statusmap)
+		if err != nil {
+			t.Errorf("%v\n", err)
+		}
+		split := strings.Split(sorted[0], "\t")
+		firsttime, _ := time.Parse("RFC3339", split[0])
+
+		for i := range sorted {
+			if i < len(sorted)-1 {
+
+				nextsplit := strings.Split(sorted[i+1], "\t")
+				nexttime, _ := time.Parse("RFC3339", nextsplit[0])
+
+				if firsttime.Before(nexttime) {
+					t.Errorf("Timestamps out of order: %v\n", sorted)
+				}
+
+				firsttime = nexttime
+			}
+		}
+	})
+}
+
+// Benchmarking a sort of 1000000 statuses by timestamp.
+// Right now it's at roughly 2000ns per 2 statuses.
+// Set sortMultiplier to be the number of desired
+// statuses divided by four.
+func Benchmark_SortByTime_Slice(b *testing.B) {
+	// I set this to 250,000,000 and it hard-locked
+	// my laptop. Oops.
+	sortMultiplier := 250
+	b.Logf("Benchmarking SortByTime with a constructed slice of %v statuses ...\n", sortMultiplier*4)
+	registry := initTestEnv()
+
+	statusmap, err := registry.GetStatuses()
+	if err != nil {
+		b.Errorf("Failed to finish benchmark initialization: %v\n", err)
+	}
+
+	// Constructed registry has four statuses. This
+	// makes a TimeMapSlice of 1000000 statuses.
+	statusmaps := make([]TimeMap, sortMultiplier*4)
+	for i := 0; i < sortMultiplier; i++ {
+		statusmaps = append(statusmaps, statusmap)
+	}
+
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		_, err := SortByTime(statusmaps...)
+		if err != nil {
+			b.Errorf("%v\n", err)
+		}
+	}
+}
+
+func Test_SortByTime_Single(t *testing.T) {
+	registry := initTestEnv()
+
+	statusmap, err := registry.GetStatuses()
+	if err != nil {
+		t.Errorf("Failed to finish test initialization: %v\n", err)
+	}
+
+	t.Run("Sort By Time (TimeMap)", func(t *testing.T) {
+		sorted, err := SortByTime(statusmap)
+		if err != nil {
+			t.Errorf("%v\n", err)
+		}
+		split := strings.Split(sorted[0], "\t")
+		firsttime, _ := time.Parse("RFC3339", split[0])
+
+		for i := range sorted {
+			if i < len(sorted)-1 {
+
+				nextsplit := strings.Split(sorted[i+1], "\t")
+				nexttime, _ := time.Parse("RFC3339", nextsplit[0])
+
+				if firsttime.Before(nexttime) {
+					t.Errorf("Timestamps out of order: %v\n", sorted)
+				}
+
+				firsttime = nexttime
+			}
+		}
+	})
+}
+
+func Benchmark_SortByTime_Single(b *testing.B) {
+	registry := initTestEnv()
+
+	statusmap, err := registry.GetStatuses()
+	if err != nil {
+		b.Errorf("Failed to finish benchmark initialization: %v\n", err)
+	}
+
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		_, err := SortByTime(statusmap)
+		if err != nil {
+			b.Errorf("%v\n", err)
+		}
+	}
+}