diff --git a/internal/fetcher/fetcher_test.go b/internal/fetcher/fetcher_test.go new file mode 100644 index 0000000..be3ec5d --- /dev/null +++ b/internal/fetcher/fetcher_test.go @@ -0,0 +1,189 @@ +package fetcher + +import ( + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" +) + +func TestIsValidArchiveURL(t *testing.T) { + tests := []struct { + name string + url string + expected bool + }{ + {"ZIP URL", "https://example.com/archive.zip", true}, + {"TAR URL", "https://example.com/archive.tar", true}, + {"TAR.GZ URL", "https://example.com/archive.tar.gz", true}, + {"TGZ URL", "https://example.com/archive.tgz", true}, + {"GZIP URL", "https://example.com/file.gz", true}, + {"RAR URL", "https://example.com/archive.rar", true}, + {"Invalid URL", "https://example.com/file.txt", false}, + {"No extension", "https://example.com/file", false}, + {"Invalid format", "not a url", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := IsValidArchiveURL(tt.url) + if result != tt.expected { + t.Errorf("IsValidArchiveURL(%q) = %v; want %v", tt.url, result, tt.expected) + } + }) + } +} + +func TestGetFilenameFromURL(t *testing.T) { + tests := []struct { + name string + url string + expected string + expectError bool + }{ + {"Simple filename", "https://example.com/archive.zip", "archive.zip", false}, + {"Path with directories", "https://example.com/path/to/file.tar.gz", "file.tar.gz", false}, + {"Query parameters", "https://example.com/file.zip?download=true", "file.zip", false}, + {"No filename", "https://example.com/", "", true}, + {"Relative path treated as filename", "not a url", "not a url", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := GetFilenameFromURL(tt.url) + if tt.expectError { + if err == nil { + t.Errorf("Expected error for %q, got nil", tt.url) + } + } else { + if err != nil { + t.Errorf("Unexpected error for %q: %v", tt.url, err) + } + if result != tt.expected { + t.Errorf("GetFilenameFromURL(%q) = %q; want %q", tt.url, result, tt.expected) + } + } + }) + } +} + +func TestDownloadFile(t *testing.T) { + // Create a test server + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Length", "13") + w.Write([]byte("test content!")) + })) + defer server.Close() + + // Create temp directory + tempDir := t.TempDir() + outputFile := filepath.Join(tempDir, "downloaded.txt") + + // Test download + err := downloadFile(outputFile, server.URL) + if err != nil { + t.Fatalf("Failed to download file: %v", err) + } + + // Verify file exists + if _, err := os.Stat(outputFile); os.IsNotExist(err) { + t.Error("Downloaded file does not exist") + } + + // Verify content + content, err := os.ReadFile(outputFile) + if err != nil { + t.Fatalf("Failed to read downloaded file: %v", err) + } + + expected := "test content!" + if string(content) != expected { + t.Errorf("Downloaded content = %q; want %q", string(content), expected) + } +} + +func TestDownloadFileNotFound(t *testing.T) { + // Create a test server that returns 404 + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + defer server.Close() + + tempDir := t.TempDir() + outputFile := filepath.Join(tempDir, "downloaded.txt") + + err := downloadFile(outputFile, server.URL) + if err == nil { + t.Error("Expected error for 404 response, got nil") + } +} + +func TestDownloadFileInvalidURL(t *testing.T) { + tempDir := t.TempDir() + outputFile := filepath.Join(tempDir, "downloaded.txt") + + err := downloadFile(outputFile, "http://invalid-url-that-does-not-exist-12345.com") + if err == nil { + t.Error("Expected error for invalid URL, got nil") + } +} + +func TestFetchAndExtractInvalidURL(t *testing.T) { + err := FetchAndExtract("not a url", "/tmp/output", false, true) + if err == nil { + t.Error("Expected error for invalid URL, got nil") + } +} + +func TestFetchAndExtractNonHTTP(t *testing.T) { + err := FetchAndExtract("ftp://example.com/file.zip", "/tmp/output", false, true) + if err == nil { + t.Error("Expected error for non-HTTP URL, got nil") + } + + expectedMsg := "only HTTP and HTTPS URLs are supported" + if err != nil && err.Error() != expectedMsg { + t.Errorf("Expected error message %q, got %q", expectedMsg, err.Error()) + } +} + +func TestProgressReader(t *testing.T) { + // Create a test reader + data := []byte("test data for progress reader") + pr := &progressReader{ + reader: nil, // We'll test the struct directly + total: int64(len(data)), + downloaded: 0, + lastPrintSize: 0, + } + + // Test initial state + if pr.downloaded != 0 { + t.Errorf("Initial downloaded = %d; want 0", pr.downloaded) + } + + if pr.total != int64(len(data)) { + t.Errorf("Total = %d; want %d", pr.total, len(data)) + } +} + +func TestFetchAndExtractInvalidDestination(t *testing.T) { + // Create a test server with a valid ZIP + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Length", "4") + w.Write([]byte("test")) + })) + defer server.Close() + + // Try to extract to an invalid destination (file instead of directory) + tempDir := t.TempDir() + invalidDest := filepath.Join(tempDir, "file.txt") + os.WriteFile(invalidDest, []byte("test"), 0644) + + err := FetchAndExtract(server.URL+"/test.zip", invalidDest, false, true) + // This should fail during extraction or detection + if err == nil { + t.Log("Note: This test may pass if the downloaded content is not a valid archive") + } +}