package shortcut import ( "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "lazyrun/internal/icon" "lazyrun/internal/store" ) var pngMagic = []byte("\x89PNG\r\n\x1a\n") // testEnv mengarahkan semua path XDG ke tempdir dan mematikan tool eksternal, // supaya tes tidak menyentuh ~/.local atau ~/Desktop milik user. func testEnv(t *testing.T) string { t.Helper() home := t.TempDir() t.Setenv("HOME", home) t.Setenv("XDG_DATA_HOME", "") t.Setenv("XDG_CONFIG_HOME", "") t.Setenv("PATH", "") // tes tidak boleh menyentuh internet: arahkan pencarian ikon ke port mati icon.SetSearchEndpoint("http://127.0.0.1:1") return home } func fakeBinary(t *testing.T, dir, name string) string { t.Helper() path := filepath.Join(dir, name) if err := os.WriteFile(path, append([]byte{0x7f, 'E', 'L', 'F'}, []byte("fake")...), 0o755); err != nil { t.Fatal(err) } return path } func TestInstallExecutable(t *testing.T) { home := testEnv(t) src := fakeBinary(t, home, "winbox") sc, err := InstallExecutable(ExecOptions{ BinaryPath: src, Name: "Winbox", Category: "Network", Menu: true, Desktop: true, }) if err != nil { t.Fatal(err) } if sc.BinaryPath != filepath.Join(home, ".local", "bin", "winbox") { t.Errorf("binary tersalin ke %s", sc.BinaryPath) } if st, err := os.Stat(sc.BinaryPath); err != nil || st.Mode().Perm()&0o111 == 0 { t.Errorf("binary harus executable: %v %v", st, err) } content, err := os.ReadFile(sc.DesktopPath) if err != nil { t.Fatal(err) } for _, want := range []string{ "Name=Winbox\n", "Exec=" + sc.BinaryPath + "\n", "Categories=Network;\n", "Icon=" + sc.IconPath + "\n", } { if !strings.Contains(string(content), want) { t.Errorf("isi .desktop tidak memuat %q:\n%s", want, content) } } if strings.Contains(string(content), "NoDisplay") { t.Errorf("menu tercentang tidak boleh NoDisplay:\n%s", content) } if sc.DesktopCopyPath != filepath.Join(home, "Desktop", "winbox.desktop") { t.Errorf("salinan desktop di %s", sc.DesktopCopyPath) } if st, err := os.Stat(sc.DesktopCopyPath); err != nil || st.Mode().Perm()&0o111 == 0 { t.Errorf("file di Desktop harus executable: %v %v", st, err) } if _, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "Winbox", Menu: true, Desktop: true}); err == nil { t.Error("install kedua dengan nama sama harus ditolak") } } func TestInstallExecutableStoresExecArgs(t *testing.T) { home := testEnv(t) src := fakeBinary(t, home, "args") sc, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "Args", Menu: true}) if err != nil { t.Fatal(err) } if len(sc.ExecArgs) != 1 || sc.ExecArgs[0] != sc.BinaryPath { t.Errorf("exec_args untuk tombol Open salah: %v", sc.ExecArgs) } } func TestInstallExecutableDesktopOnly(t *testing.T) { home := testEnv(t) src := fakeBinary(t, home, "tool") sc, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "Tool", Menu: false, Desktop: true}) if err != nil { t.Fatal(err) } content, err := os.ReadFile(sc.DesktopPath) if err != nil { t.Fatal(err) } if !strings.Contains(string(content), "NoDisplay=true") { t.Errorf("desktop-only harus disembunyikan dari menu:\n%s", content) } } func TestInstallExecutableRejectsNonBinary(t *testing.T) { home := testEnv(t) path := filepath.Join(home, "notes.txt") if err := os.WriteFile(path, []byte("halo"), 0o644); err != nil { t.Fatal(err) } if _, err := InstallExecutable(ExecOptions{BinaryPath: path, Name: "Notes", Menu: true}); err == nil { t.Error("file teks biasa harus ditolak") } } func TestInstallExecutableRequiresTarget(t *testing.T) { home := testEnv(t) src := fakeBinary(t, home, "tool") if _, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "Tool"}); err == nil { t.Error("tanpa target harus ditolak") } } func TestInstallWebApp(t *testing.T) { testEnv(t) page := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/": w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(``)) case "/fav.png": _, _ = w.Write(append(pngMagic, []byte("fake")...)) default: http.NotFound(w, r) } })) defer page.Close() sc, err := InstallWebApp(WebOptions{ URL: page.URL, Name: "WhatsApp Web", Browser: BrowserArg{Name: "Chromium", Exec: []string{"/usr/bin/chromium"}}, Menu: true, Desktop: true, }) if err != nil { t.Fatal(err) } content, err := os.ReadFile(sc.DesktopPath) if err != nil { t.Fatal(err) } if !strings.Contains(string(content), "Exec=/usr/bin/chromium --app="+page.URL+"\n") { t.Errorf("Exec PWA salah:\n%s", content) } if filepath.Ext(sc.IconPath) != ".png" { t.Errorf("favicon harus tersimpan sebagai .png, dapat %s", sc.IconPath) } if st, err := os.Stat(sc.IconPath); err != nil || st.Size() == 0 { t.Errorf("ikon favicon tidak tertulis: %v %v", st, err) } if sc.Type != store.TypeWebApp { t.Errorf("type = %s", sc.Type) } if sc.URL != page.URL { t.Errorf("url = %s", sc.URL) } } func TestInstallWebAppFirefoxUsesNewWindow(t *testing.T) { testEnv(t) sc, err := InstallWebApp(WebOptions{ URL: "https://example.com", Name: "Example", Browser: BrowserArg{Name: "Firefox", Exec: []string{"/usr/bin/firefox"}, Firefox: true}, Menu: true, }) if err != nil { t.Fatal(err) } content, err := os.ReadFile(sc.DesktopPath) if err != nil { t.Fatal(err) } if !strings.Contains(string(content), "Exec=/usr/bin/firefox --new-window https://example.com\n") { t.Errorf("Exec Firefox salah:\n%s", content) } if len(sc.ExecArgs) != 3 || sc.ExecArgs[2] != "https://example.com" { t.Errorf("exec_args PWA salah: %v", sc.ExecArgs) } } func TestInstallWebAppFlatpakBrowser(t *testing.T) { testEnv(t) sc, err := InstallWebApp(WebOptions{ URL: "https://example.com", Name: "Example", Browser: BrowserArg{Name: "Chromium (Flatpak)", Exec: []string{"flatpak", "run", "org.chromium.Chromium"}}, Menu: true, }) if err != nil { t.Fatal(err) } content, err := os.ReadFile(sc.DesktopPath) if err != nil { t.Fatal(err) } if !strings.Contains(string(content), "Exec=flatpak run org.chromium.Chromium --app=https://example.com\n") { t.Errorf("Exec flatpak salah:\n%s", content) } } func TestUninstall(t *testing.T) { home := testEnv(t) src := fakeBinary(t, home, "gone") sc, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "Gone", Menu: true, Desktop: true}) if err != nil { t.Fatal(err) } if err := Uninstall(sc, RemoveOptions{Binary: true}); err != nil { t.Fatal(err) } for _, path := range []string{sc.DesktopPath, sc.DesktopCopyPath, sc.IconPath, sc.BinaryPath} { if _, err := os.Stat(path); !os.IsNotExist(err) { t.Errorf("%s masih ada setelah uninstall", path) } } // uninstall kedua harus aman (file sudah tidak ada) if err := Uninstall(sc, RemoveOptions{Binary: true}); err != nil { t.Errorf("uninstall ulang: %v", err) } } func TestUninstallKeepsBinaryByDefault(t *testing.T) { home := testEnv(t) src := fakeBinary(t, home, "keep") sc, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "Keep", Menu: true}) if err != nil { t.Fatal(err) } if err := Uninstall(sc, RemoveOptions{}); err != nil { t.Fatal(err) } if _, err := os.Stat(sc.BinaryPath); err != nil { t.Errorf("binary harus tetap ada: %v", err) } } func TestScriptWrapperPrefersOriginalFile(t *testing.T) { home := testEnv(t) dir := filepath.Join(home, "proyek") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } src := filepath.Join(dir, "bench.sh") if err := os.WriteFile(src, []byte("#!/bin/sh\necho asli \"$(pwd)\"\n"), 0o755); err != nil { t.Fatal(err) } sc, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "Bench", Menu: true}) if err != nil { t.Fatal(err) } wrapper, err := os.ReadFile(sc.BinaryPath) if err != nil { t.Fatal(err) } text := string(wrapper) if !strings.Contains(text, shellQuote(src)) { t.Errorf("wrapper tidak memakai script asli:\n%s", text) } if !strings.Contains(text, shellQuote(dir)) { t.Errorf("wrapper tidak cd ke folder asli:\n%s", text) } if !strings.Contains(text, "PATH=\"$HOME/.local/bin:$PATH\"") { t.Errorf("wrapper tidak menyiapkan PATH:\n%s", text) } } func TestInstallCLIAppGetsTerminalEntry(t *testing.T) { testEnv(t) // pakai binary tes ini sendiri: ELF asli tanpa library GUI self, err := os.Executable() if err != nil { t.Fatal(err) } src := self // binary tanpa library GUI -> dianggap aplikasi terminal terminal := RunsInTerminal(src) if !terminal { t.Fatal("binary CLI harus terdeteksi butuh terminal") } sc, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "TUI Tool", Menu: true, Terminal: terminal}) if err != nil { t.Fatal(err) } content, err := os.ReadFile(sc.DesktopPath) if err != nil { t.Fatal(err) } if !strings.Contains(string(content), "Terminal=true\n") { t.Errorf("aplikasi CLI harus Terminal=true:\n%s", content) } } func TestInstallGUIAppKeepsTerminalFalse(t *testing.T) { home := testEnv(t) src := fakeBinary(t, home, "gui-tool") // simulasi aplikasi GUI: tidak butuh terminal sc, err := InstallExecutable(ExecOptions{BinaryPath: src, Name: "GUI Tool", Menu: true, Terminal: false}) if err != nil { t.Fatal(err) } content, err := os.ReadFile(sc.DesktopPath) if err != nil { t.Fatal(err) } if !strings.Contains(string(content), "Terminal=false\n") { t.Errorf("aplikasi GUI tidak boleh membuka terminal:\n%s", content) } }