Aufgaben-Python/courses/go/16_master_abschlussprojekt_microservice/test_aufgabe.go

147 lines
4.9 KiB
Go

package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"sync"
"time"
)
// 🧪 MASTER 16 TESTSUITE: High-Concurrency Microservice API
func assert(condition bool, msg string) {
if !condition {
panic("❌ Test fehlgeschlagen: " + msg)
}
}
func assertEquals[T comparable](actual, expected T, msg string) {
if actual != expected {
panic(fmt.Sprintf("❌ Test fehlgeschlagen: %s (Erwartet: '%v', Erhalten: '%v')", msg, expected, actual))
}
}
// TEST: TestShortenAndResolve - Prüft grundlegende URL-Kürzung und Auflösung
func TestShortenAndResolve() {
fmt.Println("=== RUN TestShortenAndResolve")
srv := NewMicroserviceServer(100)
srv.StartWorkerPool(2)
defer srv.StopWorkerPool()
// 1. URL kürzen
rec, err := srv.ShortenURL("https://cloud.google.com/go")
assert(err == nil, "ShortenURL sollte bei valider HTTPS-URL erfolgreich sein")
assert(len(rec.ShortCode) >= 6, "ShortCode muss mindestens 6 Zeichen lang sein")
assertEquals(rec.OriginalURL, "https://cloud.google.com/go", "OriginalURL muss übereinstimmen")
// 2. Ungültige URL prüfen
_, errInvalid := srv.ShortenURL("ftp://invalid-protocol")
assert(errInvalid != nil, "Ungültiges URL-Protokoll muss abgewiesen werden")
// 3. URL auflösen
orig, errResolve := srv.ResolveURL(rec.ShortCode, "192.168.1.100", "Mozilla/5.0")
assert(errResolve == nil, "ResolveURL muss den ShortCode auflösen")
assertEquals(orig, "https://cloud.google.com/go", "Aufgelöste URL muss mit Original übereinstimmen")
fmt.Println("--- PASS: TestShortenAndResolve")
}
// TEST: TestWorkerPoolAsyncAnalytics - Prüft asynchrone Klick-Verarbeitung
func TestWorkerPoolAsyncAnalytics() {
fmt.Println("=== RUN TestWorkerPoolAsyncAnalytics")
srv := NewMicroserviceServer(1000)
srv.StartWorkerPool(4)
rec, _ := srv.ShortenURL("https://golang.org/pkg")
// 5 Klicks von 3 verschiedenen IPs simulieren
ips := []string{"10.0.0.1", "10.0.0.2", "10.0.0.1", "10.0.0.3", "10.0.0.2"}
for _, ip := range ips {
_, _ = srv.ResolveURL(rec.ShortCode, ip, "Go-Test-Agent")
}
// Workerpool stoppen -> Wartet bis alle Events in der Queue verarbeitet sind!
srv.StopWorkerPool()
stats, err := srv.GetStats(rec.ShortCode)
assert(err == nil, "GetStats sollte für bestehenden Code Daten liefern")
assertEquals(stats.TotalClicks, int64(5), "TotalClicks muss 5 sein")
assertEquals(stats.UniqueIPs, 3, "UniqueIPs muss exakt 3 sein")
fmt.Println("--- PASS: TestWorkerPoolAsyncAnalytics")
}
// TEST: TestConcurrentClickStressTest - 100 gleichzeitige Klicks
func TestConcurrentClickStressTest() {
fmt.Println("=== RUN TestConcurrentClickStressTest")
srv := NewMicroserviceServer(5000)
srv.StartWorkerPool(8)
rec, _ := srv.ShortenURL("https://kubernetes.io")
var wg sync.WaitGroup
numClicks := 100
for i := 0; i < numClicks; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
ip := fmt.Sprintf("172.16.0.%d", id%10) // 10 distinct IPs
_, _ = srv.ResolveURL(rec.ShortCode, ip, "StressTestBot")
}(i)
}
wg.Wait()
srv.StopWorkerPool()
stats, _ := srv.GetStats(rec.ShortCode)
assertEquals(stats.TotalClicks, int64(numClicks), "Alle 100 parallelen Klicks müssen registriert sein")
assertEquals(stats.UniqueIPs, 10, "Genau 10 Unique IPs sollten erfasst sein")
fmt.Println("--- PASS: TestConcurrentClickStressTest")
}
// TEST: TestHTTPRouterEndpoints - Prüft REST HTTP Integration
func TestHTTPRouterEndpoints() {
fmt.Println("=== RUN TestHTTPRouterEndpoints")
srv := NewMicroserviceServer(100)
srv.StartWorkerPool(2)
defer srv.StopWorkerPool()
router := SetupMicroserviceRouter(srv)
// 1. POST /api/shorten
payload := `{"url":"https://github.com/golang/go"}`
reqPost := httptest.NewRequest(http.MethodPost, "/api/shorten", bytes.NewBufferString(payload))
recPost := httptest.NewRecorder()
router.ServeHTTP(recPost, reqPost)
assertEquals(recPost.Code, http.StatusCreated, "POST /api/shorten muss 201 Created liefern")
var created URLRecord
json.Unmarshal(recPost.Body.Bytes(), &created)
assert(created.ShortCode != "", "ShortCode muss generiert worden sein")
// 2. GET /r/{short_code}
reqGet := httptest.NewRequest(http.MethodGet, "/r/"+created.ShortCode, nil)
recGet := httptest.NewRecorder()
router.ServeHTTP(recGet, reqGet)
assertEquals(recGet.Code, http.StatusOK, "GET /r/{code} muss 200 OK liefern")
// 3. GET /api/metrics
reqMetrics := httptest.NewRequest(http.MethodGet, "/api/metrics", nil)
recMetrics := httptest.NewRecorder()
router.ServeHTTP(recMetrics, reqMetrics)
assertEquals(recMetrics.Code, http.StatusOK, "GET /api/metrics muss 200 OK liefern")
fmt.Println("--- PASS: TestHTTPRouterEndpoints")
}
func main() {
fmt.Println("🧪 Starte Master 16 Abschlussprojekt Testsuite...")
TestShortenAndResolve()
TestWorkerPoolAsyncAnalytics()
TestConcurrentClickStressTest()
TestHTTPRouterEndpoints()
fmt.Println("\n🏆 GRATULATION! Alle Tests im Master-Abschlussprojekt 16 erfolgreich bestanden!")
}