@@ -135,18 +135,16 @@ def test_open_deeplink_macos(self, mock_run):
135135 ["open" , "cursor://test" ], check = True , capture_output = True
136136 )
137137
138- @patch ("subprocess.run" )
139- def test_open_deeplink_windows (self , mock_run ):
138+ def test_open_deeplink_windows (self ):
140139 """Test opening deeplink on Windows."""
141140 with patch ("sys.platform" , "win32" ):
142- mock_run .return_value = Mock (returncode = 0 )
141+ with patch (
142+ "fastmcp.cli.install.cursor.os.startfile" , create = True
143+ ) as mock_startfile :
144+ result = open_deeplink ("cursor://test" )
143145
144- result = open_deeplink ("cursor://test" )
145-
146- assert result is True
147- mock_run .assert_called_once_with (
148- ["cmd" , "/c" , "start" , "cursor://test" ], check = True , capture_output = True
149- )
146+ assert result is True
147+ mock_startfile .assert_called_once_with ("cursor://test" )
150148
151149 @patch ("subprocess.run" )
152150 def test_open_deeplink_linux (self , mock_run ):
@@ -166,21 +164,57 @@ def test_open_deeplink_failure(self, mock_run):
166164 """Test handling of deeplink opening failure."""
167165 import subprocess
168166
169- mock_run .side_effect = subprocess .CalledProcessError (1 , ["open" ])
167+ with patch ("sys.platform" , "darwin" ):
168+ mock_run .side_effect = subprocess .CalledProcessError (1 , ["open" ])
170169
171- result = open_deeplink ("cursor://test" )
170+ result = open_deeplink ("cursor://test" )
172171
173- assert result is False
172+ assert result is False
174173
175174 @patch ("subprocess.run" )
176175 def test_open_deeplink_command_not_found (self , mock_run ):
177176 """Test handling when open command is not found."""
178- mock_run .side_effect = FileNotFoundError ()
177+ with patch ("sys.platform" , "darwin" ):
178+ mock_run .side_effect = FileNotFoundError ()
179179
180- result = open_deeplink ("cursor://test" )
180+ result = open_deeplink ("cursor://test" )
181+
182+ assert result is False
183+
184+ def test_open_deeplink_invalid_scheme (self ):
185+ """Test that non-cursor:// URLs are rejected."""
186+ result = open_deeplink ("http://malicious.com" )
187+ assert result is False
188+
189+ result = open_deeplink ("https://example.com" )
190+ assert result is False
181191
192+ result = open_deeplink ("file:///etc/passwd" )
182193 assert result is False
183194
195+ def test_open_deeplink_valid_cursor_scheme (self ):
196+ """Test that cursor:// URLs are accepted."""
197+ with patch ("sys.platform" , "darwin" ):
198+ with patch ("subprocess.run" ) as mock_run :
199+ mock_run .return_value = Mock (returncode = 0 )
200+ result = open_deeplink ("cursor://anysphere.cursor-deeplink/mcp/install" )
201+ assert result is True
202+
203+ def test_open_deeplink_empty_url (self ):
204+ """Test handling of empty URL."""
205+ result = open_deeplink ("" )
206+ assert result is False
207+
208+ def test_open_deeplink_windows_oserror (self ):
209+ """Test handling of OSError on Windows."""
210+ with patch ("sys.platform" , "win32" ):
211+ with patch (
212+ "fastmcp.cli.install.cursor.os.startfile" , create = True
213+ ) as mock_startfile :
214+ mock_startfile .side_effect = OSError ("File not found" )
215+ result = open_deeplink ("cursor://test" )
216+ assert result is False
217+
184218
185219class TestInstallCursor :
186220 """Test cursor installation functionality."""
0 commit comments