added concurrent search test
All checks were successful
Run Integration Tests / test (push) Successful in 28s

This commit is contained in:
partisan 2024-12-06 15:33:02 +01:00
parent 7498f4128a
commit 28a721887c

View file

@ -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"
@ -17,11 +22,12 @@ import (
) )
type TestSummary struct { type TestSummary struct {
Passed []string Passed []string
Failed []string Failed []string
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()]
}