@@ -97,6 +97,16 @@ def side_effect(url, _new=0, _autoraise=True) -> bool:
9797 yield mock_open
9898
9999
100+ @pytest .fixture
101+ def mock_requests_get () -> Generator [MagicMock , None , None ]:
102+ with patch ("requests.get" ) as mock_get :
103+ mock_response = MagicMock ()
104+ mock_response .status_code = 200
105+ mock_response .content = b"mocked http request"
106+ mock_get .return_value = mock_response
107+ yield mock_get
108+
109+
100110@pytest .mark .anyio
101111@pytest .mark .parametrize (
102112 "arguments" , [{"id" : "1234567890" }, {"title" : "test note" }, {"id" : "1234567890" , "title" : "test note" }]
@@ -268,6 +278,112 @@ async def test_replace_note_failed(
268278 assert len (ctx .request_context .lifespan_context .futures ) == 0
269279
270280
281+ @pytest .mark .anyio
282+ @pytest .mark .parametrize (
283+ "arguments,expect_req_params" ,
284+ [
285+ (
286+ {"id" : "123456" , "file" : "dGVzdA==" , "filename" : "test.txt" },
287+ {"id" : "123456" , "file" : "dGVzdA==" , "filename" : "test.txt" },
288+ ),
289+ (
290+ {"title" : "sample note" , "file" : "dGVzdA==" , "filename" : "test.txt" },
291+ {"title" : "sample note" , "file" : "dGVzdA==" , "filename" : "test.txt" },
292+ ),
293+ (
294+ {"id" : "123456" , "file" : "dGVzdA==" , "filename" : "test.txt" , "header" : "supplement" },
295+ {"id" : "123456" , "file" : "dGVzdA==" , "filename" : "test.txt" , "header" : "supplement" },
296+ ),
297+ (
298+ {"id" : "123456" , "file" : "dGVzdA==" , "filename" : "test.txt" , "mode" : "prepend" },
299+ {"id" : "123456" , "file" : "dGVzdA==" , "filename" : "test.txt" , "mode" : "prepend" },
300+ ),
301+ ],
302+ )
303+ async def test_add_file (
304+ temp_socket : Path ,
305+ mcp_server : Tuple [FastMCP , Context ],
306+ mock_webbrowser : MagicMock ,
307+ arguments : dict ,
308+ expect_req_params : dict ,
309+ ) -> None :
310+ s , ctx = mcp_server
311+ mock_webbrowser .stubbed_queries = {}
312+
313+ await s ._tool_manager .call_tool ("add_file" , arguments = arguments , context = ctx )
314+ assert len (ctx .request_context .lifespan_context .futures ) == 0
315+
316+ req_params = {
317+ "selected" : "no" ,
318+ "open_note" : "no" ,
319+ "new_window" : "no" ,
320+ "show_window" : "no" ,
321+ "edit" : "no" ,
322+ "x-success" : f"xfwder://{ temp_socket .stem } /{ ctx .request_id } /success" ,
323+ "x-error" : f"xfwder://{ temp_socket .stem } /{ ctx .request_id } /error" ,
324+ }
325+ req_params .update (expect_req_params )
326+ mock_webbrowser .assert_called_once_with (
327+ f"{ BASE_URL } /add-file?{ urlencode (sorted (req_params .items ()), quote_via = quote )} "
328+ )
329+
330+
331+ @pytest .mark .anyio
332+ @pytest .mark .parametrize (
333+ "arguments,expect_req_params" ,
334+ [
335+ (
336+ {"id" : "123456" , "file" : "http://example.com" , "filename" : "test.txt" },
337+ {"id" : "123456" , "file" : "bW9ja2VkIGh0dHAgcmVxdWVzdA==" , "filename" : "test.txt" },
338+ ),
339+ (
340+ {"title" : "sample note" , "file" : "https://example.com" , "filename" : "test.txt" },
341+ {"title" : "sample note" , "file" : "bW9ja2VkIGh0dHAgcmVxdWVzdA==" , "filename" : "test.txt" },
342+ ),
343+ ],
344+ )
345+ async def test_add_file_http_request (
346+ temp_socket : Path ,
347+ mcp_server : Tuple [FastMCP , Context ],
348+ mock_webbrowser : MagicMock ,
349+ mock_requests_get : MagicMock ,
350+ arguments : dict ,
351+ expect_req_params : dict ,
352+ ) -> None :
353+ s , ctx = mcp_server
354+ mock_webbrowser .stubbed_queries = {}
355+
356+ await s ._tool_manager .call_tool ("add_file" , arguments = arguments , context = ctx )
357+ assert len (ctx .request_context .lifespan_context .futures ) == 0
358+
359+ req_params = {
360+ "selected" : "no" ,
361+ "open_note" : "no" ,
362+ "new_window" : "no" ,
363+ "show_window" : "no" ,
364+ "edit" : "no" ,
365+ "x-success" : f"xfwder://{ temp_socket .stem } /{ ctx .request_id } /success" ,
366+ "x-error" : f"xfwder://{ temp_socket .stem } /{ ctx .request_id } /error" ,
367+ }
368+ req_params .update (expect_req_params )
369+ mock_webbrowser .assert_called_once_with (
370+ f"{ BASE_URL } /add-file?{ urlencode (sorted (req_params .items ()), quote_via = quote )} "
371+ )
372+ mock_requests_get .assert_called_once_with (arguments ["file" ])
373+
374+
375+ @pytest .mark .anyio
376+ async def test_add_file_failed (
377+ mcp_server : Tuple [FastMCP , Context [Any , AppContext ]], mock_webbrowser_error : MagicMock
378+ ) -> None :
379+ s , ctx = mcp_server
380+ with pytest .raises (ToolError ) as excinfo :
381+ await s ._tool_manager .call_tool ("add_file" , arguments = {"file" : "dGVzdA==" , "filename" : "test.txt" }, context = ctx )
382+
383+ assert "test error message" in str (excinfo .value )
384+ assert len (ctx .request_context .lifespan_context .futures ) == 0
385+
386+
271387@pytest .mark .anyio
272388async def test_tags (
273389 temp_socket : Path ,
0 commit comments