Jeremiah Lowin commited on
Commit
be6f863
·
1 Parent(s): ad0696c

Handle URL tests

Browse files
tests/resources/test_file_resources.py CHANGED
@@ -3,6 +3,7 @@ import os
3
  import pytest
4
  from pathlib import Path
5
  from tempfile import NamedTemporaryFile
 
6
 
7
  from fastmcp.resources import FileResource
8
 
@@ -30,7 +31,7 @@ class TestFileResource:
30
  def test_file_resource_creation(self, temp_file: Path):
31
  """Test creating a FileResource."""
32
  resource = FileResource(
33
- uri=temp_file.as_uri(),
34
  name="test",
35
  description="test file",
36
  path=temp_file,
@@ -45,9 +46,9 @@ class TestFileResource:
45
  def test_file_resource_str_path_conversion(self, temp_file: Path):
46
  """Test FileResource handles string paths."""
47
  resource = FileResource(
48
- uri=f"file://{temp_file}",
49
  name="test",
50
- path=str(temp_file),
51
  )
52
  assert isinstance(resource.path, Path)
53
  assert resource.path.is_absolute()
@@ -55,7 +56,7 @@ class TestFileResource:
55
  async def test_read_text_file(self, temp_file: Path):
56
  """Test reading a text file."""
57
  resource = FileResource(
58
- uri=f"file://{temp_file}",
59
  name="test",
60
  path=temp_file,
61
  )
@@ -66,7 +67,7 @@ class TestFileResource:
66
  async def test_read_binary_file(self, temp_file: Path):
67
  """Test reading a file as binary."""
68
  resource = FileResource(
69
- uri=f"file://{temp_file}",
70
  name="test",
71
  path=temp_file,
72
  is_binary=True,
@@ -79,7 +80,7 @@ class TestFileResource:
79
  """Test error on relative path."""
80
  with pytest.raises(ValueError, match="Path must be absolute"):
81
  FileResource(
82
- uri="file:///test.txt",
83
  name="test",
84
  path=Path("test.txt"),
85
  )
@@ -89,7 +90,7 @@ class TestFileResource:
89
  # Create path to non-existent file
90
  missing = temp_file.parent / "missing.txt"
91
  resource = FileResource(
92
- uri="file:///missing.txt",
93
  name="test",
94
  path=missing,
95
  )
@@ -104,7 +105,7 @@ class TestFileResource:
104
  temp_file.chmod(0o000) # Remove all permissions
105
  try:
106
  resource = FileResource(
107
- uri=temp_file.as_uri(),
108
  name="test",
109
  path=temp_file,
110
  )
 
3
  import pytest
4
  from pathlib import Path
5
  from tempfile import NamedTemporaryFile
6
+ from pydantic import FileUrl
7
 
8
  from fastmcp.resources import FileResource
9
 
 
31
  def test_file_resource_creation(self, temp_file: Path):
32
  """Test creating a FileResource."""
33
  resource = FileResource(
34
+ uri=FileUrl(temp_file.as_uri()),
35
  name="test",
36
  description="test file",
37
  path=temp_file,
 
46
  def test_file_resource_str_path_conversion(self, temp_file: Path):
47
  """Test FileResource handles string paths."""
48
  resource = FileResource(
49
+ uri=FileUrl(f"file://{temp_file}"),
50
  name="test",
51
+ path=Path(str(temp_file)),
52
  )
53
  assert isinstance(resource.path, Path)
54
  assert resource.path.is_absolute()
 
56
  async def test_read_text_file(self, temp_file: Path):
57
  """Test reading a text file."""
58
  resource = FileResource(
59
+ uri=FileUrl(f"file://{temp_file}"),
60
  name="test",
61
  path=temp_file,
62
  )
 
67
  async def test_read_binary_file(self, temp_file: Path):
68
  """Test reading a file as binary."""
69
  resource = FileResource(
70
+ uri=FileUrl(f"file://{temp_file}"),
71
  name="test",
72
  path=temp_file,
73
  is_binary=True,
 
80
  """Test error on relative path."""
81
  with pytest.raises(ValueError, match="Path must be absolute"):
82
  FileResource(
83
+ uri=FileUrl("file:///test.txt"),
84
  name="test",
85
  path=Path("test.txt"),
86
  )
 
90
  # Create path to non-existent file
91
  missing = temp_file.parent / "missing.txt"
92
  resource = FileResource(
93
+ uri=FileUrl("file:///missing.txt"),
94
  name="test",
95
  path=missing,
96
  )
 
105
  temp_file.chmod(0o000) # Remove all permissions
106
  try:
107
  resource = FileResource(
108
+ uri=FileUrl(temp_file.as_uri()),
109
  name="test",
110
  path=temp_file,
111
  )
tests/resources/test_function_resources.py CHANGED
@@ -1,4 +1,4 @@
1
- from pydantic import BaseModel
2
  import pytest
3
  from fastmcp.resources import FunctionResource
4
 
@@ -13,7 +13,7 @@ class TestFunctionResource:
13
  return "test content"
14
 
15
  resource = FunctionResource(
16
- uri="fn://test",
17
  name="test",
18
  description="test function",
19
  fn=my_func,
@@ -31,7 +31,7 @@ class TestFunctionResource:
31
  return "Hello, world!"
32
 
33
  resource = FunctionResource(
34
- uri="function://test",
35
  name="test",
36
  fn=get_data,
37
  )
@@ -46,7 +46,7 @@ class TestFunctionResource:
46
  return b"Hello, world!"
47
 
48
  resource = FunctionResource(
49
- uri="function://test",
50
  name="test",
51
  fn=get_data,
52
  )
@@ -60,11 +60,12 @@ class TestFunctionResource:
60
  return {"key": "value"}
61
 
62
  resource = FunctionResource(
63
- uri="function://test",
64
  name="test",
65
  fn=get_data,
66
  )
67
  content = await resource.read()
 
68
  assert '"key": "value"' in content
69
 
70
  async def test_error_handling(self):
@@ -74,7 +75,7 @@ class TestFunctionResource:
74
  raise ValueError("Test error")
75
 
76
  resource = FunctionResource(
77
- uri="function://test",
78
  name="test",
79
  fn=failing_func,
80
  )
@@ -88,7 +89,7 @@ class TestFunctionResource:
88
  name: str
89
 
90
  resource = FunctionResource(
91
- uri="function://test",
92
  name="test",
93
  fn=lambda: MyModel(name="test"),
94
  )
@@ -106,7 +107,7 @@ class TestFunctionResource:
106
  return CustomData()
107
 
108
  resource = FunctionResource(
109
- uri="function://test",
110
  name="test",
111
  fn=get_data,
112
  )
 
1
+ from pydantic import BaseModel, AnyUrl
2
  import pytest
3
  from fastmcp.resources import FunctionResource
4
 
 
13
  return "test content"
14
 
15
  resource = FunctionResource(
16
+ uri=AnyUrl("fn://test"),
17
  name="test",
18
  description="test function",
19
  fn=my_func,
 
31
  return "Hello, world!"
32
 
33
  resource = FunctionResource(
34
+ uri=AnyUrl("function://test"),
35
  name="test",
36
  fn=get_data,
37
  )
 
46
  return b"Hello, world!"
47
 
48
  resource = FunctionResource(
49
+ uri=AnyUrl("function://test"),
50
  name="test",
51
  fn=get_data,
52
  )
 
60
  return {"key": "value"}
61
 
62
  resource = FunctionResource(
63
+ uri=AnyUrl("function://test"),
64
  name="test",
65
  fn=get_data,
66
  )
67
  content = await resource.read()
68
+ assert isinstance(content, str)
69
  assert '"key": "value"' in content
70
 
71
  async def test_error_handling(self):
 
75
  raise ValueError("Test error")
76
 
77
  resource = FunctionResource(
78
+ uri=AnyUrl("function://test"),
79
  name="test",
80
  fn=failing_func,
81
  )
 
89
  name: str
90
 
91
  resource = FunctionResource(
92
+ uri=AnyUrl("function://test"),
93
  name="test",
94
  fn=lambda: MyModel(name="test"),
95
  )
 
107
  return CustomData()
108
 
109
  resource = FunctionResource(
110
+ uri=AnyUrl("function://test"),
111
  name="test",
112
  fn=get_data,
113
  )
tests/resources/test_resource_manager.py CHANGED
@@ -1,6 +1,7 @@
1
  import pytest
2
  from pathlib import Path
3
  from tempfile import NamedTemporaryFile
 
4
 
5
  from fastmcp.resources import (
6
  FileResource,
@@ -34,7 +35,7 @@ class TestResourceManager:
34
  """Test adding a resource."""
35
  manager = ResourceManager()
36
  resource = FileResource(
37
- uri=f"file://{temp_file}",
38
  name="test",
39
  path=temp_file,
40
  )
@@ -46,7 +47,7 @@ class TestResourceManager:
46
  """Test adding the same resource twice."""
47
  manager = ResourceManager()
48
  resource = FileResource(
49
- uri=f"file://{temp_file}",
50
  name="test",
51
  path=temp_file,
52
  )
@@ -59,7 +60,7 @@ class TestResourceManager:
59
  """Test warning on duplicate resources."""
60
  manager = ResourceManager()
61
  resource = FileResource(
62
- uri=f"file://{temp_file}",
63
  name="test",
64
  path=temp_file,
65
  )
@@ -71,7 +72,7 @@ class TestResourceManager:
71
  """Test disabling warning on duplicate resources."""
72
  manager = ResourceManager(warn_on_duplicate_resources=False)
73
  resource = FileResource(
74
- uri=f"file://{temp_file}",
75
  name="test",
76
  path=temp_file,
77
  )
@@ -83,7 +84,7 @@ class TestResourceManager:
83
  """Test getting a resource by URI."""
84
  manager = ResourceManager()
85
  resource = FileResource(
86
- uri=f"file://{temp_file}",
87
  name="test",
88
  path=temp_file,
89
  )
@@ -105,7 +106,7 @@ class TestResourceManager:
105
  )
106
  manager._templates[template.uri_template] = template
107
 
108
- resource = await manager.get_resource("greet://world")
109
  assert isinstance(resource, FunctionResource)
110
  content = await resource.read()
111
  assert content == "Hello, world!"
@@ -114,18 +115,18 @@ class TestResourceManager:
114
  """Test getting a non-existent resource."""
115
  manager = ResourceManager()
116
  with pytest.raises(ValueError, match="Unknown resource"):
117
- await manager.get_resource("unknown://test")
118
 
119
  def test_list_resources(self, temp_file: Path):
120
  """Test listing all resources."""
121
  manager = ResourceManager()
122
  resource1 = FileResource(
123
- uri=f"file://{temp_file}",
124
  name="test1",
125
  path=temp_file,
126
  )
127
  resource2 = FileResource(
128
- uri=f"file://{temp_file}2",
129
  name="test2",
130
  path=temp_file,
131
  )
 
1
  import pytest
2
  from pathlib import Path
3
  from tempfile import NamedTemporaryFile
4
+ from pydantic import AnyUrl, FileUrl
5
 
6
  from fastmcp.resources import (
7
  FileResource,
 
35
  """Test adding a resource."""
36
  manager = ResourceManager()
37
  resource = FileResource(
38
+ uri=FileUrl(f"file://{temp_file}"),
39
  name="test",
40
  path=temp_file,
41
  )
 
47
  """Test adding the same resource twice."""
48
  manager = ResourceManager()
49
  resource = FileResource(
50
+ uri=FileUrl(f"file://{temp_file}"),
51
  name="test",
52
  path=temp_file,
53
  )
 
60
  """Test warning on duplicate resources."""
61
  manager = ResourceManager()
62
  resource = FileResource(
63
+ uri=FileUrl(f"file://{temp_file}"),
64
  name="test",
65
  path=temp_file,
66
  )
 
72
  """Test disabling warning on duplicate resources."""
73
  manager = ResourceManager(warn_on_duplicate_resources=False)
74
  resource = FileResource(
75
+ uri=FileUrl(f"file://{temp_file}"),
76
  name="test",
77
  path=temp_file,
78
  )
 
84
  """Test getting a resource by URI."""
85
  manager = ResourceManager()
86
  resource = FileResource(
87
+ uri=FileUrl(f"file://{temp_file}"),
88
  name="test",
89
  path=temp_file,
90
  )
 
106
  )
107
  manager._templates[template.uri_template] = template
108
 
109
+ resource = await manager.get_resource(AnyUrl("greet://world"))
110
  assert isinstance(resource, FunctionResource)
111
  content = await resource.read()
112
  assert content == "Hello, world!"
 
115
  """Test getting a non-existent resource."""
116
  manager = ResourceManager()
117
  with pytest.raises(ValueError, match="Unknown resource"):
118
+ await manager.get_resource(AnyUrl("unknown://test"))
119
 
120
  def test_list_resources(self, temp_file: Path):
121
  """Test listing all resources."""
122
  manager = ResourceManager()
123
  resource1 = FileResource(
124
+ uri=FileUrl(f"file://{temp_file}"),
125
  name="test1",
126
  path=temp_file,
127
  )
128
  resource2 = FileResource(
129
+ uri=FileUrl(f"file://{temp_file}2"),
130
  name="test2",
131
  path=temp_file,
132
  )
tests/resources/test_resource_template.py CHANGED
@@ -1,121 +1,71 @@
 
1
  import pytest
2
- from fastmcp.resources import ResourceTemplate, FunctionResource
 
 
3
 
4
 
5
  class TestResourceTemplate:
6
  """Test ResourceTemplate functionality."""
7
 
8
- def test_template_from_function(self):
9
  """Test creating a template from a function."""
10
 
11
- def weather(city: str, units: str = "metric") -> str:
12
- return f"Weather in {city} ({units})"
13
 
14
  template = ResourceTemplate.from_function(
15
- fn=weather,
16
- uri_template="weather://{city}/current",
17
- name="weather",
18
- description="Get current weather",
19
  )
20
-
21
- assert template.name == "weather"
22
- assert template.uri_template == "weather://{city}/current"
23
- assert template.mime_type == "text/plain"
24
- assert "city" in template.parameters["properties"]
25
-
26
- def test_template_from_lambda_error(self):
27
- """Test error when creating template from lambda without name."""
28
- with pytest.raises(
29
- ValueError, match="You must provide a name for lambda functions"
30
- ):
31
- ResourceTemplate.from_function(
32
- fn=lambda x: x,
33
- uri_template="test://{x}",
34
- )
35
 
36
  def test_template_matches(self):
37
- """Test URI matching against template."""
38
 
39
- def dummy(x: str) -> str:
40
- return x
41
 
42
  template = ResourceTemplate.from_function(
43
- fn=dummy,
44
- uri_template="test://{x}/value",
45
  name="test",
46
  )
47
 
48
- # Test matching URI
49
- params = template.matches("test://hello/value")
50
- assert params == {"x": "hello"}
51
 
52
- # Test non-matching URI
53
- params = template.matches("test://hello/wrong")
54
- assert params is None
55
 
56
- async def test_create_text_resource(self):
57
- """Test creating a text resource from template."""
58
 
59
- def greet(name: str) -> str:
60
- return f"Hello, {name}!"
61
 
62
  template = ResourceTemplate.from_function(
63
- fn=greet,
64
- uri_template="greet://{name}",
65
- name="greeter",
66
- )
67
-
68
- resource = await template.create_resource(
69
- "greet://world",
70
- {"name": "world"},
71
- )
72
-
73
- assert isinstance(resource, FunctionResource)
74
- content = await resource.read()
75
- assert content == "Hello, world!"
76
-
77
- async def test_create_binary_resource(self):
78
- """Test creating a binary resource from template."""
79
-
80
- def get_bytes(value: str) -> bytes:
81
- return value.encode()
82
-
83
- template = ResourceTemplate.from_function(
84
- fn=get_bytes,
85
- uri_template="bytes://{value}",
86
- name="bytes",
87
- )
88
-
89
- resource = await template.create_resource(
90
- "bytes://test",
91
- {"value": "test"},
92
- )
93
-
94
- assert isinstance(resource, FunctionResource)
95
- content = await resource.read()
96
- assert content == b"test"
97
-
98
- async def test_json_conversion(self):
99
- """Test automatic JSON conversion of non-string/bytes results."""
100
-
101
- def get_data(key: str) -> dict:
102
- return {"key": key, "value": 123}
103
-
104
- template = ResourceTemplate.from_function(
105
- fn=get_data,
106
- uri_template="data://{key}",
107
- name="data",
108
  )
109
 
110
  resource = await template.create_resource(
111
- "data://test",
112
- {"key": "test"},
113
  )
114
 
115
  assert isinstance(resource, FunctionResource)
116
  content = await resource.read()
117
- assert '"key": "test"' in content
118
- assert '"value": 123' in content
 
119
 
120
  async def test_template_error(self):
121
  """Test error handling in template resource creation."""
@@ -174,65 +124,57 @@ class TestResourceTemplate:
174
  content = await resource.read()
175
  assert content == b"test"
176
 
177
- async def test_async_json_conversion(self):
178
- """Test automatic JSON conversion of async results."""
179
 
180
- async def get_data(key: str) -> dict:
181
- return {"key": key, "value": 123}
 
 
 
 
182
 
183
  template = ResourceTemplate.from_function(
184
  fn=get_data,
185
- uri_template="data://{key}",
186
- name="data",
187
  )
188
 
189
  resource = await template.create_resource(
190
- "data://test",
191
- {"key": "test"},
192
  )
193
 
194
  assert isinstance(resource, FunctionResource)
195
  content = await resource.read()
196
- assert '"key": "test"' in content
197
- assert '"value": 123' in content
198
-
199
- async def test_async_error(self):
200
- """Test error handling in async template."""
201
-
202
- async def failing_func(x: str) -> str:
203
- raise ValueError("Test error")
204
 
205
- template = ResourceTemplate.from_function(
206
- fn=failing_func,
207
- uri_template="fail://{x}",
208
- name="fail",
209
- )
210
-
211
- with pytest.raises(
212
- ValueError, match="Error creating resource from template: Test error"
213
- ):
214
- await template.create_resource("fail://test", {"x": "test"})
215
 
216
- async def test_sync_returning_coroutine(self):
217
- """Test sync function that returns a coroutine."""
 
218
 
219
- async def async_helper(name: str) -> str:
220
- return f"Hello, {name}!"
221
 
222
- def get_greeting(name: str) -> str:
223
- return async_helper(name) # Returns coroutine
224
 
225
  template = ResourceTemplate.from_function(
226
- fn=get_greeting,
227
- uri_template="greet://{name}",
228
- name="greeter",
229
  )
230
 
231
  resource = await template.create_resource(
232
- "greet://world",
233
- {"name": "world"},
234
  )
235
 
236
  assert isinstance(resource, FunctionResource)
237
  content = await resource.read()
238
- assert content == "Hello, world!"
 
1
+ import json
2
  import pytest
3
+ from pydantic import BaseModel
4
+
5
+ from fastmcp.resources import FunctionResource, ResourceTemplate
6
 
7
 
8
  class TestResourceTemplate:
9
  """Test ResourceTemplate functionality."""
10
 
11
+ def test_template_creation(self):
12
  """Test creating a template from a function."""
13
 
14
+ def my_func(key: str, value: int) -> dict:
15
+ return {"key": key, "value": value}
16
 
17
  template = ResourceTemplate.from_function(
18
+ fn=my_func,
19
+ uri_template="test://{key}/{value}",
20
+ name="test",
 
21
  )
22
+ assert template.uri_template == "test://{key}/{value}"
23
+ assert template.name == "test"
24
+ assert template.mime_type == "text/plain" # default
25
+ assert template.fn == my_func
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  def test_template_matches(self):
28
+ """Test matching URIs against a template."""
29
 
30
+ def my_func(key: str, value: int) -> dict:
31
+ return {"key": key, "value": value}
32
 
33
  template = ResourceTemplate.from_function(
34
+ fn=my_func,
35
+ uri_template="test://{key}/{value}",
36
  name="test",
37
  )
38
 
39
+ # Valid match
40
+ params = template.matches("test://foo/123")
41
+ assert params == {"key": "foo", "value": "123"}
42
 
43
+ # No match
44
+ assert template.matches("test://foo") is None
45
+ assert template.matches("other://foo/123") is None
46
 
47
+ async def test_create_resource(self):
48
+ """Test creating a resource from a template."""
49
 
50
+ def my_func(key: str, value: int) -> dict:
51
+ return {"key": key, "value": value}
52
 
53
  template = ResourceTemplate.from_function(
54
+ fn=my_func,
55
+ uri_template="test://{key}/{value}",
56
+ name="test",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  )
58
 
59
  resource = await template.create_resource(
60
+ "test://foo/123",
61
+ {"key": "foo", "value": 123},
62
  )
63
 
64
  assert isinstance(resource, FunctionResource)
65
  content = await resource.read()
66
+ assert isinstance(content, str)
67
+ data = json.loads(content)
68
+ assert data == {"key": "foo", "value": 123}
69
 
70
  async def test_template_error(self):
71
  """Test error handling in template resource creation."""
 
124
  content = await resource.read()
125
  assert content == b"test"
126
 
127
+ async def test_basemodel_conversion(self):
128
+ """Test handling of BaseModel types."""
129
 
130
+ class MyModel(BaseModel):
131
+ key: str
132
+ value: int
133
+
134
+ def get_data(key: str, value: int) -> MyModel:
135
+ return MyModel(key=key, value=value)
136
 
137
  template = ResourceTemplate.from_function(
138
  fn=get_data,
139
+ uri_template="test://{key}/{value}",
140
+ name="test",
141
  )
142
 
143
  resource = await template.create_resource(
144
+ "test://foo/123",
145
+ {"key": "foo", "value": 123},
146
  )
147
 
148
  assert isinstance(resource, FunctionResource)
149
  content = await resource.read()
150
+ assert isinstance(content, str)
151
+ data = json.loads(content)
152
+ assert data == {"key": "foo", "value": 123}
 
 
 
 
 
153
 
154
+ async def test_custom_type_conversion(self):
155
+ """Test handling of custom types."""
 
 
 
 
 
 
 
 
156
 
157
+ class CustomData:
158
+ def __init__(self, value: str):
159
+ self.value = value
160
 
161
+ def __str__(self) -> str:
162
+ return self.value
163
 
164
+ def get_data(value: str) -> CustomData:
165
+ return CustomData(value)
166
 
167
  template = ResourceTemplate.from_function(
168
+ fn=get_data,
169
+ uri_template="test://{value}",
170
+ name="test",
171
  )
172
 
173
  resource = await template.create_resource(
174
+ "test://hello",
175
+ {"value": "hello"},
176
  )
177
 
178
  assert isinstance(resource, FunctionResource)
179
  content = await resource.read()
180
+ assert content == "hello"
tests/resources/test_resources.py CHANGED
@@ -1,4 +1,5 @@
1
  import pytest
 
2
 
3
  from fastmcp.resources import FunctionResource, Resource
4
 
@@ -14,7 +15,7 @@ class TestResourceValidation:
14
 
15
  # Valid URI
16
  resource = FunctionResource(
17
- uri="http://example.com/data",
18
  name="test",
19
  fn=dummy_func,
20
  )
@@ -23,7 +24,7 @@ class TestResourceValidation:
23
  # Missing protocol
24
  with pytest.raises(ValueError, match="Input should be a valid URL"):
25
  FunctionResource(
26
- uri="invalid",
27
  name="test",
28
  fn=dummy_func,
29
  )
@@ -31,7 +32,7 @@ class TestResourceValidation:
31
  # Missing host
32
  with pytest.raises(ValueError, match="Input should be a valid URL"):
33
  FunctionResource(
34
- uri="http://",
35
  name="test",
36
  fn=dummy_func,
37
  )
@@ -43,7 +44,7 @@ class TestResourceValidation:
43
  return "data"
44
 
45
  resource = FunctionResource(
46
- uri="resource://my-resource",
47
  fn=dummy_func,
48
  )
49
  assert resource.name == "resource://my-resource"
@@ -62,7 +63,7 @@ class TestResourceValidation:
62
 
63
  # Explicit name takes precedence over URI
64
  resource = FunctionResource(
65
- uri="resource://uri-name",
66
  name="explicit-name",
67
  fn=dummy_func,
68
  )
@@ -76,14 +77,14 @@ class TestResourceValidation:
76
 
77
  # Default mime type
78
  resource = FunctionResource(
79
- uri="resource://test",
80
  fn=dummy_func,
81
  )
82
  assert resource.mime_type == "text/plain"
83
 
84
  # Custom mime type
85
  resource = FunctionResource(
86
- uri="resource://test",
87
  fn=dummy_func,
88
  mime_type="application/json",
89
  )
@@ -96,4 +97,4 @@ class TestResourceValidation:
96
  pass
97
 
98
  with pytest.raises(TypeError, match="abstract method"):
99
- ConcreteResource(uri="test://test", name="test") # type: ignore
 
1
  import pytest
2
+ from pydantic import AnyUrl
3
 
4
  from fastmcp.resources import FunctionResource, Resource
5
 
 
15
 
16
  # Valid URI
17
  resource = FunctionResource(
18
+ uri=AnyUrl("http://example.com/data"),
19
  name="test",
20
  fn=dummy_func,
21
  )
 
24
  # Missing protocol
25
  with pytest.raises(ValueError, match="Input should be a valid URL"):
26
  FunctionResource(
27
+ uri=AnyUrl("invalid"),
28
  name="test",
29
  fn=dummy_func,
30
  )
 
32
  # Missing host
33
  with pytest.raises(ValueError, match="Input should be a valid URL"):
34
  FunctionResource(
35
+ uri=AnyUrl("http://"),
36
  name="test",
37
  fn=dummy_func,
38
  )
 
44
  return "data"
45
 
46
  resource = FunctionResource(
47
+ uri=AnyUrl("resource://my-resource"),
48
  fn=dummy_func,
49
  )
50
  assert resource.name == "resource://my-resource"
 
63
 
64
  # Explicit name takes precedence over URI
65
  resource = FunctionResource(
66
+ uri=AnyUrl("resource://uri-name"),
67
  name="explicit-name",
68
  fn=dummy_func,
69
  )
 
77
 
78
  # Default mime type
79
  resource = FunctionResource(
80
+ uri=AnyUrl("resource://test"),
81
  fn=dummy_func,
82
  )
83
  assert resource.mime_type == "text/plain"
84
 
85
  # Custom mime type
86
  resource = FunctionResource(
87
+ uri=AnyUrl("resource://test"),
88
  fn=dummy_func,
89
  mime_type="application/json",
90
  )
 
97
  pass
98
 
99
  with pytest.raises(TypeError, match="abstract method"):
100
+ ConcreteResource(uri=AnyUrl("test://test"), name="test") # type: ignore