1package desktop
2
3import (
4 "context"
5 "errors"
6 "os/exec"
7 "testing"
8
9 "github.com/bnema/dumber/internal/application/port/mocks"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12)
13
14func TestBrowserLauncher_LaunchFreshWindowURL_ReturnsWithoutSpawningWhenRelayDelivers(t *testing.T) {
15 relay := mocks.NewMockBrowserLaunchRelay(t)
16 relay.EXPECT().DeliverOpenFreshWindow(context.Background(), "https://example.com").Return(true, nil)
17
18 launcher := NewBrowserLauncher(relay)
19 spawned := false
20 launcher.resolveExecutablePath = func() (string, error) {
21 t.Fatal("expected executable path resolution to be skipped")
22 return "", nil
23 }
24 launcher.startDetachedProcess = func(_ *exec.Cmd) error {
25 spawned = true
26 return nil
27 }
28
29 err := launcher.LaunchFreshWindowURL(context.Background(), "https://example.com")
30
31 require.NoError(t, err)
32 assert.False(t, spawned)
33}
34
35func TestBrowserLauncher_LaunchFreshWindowURL_FallsBackToSpawnWhenRelayMisses(t *testing.T) {
36 relay := mocks.NewMockBrowserLaunchRelay(t)
37 relay.EXPECT().DeliverOpenFreshWindow(context.Background(), "https://example.com").Return(false, nil)
38
39 launcher := NewBrowserLauncher(relay)
40 launcher.resolveExecutablePath = func() (string, error) {
41 return "/usr/bin/dumber", nil
42 }
43
44 var gotCmd *exec.Cmd
45 launcher.startDetachedProcess = func(cmd *exec.Cmd) error {
46 gotCmd = cmd
47 return nil
48 }
49
50 err := launcher.LaunchFreshWindowURL(context.Background(), "https://example.com")
51
52 require.NoError(t, err)
53 require.NotNil(t, gotCmd)
54 assert.Equal(t, "/usr/bin/dumber", gotCmd.Path)
55 assert.Equal(t, []string{"/usr/bin/dumber", "browse", "https://example.com"}, gotCmd.Args)
56 assert.Contains(t, gotCmd.Env, FreshWindowLaunchEnvVar+"=1")
57}
58
59func TestBrowserLauncher_LaunchFreshWindowURL_ReturnsUnconfirmedErrorWhenRelayDeliveryIsAmbiguous(t *testing.T) {
60 relay := mocks.NewMockBrowserLaunchRelay(t)
61 relay.EXPECT().DeliverOpenFreshWindow(context.Background(), "https://example.com").Return(true, ErrBrowserLaunchRelayUnconfirmed)
62
63 launcher := NewBrowserLauncher(relay)
64 launcher.resolveExecutablePath = func() (string, error) {
65 t.Fatal("expected executable path resolution to be skipped")
66 return "", nil
67 }
68 launcher.startDetachedProcess = func(_ *exec.Cmd) error {
69 t.Fatal("expected launch to stop after ambiguous relay delivery")
70 return nil
71 }
72
73 err := launcher.LaunchFreshWindowURL(context.Background(), "https://example.com")
74
75 require.Error(t, err)
76 assert.ErrorIs(t, err, ErrBrowserLaunchUnconfirmed)
77}
78
79func TestBrowserLauncher_LaunchFreshWindowURL_PropagatesRelayError(t *testing.T) {
80 relay := mocks.NewMockBrowserLaunchRelay(t)
81 wantErr := errors.New("relay exploded")
82 relay.EXPECT().DeliverOpenFreshWindow(context.Background(), "https://example.com").Return(false, wantErr)
83
84 launcher := NewBrowserLauncher(relay)
85 launcher.resolveExecutablePath = func() (string, error) {
86 t.Fatal("expected executable path resolution to be skipped")
87 return "", nil
88 }
89 launcher.startDetachedProcess = func(_ *exec.Cmd) error {
90 t.Fatal("expected launch to stop after relay error")
91 return nil
92 }
93
94 err := launcher.LaunchFreshWindowURL(context.Background(), "https://example.com")
95
96 require.Error(t, err)
97 require.ErrorIs(t, err, wantErr)
98 assert.NotContains(t, err.Error(), "https://example.com")
99}
100
101func TestBrowserLauncher_LaunchFreshWindowURL_NilLauncherReturnsError(t *testing.T) {
102 var launcher *BrowserLauncher
103
104 err := launcher.LaunchFreshWindowURL(context.Background(), "https://example.com")
105
106 require.Error(t, err)
107 assert.Contains(t, err.Error(), "unavailable")
108}