mirror of
https://github.com/opentofu/opentofu.git
synced 2026-05-28 04:15:54 -04:00
136 lines
4.8 KiB
Go
136 lines
4.8 KiB
Go
|
|
// Copyright (c) The OpenTofu Authors
|
||
|
|
// SPDX-License-Identifier: MPL-2.0
|
||
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
||
|
|
// SPDX-License-Identifier: MPL-2.0
|
||
|
|
|
||
|
|
package webbrowser
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"runtime"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
const fakeBrowserLaunchCmdOutputEnvName = "OPENTOFU_WEBBROWSER_EXEC_TEST_OUTPUT"
|
||
|
|
|
||
|
|
// TestMain overrides the test entrypoint so that we can reuse the test
|
||
|
|
// executable as a fake browser launcher command when testing
|
||
|
|
// [NewExecLauncher].
|
||
|
|
func TestMain(m *testing.M) {
|
||
|
|
if f := os.Getenv(fakeBrowserLaunchCmdOutputEnvName); f != "" {
|
||
|
|
err := fakeBrowserLauncherCommand(f)
|
||
|
|
if err != nil {
|
||
|
|
fmt.Fprintf(os.Stderr, "fake browser launcher failed: %s", err)
|
||
|
|
os.Exit(1)
|
||
|
|
}
|
||
|
|
os.Exit(0)
|
||
|
|
}
|
||
|
|
os.Exit(m.Run())
|
||
|
|
}
|
||
|
|
|
||
|
|
func fakeBrowserLauncherCommand(outputFilename string) error {
|
||
|
|
// The "exec" browser launcher must pass the URL to open in the
|
||
|
|
// first argument to the executable it launches.
|
||
|
|
url := os.Args[1]
|
||
|
|
return os.WriteFile(outputFilename, []byte(url), os.ModePerm)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestExecLauncher(t *testing.T) {
|
||
|
|
// For this test we re-use the text executable as a fake browser-launching
|
||
|
|
// program, through the special logic in [TestMain].
|
||
|
|
fakeExec := os.Args[0]
|
||
|
|
|
||
|
|
tempDir := t.TempDir()
|
||
|
|
outputFile := filepath.Join(tempDir, "browser-exec-launcher-test")
|
||
|
|
t.Setenv(fakeBrowserLaunchCmdOutputEnvName, outputFile)
|
||
|
|
|
||
|
|
launcher := NewExecLauncher(fakeExec)
|
||
|
|
err := launcher.OpenURL("http://example.com/")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
result, err := os.ReadFile(outputFile)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if got, want := string(result), "http://example.com/"; got != want {
|
||
|
|
t.Errorf("wrong URL written to output file\ngot: %s\nwant: %s", got, want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestParseBrowserEnv_success(t *testing.T) {
|
||
|
|
// ParseBrowserEnv only actually needs to work on Unix-like systems, so
|
||
|
|
// the test scenario below is not written to be portable.
|
||
|
|
// There's no runtime equivalent of the "unix" build tag, so we just
|
||
|
|
// explicitly test the two main Unix OSes we support here.
|
||
|
|
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
|
||
|
|
t.Skip("ParseBrowserEnv is only for unix systems")
|
||
|
|
}
|
||
|
|
|
||
|
|
tmpDir := t.TempDir()
|
||
|
|
fakeExec := filepath.Join(tmpDir, "fake-launch-browser")
|
||
|
|
err := os.WriteFile(fakeExec, []byte(`not a real program`), 0755)
|
||
|
|
if err != nil {
|
||
|
|
// NOTE: This test requires the temp directory to be somewhere that
|
||
|
|
// allows executables, so this won't work if the temp directory is
|
||
|
|
// on a "noexec" mount on a Unix-style system.
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
// Temporarily we'll reset the search path to just our temp directory
|
||
|
|
t.Setenv("PATH", tmpDir)
|
||
|
|
result := ParseBrowserEnv("fake-launch-browser")
|
||
|
|
if result == "" {
|
||
|
|
t.Fatal("failed to find fake executable")
|
||
|
|
}
|
||
|
|
if got, want := filepath.Base(result), "fake-launch-browser"; got != want {
|
||
|
|
t.Fatalf("returned path %q has wrong basename %q; want %q", result, got, want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestParseBrowserEnv_empty(t *testing.T) {
|
||
|
|
result := ParseBrowserEnv("")
|
||
|
|
if result != "" {
|
||
|
|
t.Errorf("returned %q, but wanted empty string", result)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestParseBrowserEnv_esrComplexSpec(t *testing.T) {
|
||
|
|
// The following tests with a strings following the more complex
|
||
|
|
// interpretation of BROWSER from http://www.catb.org/~esr/BROWSER/ , which
|
||
|
|
// OpenTofu intentionally doesn't support and so should be treated as
|
||
|
|
// if the environment variable isn't set at all.
|
||
|
|
t.Run(`with %s`, func(t *testing.T) {
|
||
|
|
// The esr proposal calls for checking whether there's a %s sequence
|
||
|
|
// in the value and then, if so, substituting the URL there and then
|
||
|
|
// passing the entire result to a shell. This is the main thing that
|
||
|
|
// different implementations did inconsistently, because it's
|
||
|
|
// unspecified whether the %s should be placed in quotes in the
|
||
|
|
// environment variable, if those quotes should be inserted by the
|
||
|
|
// program acting on the variable, or if some other shell escaping
|
||
|
|
// strategy should be used instead. We just ignore this form entirely
|
||
|
|
// because it's apparently not commonly used and it's unclear how
|
||
|
|
// to implement it without causing security problems.
|
||
|
|
result := ParseBrowserEnv("example %s")
|
||
|
|
if result != "" {
|
||
|
|
t.Errorf("returned %q, but wanted empty string", result)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
t.Run("multiple commands", func(t *testing.T) {
|
||
|
|
// The esr proposal calls for splitting the string on semicolon
|
||
|
|
// and trying one command at a time until one succeeds. That's
|
||
|
|
// ambiguous with there being a single command whose path contains
|
||
|
|
// a semicolon, so we just try to treat it as a single command and
|
||
|
|
// ignore the value if that doesn't work. In practice the need for
|
||
|
|
// multiple options to try tends to be met instead by setting BROWSER
|
||
|
|
// to refer to a wrapper script that deals with the selection policy,
|
||
|
|
// which is the pattern OpenTofu supports.
|
||
|
|
result := ParseBrowserEnv("example1;example2")
|
||
|
|
if result != "" {
|
||
|
|
t.Errorf("returned %q, but wanted empty string", result)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|