added concurrent search test
All checks were successful
Run Integration Tests / test (push) Successful in 28s
All checks were successful
Run Integration Tests / test (push) Successful in 28s
This commit is contained in:
parent
7498f4128a
commit
28a721887c
1 changed files with 141 additions and 12 deletions
|
@ -3,12 +3,17 @@ package tests
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -22,6 +27,7 @@ type TestSummary struct {
|
||||||
CPUUsage []float64
|
CPUUsage []float64
|
||||||
RAMUsage []uint64
|
RAMUsage []uint64
|
||||||
CacheTests []CacheTestResult
|
CacheTests []CacheTestResult
|
||||||
|
ConcurrentTestStats ConcurrentTestStats
|
||||||
}
|
}
|
||||||
|
|
||||||
type CacheTestResult struct {
|
type CacheTestResult struct {
|
||||||
|
@ -31,9 +37,21 @@ type CacheTestResult struct {
|
||||||
IsCacheEffective bool
|
IsCacheEffective bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConcurrentTestStats struct {
|
||||||
|
TotalRequests int
|
||||||
|
TotalFailures int
|
||||||
|
RequestsPerType map[string]int
|
||||||
|
FailuresPerType map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplication(t *testing.T) {
|
func TestApplication(t *testing.T) {
|
||||||
// Initialize test summary
|
// Initialize test summary
|
||||||
summary := &TestSummary{}
|
summary := &TestSummary{
|
||||||
|
ConcurrentTestStats: ConcurrentTestStats{
|
||||||
|
RequestsPerType: make(map[string]int),
|
||||||
|
FailuresPerType: make(map[string]int),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the test runs from the root directory
|
// Ensure the test runs from the root directory
|
||||||
rootDir := "../" // Path to the root directory of the repository
|
rootDir := "../" // Path to the root directory of the repository
|
||||||
|
@ -136,6 +154,9 @@ func TestApplication(t *testing.T) {
|
||||||
runTest(t, summary, "Test Search Types and Cache Effectiveness", func(t *testing.T) {
|
runTest(t, summary, "Test Search Types and Cache Effectiveness", func(t *testing.T) {
|
||||||
testSearchTypesAndCache(t, summary)
|
testSearchTypesAndCache(t, summary)
|
||||||
})
|
})
|
||||||
|
runTest(t, summary, "Test Concurrent Random Requests", func(t *testing.T) {
|
||||||
|
testConcurrentRandomRequests(t, summary)
|
||||||
|
})
|
||||||
runTest(t, summary, "Check Resource Usage After Tests", func(t *testing.T) {
|
runTest(t, summary, "Check Resource Usage After Tests", func(t *testing.T) {
|
||||||
checkResourceUsage(t, summary, appProcess)
|
checkResourceUsage(t, summary, appProcess)
|
||||||
})
|
})
|
||||||
|
@ -187,6 +208,14 @@ func printSummary(summary *TestSummary, t *testing.T) {
|
||||||
t.Logf(" Cache Effective: No")
|
t.Logf(" Cache Effective: No")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Logf("\nConcurrent Random Requests Test Stats:")
|
||||||
|
t.Logf(" Total Requests Sent: %d", summary.ConcurrentTestStats.TotalRequests)
|
||||||
|
t.Logf(" Total Failures: %d", summary.ConcurrentTestStats.TotalFailures)
|
||||||
|
for stype, reqCount := range summary.ConcurrentTestStats.RequestsPerType {
|
||||||
|
failCount := summary.ConcurrentTestStats.FailuresPerType[stype]
|
||||||
|
t.Logf(" Type '%s': %d requests, %d failures", stype, reqCount, failCount)
|
||||||
|
}
|
||||||
t.Logf("\n======================\n")
|
t.Logf("\n======================\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +228,6 @@ func checkResourceUsage(t *testing.T, summary *TestSummary, appProcess *process.
|
||||||
} else {
|
} else {
|
||||||
// Sum the CPU usage of the main process and its children
|
// Sum the CPU usage of the main process and its children
|
||||||
totalCPU := cpuPercent
|
totalCPU := cpuPercent
|
||||||
// Get child processes
|
|
||||||
children, err := appProcess.Children()
|
children, err := appProcess.Children()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("Failed to get child processes: %v", err)
|
t.Logf("Failed to get child processes: %v", err)
|
||||||
|
@ -224,7 +252,6 @@ func checkResourceUsage(t *testing.T, summary *TestSummary, appProcess *process.
|
||||||
summary.RAMUsage = append(summary.RAMUsage, 0)
|
summary.RAMUsage = append(summary.RAMUsage, 0)
|
||||||
} else {
|
} else {
|
||||||
totalRAM := memInfo.RSS
|
totalRAM := memInfo.RSS
|
||||||
// Get memory info for child processes
|
|
||||||
children, err := appProcess.Children()
|
children, err := appProcess.Children()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("Failed to get child processes: %v", err)
|
t.Logf("Failed to get child processes: %v", err)
|
||||||
|
@ -278,12 +305,16 @@ func testEndpoints(t *testing.T) {
|
||||||
|
|
||||||
func testSearchTypesAndCache(t *testing.T, summary *TestSummary) {
|
func testSearchTypesAndCache(t *testing.T, summary *TestSummary) {
|
||||||
searchTypes := []string{"text", "image", "video", "forum", "map", "file"}
|
searchTypes := []string{"text", "image", "video", "forum", "map", "file"}
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
for _, searchType := range searchTypes {
|
for _, searchType := range searchTypes {
|
||||||
url := "http://localhost:5000/search?q=test&t=" + searchType
|
url := "http://localhost:5000/search?q=test&t=" + searchType
|
||||||
|
|
||||||
// Initial search
|
// Initial search
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
resp, err := http.Get(url)
|
resp, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to GET %s: %v", url, err)
|
t.Errorf("Failed to GET %s: %v", url, err)
|
||||||
continue
|
continue
|
||||||
|
@ -303,10 +334,16 @@ func testSearchTypesAndCache(t *testing.T, summary *TestSummary) {
|
||||||
}
|
}
|
||||||
t.Logf("Search type '%s' took %v", searchType, initialDuration)
|
t.Logf("Search type '%s' took %v", searchType, initialDuration)
|
||||||
|
|
||||||
|
// If initial search took longer than 10 seconds, skip
|
||||||
|
if initialDuration > 10*time.Second {
|
||||||
|
t.Logf("Search type '%s' took too long (%v), skipping cached search", searchType, initialDuration)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Cached search
|
// Cached search
|
||||||
time.Sleep(1 * time.Second) // Short delay to simulate time between searches
|
time.Sleep(1 * time.Second)
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
resp, err = http.Get(url)
|
resp, err = client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed to GET %s (cached): %v", url, err)
|
t.Errorf("Failed to GET %s (cached): %v", url, err)
|
||||||
continue
|
continue
|
||||||
|
@ -332,7 +369,6 @@ func testSearchTypesAndCache(t *testing.T, summary *TestSummary) {
|
||||||
t.Errorf("Cache not effective for search type '%s'", searchType)
|
t.Errorf("Cache not effective for search type '%s'", searchType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the results
|
|
||||||
summary.CacheTests = append(summary.CacheTests, CacheTestResult{
|
summary.CacheTests = append(summary.CacheTests, CacheTestResult{
|
||||||
SearchType: searchType,
|
SearchType: searchType,
|
||||||
InitialDuration: initialDuration,
|
InitialDuration: initialDuration,
|
||||||
|
@ -368,3 +404,96 @@ func testSuggestionsAPI(t *testing.T) {
|
||||||
func bToMb(b uint64) uint64 {
|
func bToMb(b uint64) uint64 {
|
||||||
return b / 1024 / 1024
|
return b / 1024 / 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testConcurrentRandomRequests(t *testing.T, summary *TestSummary) {
|
||||||
|
searchTypes := []string{"text", "image", "video", "forum", "map", "file"}
|
||||||
|
numRequests := 10 // Number of requests per search type
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, stype := range searchTypes {
|
||||||
|
summary.ConcurrentTestStats.RequestsPerType[stype] = numRequests
|
||||||
|
for i := 0; i < numRequests; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(searchType string) {
|
||||||
|
defer wg.Done()
|
||||||
|
query := getRandomUserQuery(searchType)
|
||||||
|
fullURL := fmt.Sprintf("http://localhost:5000/search?q=%s&t=%s", url.QueryEscape(query), searchType)
|
||||||
|
resp, err := client.Get(fullURL)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to GET %s: %v", fullURL, err)
|
||||||
|
summary.ConcurrentTestStats.TotalFailures++
|
||||||
|
summary.ConcurrentTestStats.FailuresPerType[searchType]++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("Search type '%s' with query '%s' returned status code %d", searchType, query, resp.StatusCode)
|
||||||
|
summary.ConcurrentTestStats.TotalFailures++
|
||||||
|
summary.ConcurrentTestStats.FailuresPerType[searchType]++
|
||||||
|
}
|
||||||
|
}(stype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all requests to complete
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
summary.ConcurrentTestStats.TotalRequests = numRequests * len(searchTypes)
|
||||||
|
t.Logf("Concurrent random requests test completed: %d requests sent", summary.ConcurrentTestStats.TotalRequests)
|
||||||
|
if summary.ConcurrentTestStats.TotalFailures > 0 {
|
||||||
|
t.Errorf("Number of failed requests: %d", summary.ConcurrentTestStats.TotalFailures)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRandomUserQuery returns a random user-like search query based on the search type
|
||||||
|
func getRandomUserQuery(searchType string) string {
|
||||||
|
var queries []string
|
||||||
|
switch searchType {
|
||||||
|
case "text":
|
||||||
|
queries = []string{
|
||||||
|
"weather forecast", "latest tech news", "open source software", "how to cook pasta",
|
||||||
|
"learn golang", "famous quotes", "best laptops 2024", "history of linux",
|
||||||
|
"simple bread recipe", "mountain climbing tips",
|
||||||
|
}
|
||||||
|
case "image":
|
||||||
|
queries = []string{
|
||||||
|
"cute cat pictures", "beautiful landscapes", "famous paintings", "colorful birds",
|
||||||
|
"space wallpapers", "vintage cars", "r/unixporn", "minimalist backgrounds",
|
||||||
|
}
|
||||||
|
case "video":
|
||||||
|
queries = []string{
|
||||||
|
"cute cats", "music videos", "sports highlights", "coding tutorials",
|
||||||
|
"documentaries about space", "stand-up comedy clips", "top movie trailers",
|
||||||
|
}
|
||||||
|
case "forum":
|
||||||
|
queries = []string{
|
||||||
|
"linux help forum", "DIY electronics discussion", "programming Q&A",
|
||||||
|
"travel tips community", "best gaming computers", "homebrewing advice",
|
||||||
|
"gardening support", "car maintenance forum", "best homelab setup",
|
||||||
|
}
|
||||||
|
case "map":
|
||||||
|
queries = []string{
|
||||||
|
"coffee shops in New York", "best pizza places in Chicago", "tourist attractions Tokyo",
|
||||||
|
"Brazil", "public parks in Berlin", "bookstores in London", "Japan",
|
||||||
|
}
|
||||||
|
case "file":
|
||||||
|
queries = []string{
|
||||||
|
"debian iso", "free ebooks", "open source fonts", "linux distribution torrents",
|
||||||
|
"arch iso", "alpine linux",
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// fallback if unknown type
|
||||||
|
queries = []string{"test query", "random search", "hello world"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick a random query
|
||||||
|
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(queries))))
|
||||||
|
if err != nil {
|
||||||
|
return queries[0] // fallback to first if error occurs
|
||||||
|
}
|
||||||
|
return queries[n.Int64()]
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue