1+ <?php
2+
3+ namespace Tests \Feature ;
4+
5+ use Illuminate \Foundation \Testing \RefreshDatabase ;
6+ use Mockery ;
7+ use Native \Laravel \Facades \Shell ;
8+ use Tests \TestCase ;
9+
10+ class OpenExternalTest extends TestCase
11+ {
12+ use RefreshDatabase;
13+
14+ protected function setUp (): void
15+ {
16+ parent ::setUp ();
17+
18+ // Reset Shell facade mock for each test
19+ Shell::clearResolvedInstance ('Shell ' );
20+ }
21+
22+ protected function tearDown (): void
23+ {
24+ Mockery::close ();
25+ parent ::tearDown ();
26+ }
27+
28+ public function test_opens_valid_url_successfully (): void
29+ {
30+ Shell::shouldReceive ('openExternal ' )
31+ ->once ()
32+ ->with ('https://github.com/vijaythecoder/clueless ' );
33+
34+ $ response = $ this ->postJson ('/api/open-external ' , [
35+ 'url ' => 'https://github.com/vijaythecoder/clueless '
36+ ]);
37+
38+ $ response ->assertStatus (200 )
39+ ->assertJson ([
40+ 'success ' => true
41+ ]);
42+ }
43+
44+ public function test_opens_openai_url_successfully (): void
45+ {
46+ Shell::shouldReceive ('openExternal ' )
47+ ->once ()
48+ ->with ('https://platform.openai.com/api-keys ' );
49+
50+ $ response = $ this ->postJson ('/api/open-external ' , [
51+ 'url ' => 'https://platform.openai.com/api-keys '
52+ ]);
53+
54+ $ response ->assertStatus (200 )
55+ ->assertJson ([
56+ 'success ' => true
57+ ]);
58+ }
59+
60+ public function test_rejects_invalid_url (): void
61+ {
62+ $ response = $ this ->postJson ('/api/open-external ' , [
63+ 'url ' => 'not-a-valid-url '
64+ ]);
65+
66+ $ response ->assertStatus (400 )
67+ ->assertJson ([
68+ 'error ' => 'Invalid URL '
69+ ]);
70+ }
71+
72+ public function test_rejects_malicious_urls (): void
73+ {
74+ $ maliciousUrls = [
75+ 'javascript:alert("xss") ' ,
76+ 'data:text/html,<script>alert("xss")</script> ' ,
77+ 'not-a-url '
78+ ];
79+
80+ foreach ($ maliciousUrls as $ url ) {
81+ $ response = $ this ->postJson ('/api/open-external ' , [
82+ 'url ' => $ url
83+ ]);
84+
85+ $ response ->assertStatus (400 )
86+ ->assertJson ([
87+ 'error ' => 'Invalid URL '
88+ ]);
89+ }
90+ }
91+
92+ public function test_requires_url_parameter (): void
93+ {
94+ $ response = $ this ->postJson ('/api/open-external ' , []);
95+
96+ $ response ->assertStatus (400 );
97+ }
98+
99+ public function test_handles_empty_url (): void
100+ {
101+ $ response = $ this ->postJson ('/api/open-external ' , [
102+ 'url ' => ''
103+ ]);
104+
105+ $ response ->assertStatus (400 )
106+ ->assertJson ([
107+ 'error ' => 'Invalid URL '
108+ ]);
109+ }
110+
111+ public function test_handles_null_url (): void
112+ {
113+ $ response = $ this ->postJson ('/api/open-external ' , [
114+ 'url ' => null
115+ ]);
116+
117+ $ response ->assertStatus (400 )
118+ ->assertJson ([
119+ 'error ' => 'Invalid URL '
120+ ]);
121+ }
122+
123+ public function test_allows_https_urls (): void
124+ {
125+ $ validUrls = [
126+ 'https://github.com ' ,
127+ 'https://platform.openai.com ' ,
128+ 'https://www.example.com ' ,
129+ 'https://subdomain.example.com/path?query=value '
130+ ];
131+
132+ foreach ($ validUrls as $ url ) {
133+ Shell::shouldReceive ('openExternal ' )
134+ ->once ()
135+ ->with ($ url );
136+
137+ $ response = $ this ->postJson ('/api/open-external ' , [
138+ 'url ' => $ url
139+ ]);
140+
141+ $ response ->assertStatus (200 )
142+ ->assertJson ([
143+ 'success ' => true
144+ ]);
145+ }
146+ }
147+
148+ public function test_allows_http_urls (): void
149+ {
150+ Shell::shouldReceive ('openExternal ' )
151+ ->once ()
152+ ->with ('http://example.com ' );
153+
154+ $ response = $ this ->postJson ('/api/open-external ' , [
155+ 'url ' => 'http://example.com '
156+ ]);
157+
158+ $ response ->assertStatus (200 )
159+ ->assertJson ([
160+ 'success ' => true
161+ ]);
162+ }
163+
164+ public function test_handles_shell_exception (): void
165+ {
166+ Shell::shouldReceive ('openExternal ' )
167+ ->once ()
168+ ->with ('https://github.com ' )
169+ ->andThrow (new \Exception ('Shell error ' ));
170+
171+ $ response = $ this ->postJson ('/api/open-external ' , [
172+ 'url ' => 'https://github.com '
173+ ]);
174+
175+ // The route doesn't handle exceptions explicitly, so it would return 500
176+ // In a real implementation, you might want to catch and handle this
177+ $ response ->assertStatus (500 );
178+ }
179+ }
0 commit comments