diff --git a/docker/cdn_test.go b/docker/cdn_test.go new file mode 100644 index 00000000..cbcec59b --- /dev/null +++ b/docker/cdn_test.go @@ -0,0 +1,407 @@ +package docker_test + +import ( + "os" + "os/exec" + "path/filepath" + "regexp" + "sort" + "strings" + "testing" +) + +// cdnURLPattern is the same regex used by download_cdn.sh to extract CDN URLs. +var cdnURLPattern = regexp.MustCompile(`https://cdn\.jsdelivr\.net/npm/[^"' )\x60]*`) + +// HTML fixtures that mirror the real www/*.html files. +var htmlFixtures = map[string]string{ + "hls.html": ` + + + + + +`, + + "config.html": ` + + + + + + +`, + + "net.html": ` + + + + + +`, + + "links.html": ` + + + + +`, +} + +func parseCDNURL(rawURL string) (pkgName, filePath, localURL string) { + npmPath := strings.TrimPrefix(rawURL, "https://cdn.jsdelivr.net/npm/") + + parts := strings.SplitN(npmPath, "/", 2) + pkgVer := parts[0] + + if len(parts) > 1 { + filePath = parts[1] + } + + // Remove @version suffix to get package name + if idx := strings.LastIndex(pkgVer, "@"); idx > 0 { + pkgName = pkgVer[:idx] + } else { + pkgName = pkgVer + } + + if filePath != "" { + localURL = "cdn/" + pkgName + "/" + filePath + } else { + localURL = "cdn/" + pkgName + "/index.js" + } + return +} + +// extractURLs finds all CDN URLs in the given HTML content. +func extractURLs(htmlFiles map[string]string) []string { + seen := map[string]bool{} + for _, content := range htmlFiles { + for _, match := range cdnURLPattern.FindAllString(content, -1) { + seen[match] = true + } + } + urls := make([]string, 0, len(seen)) + for u := range seen { + urls = append(urls, u) + } + sort.Strings(urls) + return urls +} + +// patchHTML replaces all CDN URLs in content with local paths. +func patchHTML(content string, urls []string) string { + for _, u := range urls { + _, _, localURL := parseCDNURL(u) + content = strings.ReplaceAll(content, u, localURL) + } + return content +} + +func TestExtractURLs(t *testing.T) { + urls := extractURLs(htmlFixtures) + + expected := []string{ + "https://cdn.jsdelivr.net/npm/hls.js@1", + "https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/dist/js-yaml.min.js", + "https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min", + "https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs/loader.js", + "https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js", + "https://cdn.jsdelivr.net/npm/vis-network@10.0.2/standalone/umd/vis-network.min.js", + } + + if len(urls) != len(expected) { + t.Fatalf("expected %d URLs, got %d: %v", len(expected), len(urls), urls) + } + for i, u := range urls { + if u != expected[i] { + t.Errorf("URL[%d]: expected %q, got %q", i, expected[i], u) + } + } +} + +func TestParseCDNURL(t *testing.T) { + tests := []struct { + url string + pkgName string + filePath string + localURL string + }{ + { + url: "https://cdn.jsdelivr.net/npm/hls.js@1", + pkgName: "hls.js", + filePath: "", + localURL: "cdn/hls.js/index.js", + }, + { + url: "https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/dist/js-yaml.min.js", + pkgName: "js-yaml", + filePath: "dist/js-yaml.min.js", + localURL: "cdn/js-yaml/dist/js-yaml.min.js", + }, + { + url: "https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs/loader.js", + pkgName: "monaco-editor", + filePath: "min/vs/loader.js", + localURL: "cdn/monaco-editor/min/vs/loader.js", + }, + { + url: "https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min", + pkgName: "monaco-editor", + filePath: "min", + localURL: "cdn/monaco-editor/min", + }, + { + url: "https://cdn.jsdelivr.net/npm/vis-network@10.0.2/standalone/umd/vis-network.min.js", + pkgName: "vis-network", + filePath: "standalone/umd/vis-network.min.js", + localURL: "cdn/vis-network/standalone/umd/vis-network.min.js", + }, + { + url: "https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js", + pkgName: "qrcodejs", + filePath: "qrcode.min.js", + localURL: "cdn/qrcodejs/qrcode.min.js", + }, + } + + for _, tt := range tests { + t.Run(tt.pkgName, func(t *testing.T) { + pkgName, filePath, localURL := parseCDNURL(tt.url) + if pkgName != tt.pkgName { + t.Errorf("pkgName: expected %q, got %q", tt.pkgName, pkgName) + } + if filePath != tt.filePath { + t.Errorf("filePath: expected %q, got %q", tt.filePath, filePath) + } + if localURL != tt.localURL { + t.Errorf("localURL: expected %q, got %q", tt.localURL, localURL) + } + }) + } +} + +func TestPatchHTML(t *testing.T) { + urls := extractURLs(htmlFixtures) + + t.Run("hls", func(t *testing.T) { + patched := patchHTML(htmlFixtures["hls.html"], urls) + if !strings.Contains(patched, `src="cdn/hls.js/index.js"`) { + t.Error("hls.js src not patched") + } + if strings.Contains(patched, "cdn.jsdelivr.net") { + t.Error("CDN URL still present") + } + if !strings.Contains(patched, `