summary refs log tree commit diff stats
path: root/registry/query_test.go
diff options
context:
space:
mode:
authorBen Morrison <ben@gbmor.dev>2020-06-20 02:27:31 -0400
committerBen Morrison <ben@gbmor.dev>2020-06-20 02:27:31 -0400
commit538e305925b9b04102ef0a4fb7cca19a6c116142 (patch)
tree8e68b426c40c3151f39da1be874d2938f31ab5f9 /registry/query_test.go
parent0a69c582ec8b88b1d3af70ef43c3eeb1b99f973d (diff)
downloadgetwtxt-538e305925b9b04102ef0a4fb7cca19a6c116142.tar.gz
updating module to live at sourcehut
Also moving the 'registry' library into this repo, rather
than maintaining them separately. It will still be decoupled,
just live in this repository.
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)
+		}
+	}
+}