diff --git a/core/routers/luarouter_test.go b/core/routers/luarouter_test.go deleted file mode 100644 index 61b2c5f..0000000 --- a/core/routers/luarouter_test.go +++ /dev/null @@ -1,256 +0,0 @@ -package routers - -import ( - "os" - "path/filepath" - "testing" -) - -func setupTestRoutes(t *testing.T) (string, func()) { - // Create a temporary directory for test routes - tempDir, err := os.MkdirTemp("", "fsrouter-test") - if err != nil { - t.Fatalf("Failed to create temp directory: %v", err) - } - - // Create route structure with valid Lua code - routes := map[string]string{ - "get.lua": "return { path = '/' }", - "post.lua": "return { path = '/' }", - "api/get.lua": "return { path = '/api' }", - "api/users/get.lua": "return { path = '/api/users' }", - "api/users/[id]/get.lua": "return { path = '/api/users/[id]' }", - "api/users/[id]/posts/get.lua": "return { path = '/api/users/[id]/posts' }", - "api/[version]/docs/get.lua": "return { path = '/api/[version]/docs' }", - } - - for path, content := range routes { - routePath := filepath.Join(tempDir, path) - - // Create directories - err := os.MkdirAll(filepath.Dir(routePath), 0755) - if err != nil { - t.Fatalf("Failed to create directory %s: %v", filepath.Dir(routePath), err) - } - - // Create file - err = os.WriteFile(routePath, []byte(content), 0644) - if err != nil { - t.Fatalf("Failed to create file %s: %v", routePath, err) - } - } - - // Return cleanup function - cleanup := func() { - os.RemoveAll(tempDir) - } - - return tempDir, cleanup -} - -func TestRouterInitialization(t *testing.T) { - routesDir, cleanup := setupTestRoutes(t) - defer cleanup() - - router, err := NewLuaRouter(routesDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - if router == nil { - t.Fatal("Router is nil") - } -} - -func TestRouteMatching(t *testing.T) { - routesDir, cleanup := setupTestRoutes(t) - defer cleanup() - - router, err := NewLuaRouter(routesDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - tests := []struct { - method string - path string - wantFound bool - wantParams map[string]string - wantHandler string - }{ - // Static routes - {"GET", "/", true, nil, filepath.Join(routesDir, "get.lua")}, - {"POST", "/", true, nil, filepath.Join(routesDir, "post.lua")}, - {"GET", "/api", true, nil, filepath.Join(routesDir, "api/get.lua")}, - {"GET", "/api/users", true, nil, filepath.Join(routesDir, "api/users/get.lua")}, - - // Parameterized routes - {"GET", "/api/users/123", true, map[string]string{"id": "123"}, filepath.Join(routesDir, "api/users/[id]/get.lua")}, - {"GET", "/api/users/456/posts", true, map[string]string{"id": "456"}, filepath.Join(routesDir, "api/users/[id]/posts/get.lua")}, - {"GET", "/api/v1/docs", true, map[string]string{"version": "v1"}, filepath.Join(routesDir, "api/[version]/docs/get.lua")}, - - // Non-existent routes - {"PUT", "/", false, nil, ""}, - {"GET", "/nonexistent", false, nil, ""}, - {"GET", "/api/nonexistent", false, nil, ""}, - } - - for _, tt := range tests { - t.Run(tt.method+" "+tt.path, func(t *testing.T) { - var params Params - node, found := router.Match(tt.method, tt.path, ¶ms) - - if found != tt.wantFound { - t.Errorf("Match() found = %v, want %v", found, tt.wantFound) - } - - if !found { - return - } - - if node.handler != tt.wantHandler { - t.Errorf("Match() handler = %v, want %v", node.handler, tt.wantHandler) - } - - // Verify bytecode was compiled - if len(node.bytecode) == 0 { - t.Errorf("No bytecode found for handler: %s", node.handler) - } - - // Verify parameters - if tt.wantParams != nil { - for key, wantValue := range tt.wantParams { - gotValue := params.Get(key) - if gotValue != wantValue { - t.Errorf("Parameter %s = %s, want %s", key, gotValue, wantValue) - } - } - } - }) - } -} - -func TestParamExtraction(t *testing.T) { - routesDir, cleanup := setupTestRoutes(t) - defer cleanup() - - router, err := NewLuaRouter(routesDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - var params Params - _, found := router.Match("GET", "/api/v2/docs", ¶ms) - - if !found { - t.Fatalf("Route not found") - } - - if params.Count != 1 { - t.Errorf("Expected 1 parameter, got %d", params.Count) - } - - if params.Keys[0] != "version" { - t.Errorf("Expected parameter key 'version', got '%s'", params.Keys[0]) - } - - if params.Values[0] != "v2" { - t.Errorf("Expected parameter value 'v2', got '%s'", params.Values[0]) - } - - if params.Get("version") != "v2" { - t.Errorf("Get(\"version\") returned '%s', expected 'v2'", params.Get("version")) - } -} - -func TestGetBytecode(t *testing.T) { - routesDir, cleanup := setupTestRoutes(t) - defer cleanup() - - router, err := NewLuaRouter(routesDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - var params Params - bytecode, _, found := router.GetBytecode("GET", "/api/users/123", ¶ms) - - if !found { - t.Fatalf("Route not found") - } - - if len(bytecode) == 0 { - t.Errorf("Expected non-empty bytecode") - } - - // Check parameters were extracted - if params.Get("id") != "123" { - t.Errorf("Expected id parameter '123', got '%s'", params.Get("id")) - } -} - -func TestRefresh(t *testing.T) { - routesDir, cleanup := setupTestRoutes(t) - defer cleanup() - - router, err := NewLuaRouter(routesDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - // Add a new route file - newRoutePath := filepath.Join(routesDir, "new", "get.lua") - err = os.MkdirAll(filepath.Dir(newRoutePath), 0755) - if err != nil { - t.Fatalf("Failed to create directory: %v", err) - } - - err = os.WriteFile(newRoutePath, []byte("return { path = '/new' }"), 0644) - if err != nil { - t.Fatalf("Failed to create file: %v", err) - } - - // Before refresh, route should not be found - var params Params - _, _, found := router.GetBytecode("GET", "/new", ¶ms) - if found { - t.Errorf("New route should not be found before refresh") - } - - // Refresh router - err = router.Refresh() - if err != nil { - t.Fatalf("Failed to refresh router: %v", err) - } - - // After refresh, route should be found - bytecode, _, found := router.GetBytecode("GET", "/new", ¶ms) - if !found { - t.Errorf("New route should be found after refresh") - } - - if len(bytecode) == 0 { - t.Errorf("Expected non-empty bytecode for new route") - } -} - -func TestInvalidRoutesDir(t *testing.T) { - // Non-existent directory - _, err := NewLuaRouter("/non/existent/directory") - if err == nil { - t.Error("Expected error for non-existent directory, got nil") - } - - // Create a file instead of a directory - tmpFile, err := os.CreateTemp("", "fsrouter-test-file") - if err != nil { - t.Fatalf("Failed to create temp file: %v", err) - } - defer os.Remove(tmpFile.Name()) - defer tmpFile.Close() - - _, err = NewLuaRouter(tmpFile.Name()) - if err == nil { - t.Error("Expected error for file as routes dir, got nil") - } -} diff --git a/core/routers/staticrouter_test.go b/core/routers/staticrouter_test.go deleted file mode 100644 index 2a640e7..0000000 --- a/core/routers/staticrouter_test.go +++ /dev/null @@ -1,420 +0,0 @@ -package routers - -import ( - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strings" - "testing" - - "git.sharkk.net/Sky/Moonshark/core/logger" -) - -func setupStaticFiles(t *testing.T) (string, func()) { - // Create a temporary directory - tempDir, err := os.MkdirTemp("", "staticrouter-test") - if err != nil { - t.Fatalf("Failed to create temp directory: %v", err) - } - - // Create file structure - files := map[string]string{ - "index.html": "Home", - "about.html": "About", - "api/index.json": `{"version": "1.0"}`, - "users/index.html": "Users", - "users/123/profile.html": "User Profile", - "posts/hello-world/comments.html": "Post Comments", - "docs/v1/api.html": "API Docs", - "styles.css": "body { color: red; }", - "script.js": "function test() { return true; }", - } - - for path, content := range files { - filePath := filepath.Join(tempDir, path) - - // Create directories - err := os.MkdirAll(filepath.Dir(filePath), 0755) - if err != nil { - t.Fatalf("Failed to create directory %s: %v", filepath.Dir(filePath), err) - } - - // Create file - err = os.WriteFile(filePath, []byte(content), 0644) - if err != nil { - t.Fatalf("Failed to create file %s: %v", filePath, err) - } - } - - // Return cleanup function - cleanup := func() { - os.RemoveAll(tempDir) - } - - return tempDir, cleanup -} - -func TestStaticRouterInitialization(t *testing.T) { - rootDir, cleanup := setupStaticFiles(t) - defer cleanup() - - router, err := NewStaticRouter(rootDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - if router == nil { - t.Fatal("Router is nil") - } - - // Test configuration methods - router.SetMaxItems(200) - router.SetMaxItemSize(2 << 20) // 2MB - router.SetTotalCapacity(50 << 20) // 50MB - - // These methods shouldn't fail, though we can't verify internal state directly -} - -func TestStaticRouteMatching(t *testing.T) { - rootDir, cleanup := setupStaticFiles(t) - defer cleanup() - - router, err := NewStaticRouter(rootDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - tests := []struct { - path string - wantFound bool - }{ - {"/static/index.html", true}, - {"/static/about.html", true}, - {"/static/api/index.json", true}, - {"/static/users/index.html", true}, - {"/static/users/123/profile.html", true}, - {"/static/posts/hello-world/comments.html", true}, - {"/static/docs/v1/api.html", true}, - {"/static/styles.css", true}, - {"/static/script.js", true}, - - // Non-existent routes - {"/static/nonexistent.html", false}, - {"/static/api/nonexistent.json", false}, - - // Routes without prefix - {"/index.html", false}, - {"/styles.css", false}, - } - - for _, tt := range tests { - t.Run(tt.path, func(t *testing.T) { - _, found := router.Match(tt.path) - if found != tt.wantFound { - t.Errorf("Match() found = %v, want %v", found, tt.wantFound) - } - }) - } - - // Test with different prefix - router.SetURLPrefix("/assets") - - // Should now match with new prefix - _, found := router.Match("/assets/index.html") - if !found { - t.Errorf("Match() should find file with new prefix") - } - - // Should not match with old prefix - _, found = router.Match("/static/index.html") - if found { - t.Errorf("Match() should not find file with old prefix") - } -} - -func TestStaticRefresh(t *testing.T) { - rootDir, cleanup := setupStaticFiles(t) - defer cleanup() - - router, err := NewStaticRouter(rootDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - // Add a new file - newFilePath := filepath.Join(rootDir, "new.html") - err = os.WriteFile(newFilePath, []byte("New"), 0644) - if err != nil { - t.Fatalf("Failed to create file: %v", err) - } - - // File should be found with proper prefix - _, found := router.Match("/static/new.html") - if !found { - t.Errorf("New file should be found immediately") - } - - // Test refresh clears cache - err = router.Refresh() - if err != nil { - t.Fatalf("Failed to refresh router: %v", err) - } - - // File should still be found after refresh - _, found = router.Match("/static/new.html") - if !found { - t.Errorf("File should still be found after refresh") - } -} - -func TestStaticRouterHTTP(t *testing.T) { - rootDir, cleanup := setupStaticFiles(t) - defer cleanup() - - router, err := NewStaticRouter(rootDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - // Enable debug logging for coverage - router.EnableDebugLog() - - // Create a request to get a CSS file - first request will be uncached - req, err := http.NewRequest("GET", "/static/styles.css", nil) - if err != nil { - t.Fatalf("Failed to create request: %v", err) - } - req.Header.Set("Accept-Encoding", "gzip") - - recorder := httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusOK { - t.Errorf("Expected status %d, got %d", http.StatusOK, recorder.Code) - } - - // Second request should be cached - recorder = httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusOK { - t.Errorf("Expected status %d, got %d for cached request", http.StatusOK, recorder.Code) - } - - // Verify content is gzipped - if recorder.Header().Get("Content-Encoding") != "gzip" { - t.Errorf("Expected gzip encoding, got %s", recorder.Header().Get("Content-Encoding")) - } - - // Verify content type is correct - if !strings.Contains(recorder.Header().Get("Content-Type"), "text/css") { - t.Errorf("Expected text/css content type, got %s", recorder.Header().Get("Content-Type")) - } - - // Test with If-Modified-Since header - req.Header.Set("If-Modified-Since", recorder.Header().Get("Last-Modified")) - recorder = httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusNotModified { - t.Errorf("Expected status %d, got %d for If-Modified-Since", http.StatusNotModified, recorder.Code) - } - - // Test request without gzip support - req.Header.Del("Accept-Encoding") - req.Header.Del("If-Modified-Since") // Ensure we don't get a 304 - recorder = httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusOK { - t.Errorf("Expected status %d, got %d for non-gzip request", http.StatusOK, recorder.Code) - } - - if recorder.Header().Get("Content-Encoding") == "gzip" { - t.Errorf("Should not have gzip encoding for non-gzip request") - } - - // Test request to non-existing file - req, _ = http.NewRequest("GET", "/static/nonexistent.css", nil) - recorder = httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusNotFound { - t.Errorf("Expected status %d, got %d for nonexistent file", http.StatusNotFound, recorder.Code) - } - - // Test request without prefix - req, _ = http.NewRequest("GET", "/styles.css", nil) - recorder = httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusNotFound { - t.Errorf("Expected status %d, got %d for request without prefix", http.StatusNotFound, recorder.Code) - } - - // Test with custom prefix - router.SetURLPrefix("/assets") - req, _ = http.NewRequest("GET", "/assets/styles.css", nil) - req.Header.Set("Accept-Encoding", "gzip") - recorder = httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusOK { - t.Errorf("Expected status %d, got %d with custom prefix", http.StatusOK, recorder.Code) - } - - // Disable debug logging for coverage - router.DisableDebugLog() -} - -func TestStaticRouterPreload(t *testing.T) { - rootDir, cleanup := setupStaticFiles(t) - defer cleanup() - - router, err := NewStaticRouter(rootDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - // Enable debug logging for coverage - router.EnableDebugLog() - - // Preload files - router.PreloadCommonFiles() - - // Request CSS file which should be preloaded - req, err := http.NewRequest("GET", "/static/styles.css", nil) - if err != nil { - t.Fatalf("Failed to create request: %v", err) - } - req.Header.Set("Accept-Encoding", "gzip") - - recorder := httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusOK { - t.Errorf("Expected status %d, got %d", http.StatusOK, recorder.Code) - } - - // Should be served from cache - if recorder.Header().Get("Content-Encoding") != "gzip" { - t.Errorf("Expected gzip encoding after preload, got %s", recorder.Header().Get("Content-Encoding")) - } -} - -func TestStaticRouterStats(t *testing.T) { - rootDir, cleanup := setupStaticFiles(t) - defer cleanup() - - router, err := NewStaticRouter(rootDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - // Preload files to populate cache - router.PreloadCommonFiles() - - // Wait a bit for preloading to complete - // This is a bit of a hack but necessary for the async nature of preloading - for i := 0; i < 100; i++ { - stats := router.GetStats() - items, ok := stats["items"].(int) - if ok && items > 0 { - break - } - // Small sleep to avoid tight loop - http.NewRequest("GET", "/styles.css", nil) // Just to waste a little time - } - - // Get stats - stats := router.GetStats() - - // Verify stats exist - if stats["items"] == nil { - t.Errorf("Expected items stat to exist") - } - if stats["maxItems"] == nil { - t.Errorf("Expected maxItems stat to exist") - } - if stats["currentSize"] == nil { - t.Errorf("Expected currentSize stat to exist") - } - if stats["totalCapacity"] == nil { - t.Errorf("Expected totalCapacity stat to exist") - } - if stats["usagePercent"] == nil { - t.Errorf("Expected usagePercent stat to exist") - } -} - -func TestStaticRouterLargeFile(t *testing.T) { - rootDir, cleanup := setupStaticFiles(t) - defer cleanup() - - // Create a large file (2MB) that should exceed default max item size - largeFilePath := filepath.Join(rootDir, "large.bin") - largeContent := make([]byte, 2<<20) // 2MB of zeros - for i := range largeContent { - largeContent[i] = byte(i % 256) // Fill with pattern to prevent compression - } - - err := os.WriteFile(largeFilePath, largeContent, 0644) - if err != nil { - t.Fatalf("Failed to create large file: %v", err) - } - - router, err := NewStaticRouter(rootDir) - if err != nil { - t.Fatalf("Failed to create router: %v", err) - } - - // Request large file with proper prefix - req, err := http.NewRequest("GET", "/static/large.bin", nil) - if err != nil { - t.Fatalf("Failed to create request: %v", err) - } - req.Header.Set("Accept-Encoding", "gzip") - - recorder := httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusOK { - t.Errorf("Expected status %d, got %d", http.StatusOK, recorder.Code) - } - - // Should not be served from cache (too large) - if recorder.Header().Get("Content-Encoding") == "gzip" { - t.Errorf("Large file should not be served with gzip from cache") - } - - // Verify content length is correct - if recorder.Body.Len() != len(largeContent) { - t.Errorf("Expected body length %d, got %d", len(largeContent), recorder.Body.Len()) - } -} - -func TestStaticRouterWithLogger(t *testing.T) { - rootDir, cleanup := setupStaticFiles(t) - defer cleanup() - - // Create a test logger - log := logger.New(logger.LevelDebug, false) - - // Create router with custom logger - router, err := NewStaticRouterWithLogger(rootDir, log) - if err != nil { - t.Fatalf("Failed to create router with logger: %v", err) - } - - router.EnableDebugLog() - - // Basic test with explicit file - req, _ := http.NewRequest("GET", "/static/styles.css", nil) - recorder := httptest.NewRecorder() - router.ServeHTTP(recorder, req) - - if recorder.Code != http.StatusOK { - t.Errorf("Expected status OK, got %d", recorder.Code) - } -}