1+ from io import BytesIO
12from unittest .mock import Mock
23
3- from nylas .models .attachments import Attachment , FindAttachmentQueryParams
4+ from nylas .models .attachments import Attachment , CreateAttachmentRequest , FindAttachmentQueryParams
45from nylas .resources .attachments import Attachments
56
67
7- class TestAttachments :
8- def test_attachment_deserialization (self , http_client ):
8+ class TestAttachmentModel :
9+ """Tests for the Attachment dataclass model."""
10+
11+ def test_attachment_deserialization (self ):
12+ """Test full deserialization of Attachment from dict."""
913 attach_json = {
1014 "content_type" : "image/png" ,
1115 "filename" : "pic.png" ,
@@ -14,6 +18,7 @@ def test_attachment_deserialization(self, http_client):
1418 "is_inline" : True ,
1519 "size" : 13068 ,
1620 "content_id" : "<ce9b9547-9eeb-43b2-ac4e-58768bdf04e4>" ,
21+ "content_disposition" : "inline" ,
1722 }
1823
1924 attachment = Attachment .from_dict (attach_json )
@@ -25,6 +30,232 @@ def test_attachment_deserialization(self, http_client):
2530 assert attachment .is_inline is True
2631 assert attachment .size == 13068
2732 assert attachment .content_id == "<ce9b9547-9eeb-43b2-ac4e-58768bdf04e4>"
33+ assert attachment .content_disposition == "inline"
34+
35+ def test_attachment_serialization (self ):
36+ """Test serialization of Attachment to dict."""
37+ attachment = Attachment (
38+ id = "185e56cb50e12e82" ,
39+ grant_id = "41009df5-bf11-4c97-aa18-b285b5f2e386" ,
40+ filename = "document.pdf" ,
41+ content_type = "application/pdf" ,
42+ size = 2048 ,
43+ content_id = "<doc-123>" ,
44+ content_disposition = "attachment" ,
45+ is_inline = False ,
46+ )
47+
48+ result = attachment .to_dict ()
49+
50+ assert result ["id" ] == "185e56cb50e12e82"
51+ assert result ["grant_id" ] == "41009df5-bf11-4c97-aa18-b285b5f2e386"
52+ assert result ["filename" ] == "document.pdf"
53+ assert result ["content_type" ] == "application/pdf"
54+ assert result ["size" ] == 2048
55+ assert result ["content_id" ] == "<doc-123>"
56+ assert result ["content_disposition" ] == "attachment"
57+ assert result ["is_inline" ] is False
58+
59+ def test_attachment_deserialization_partial_fields (self ):
60+ """Test deserialization with only required fields."""
61+ attach_json = {
62+ "id" : "abc123" ,
63+ "filename" : "test.txt" ,
64+ }
65+
66+ attachment = Attachment .from_dict (attach_json )
67+
68+ assert attachment .id == "abc123"
69+ assert attachment .filename == "test.txt"
70+ assert attachment .grant_id is None
71+ assert attachment .content_type is None
72+ assert attachment .size is None
73+ assert attachment .content_id is None
74+ assert attachment .content_disposition is None
75+ assert attachment .is_inline is None
76+
77+ def test_attachment_deserialization_empty_dict (self ):
78+ """Test deserialization from empty dict."""
79+ attachment = Attachment .from_dict ({})
80+
81+ assert attachment .id is None
82+ assert attachment .grant_id is None
83+ assert attachment .filename is None
84+ assert attachment .content_type is None
85+ assert attachment .size is None
86+ assert attachment .content_id is None
87+ assert attachment .content_disposition is None
88+ assert attachment .is_inline is None
89+
90+ def test_attachment_default_values (self ):
91+ """Test Attachment instantiation with default values."""
92+ attachment = Attachment ()
93+
94+ assert attachment .id is None
95+ assert attachment .grant_id is None
96+ assert attachment .filename is None
97+ assert attachment .content_type is None
98+ assert attachment .size is None
99+ assert attachment .content_id is None
100+ assert attachment .content_disposition is None
101+ assert attachment .is_inline is None
102+
103+ def test_attachment_content_disposition_attachment (self ):
104+ """Test attachment with content_disposition set to 'attachment'."""
105+ attach_json = {
106+ "id" : "file-123" ,
107+ "filename" : "report.xlsx" ,
108+ "content_disposition" : "attachment" ,
109+ "is_inline" : False ,
110+ }
111+
112+ attachment = Attachment .from_dict (attach_json )
113+
114+ assert attachment .content_disposition == "attachment"
115+ assert attachment .is_inline is False
116+
117+ def test_attachment_content_disposition_inline (self ):
118+ """Test inline attachment with content_disposition."""
119+ attach_json = {
120+ "id" : "img-456" ,
121+ "filename" : "logo.png" ,
122+ "content_disposition" : "inline" ,
123+ "is_inline" : True ,
124+ "content_id" :
"<[email protected] >" ,
125+ }
126+
127+ attachment = Attachment .from_dict (attach_json )
128+
129+ assert attachment .content_disposition == "inline"
130+ assert attachment .is_inline is True
131+ assert attachment .
content_id == "<[email protected] >" 132+
133+ def test_attachment_roundtrip_serialization (self ):
134+ """Test that serialization and deserialization are inverses."""
135+ original = Attachment (
136+ id = "test-id" ,
137+ grant_id = "grant-123" ,
138+ filename = "file.txt" ,
139+ content_type = "text/plain" ,
140+ size = 100 ,
141+ content_id = "<cid123>" ,
142+ content_disposition = "attachment" ,
143+ is_inline = False ,
144+ )
145+
146+ serialized = original .to_dict ()
147+ deserialized = Attachment .from_dict (serialized )
148+
149+ assert deserialized .id == original .id
150+ assert deserialized .grant_id == original .grant_id
151+ assert deserialized .filename == original .filename
152+ assert deserialized .content_type == original .content_type
153+ assert deserialized .size == original .size
154+ assert deserialized .content_id == original .content_id
155+ assert deserialized .content_disposition == original .content_disposition
156+ assert deserialized .is_inline == original .is_inline
157+
158+
159+ class TestCreateAttachmentRequest :
160+ """Tests for the CreateAttachmentRequest TypedDict."""
161+
162+ def test_create_attachment_request_with_base64_content (self ):
163+ """Test creating attachment request with base64 encoded content."""
164+ request : CreateAttachmentRequest = {
165+ "filename" : "test.txt" ,
166+ "content_type" : "text/plain" ,
167+ "content" : "SGVsbG8gV29ybGQh" , # base64 for "Hello World!"
168+ "size" : 12 ,
169+ }
170+
171+ assert request ["filename" ] == "test.txt"
172+ assert request ["content_type" ] == "text/plain"
173+ assert request ["content" ] == "SGVsbG8gV29ybGQh"
174+ assert request ["size" ] == 12
175+
176+ def test_create_attachment_request_with_file_object (self ):
177+ """Test creating attachment request with file-like object."""
178+ file_content = BytesIO (b"File content here" )
179+
180+ request : CreateAttachmentRequest = {
181+ "filename" : "document.pdf" ,
182+ "content_type" : "application/pdf" ,
183+ "content" : file_content ,
184+ "size" : 17 ,
185+ }
186+
187+ assert request ["filename" ] == "document.pdf"
188+ assert request ["content_type" ] == "application/pdf"
189+ assert request ["content" ] == file_content
190+ assert request ["size" ] == 17
191+
192+ def test_create_attachment_request_with_optional_fields (self ):
193+ """Test creating attachment request with all optional fields."""
194+ request : CreateAttachmentRequest = {
195+ "filename" : "image.png" ,
196+ "content_type" : "image/png" ,
197+ "content" : "iVBORw0KGgo=" ,
198+ "size" : 1024 ,
199+ "content_id" :
"<[email protected] >" ,
200+ "content_disposition" : "inline" ,
201+ "is_inline" : True ,
202+ }
203+
204+ assert request ["filename" ] == "image.png"
205+ assert request ["content_type" ] == "image/png"
206+ assert request ["content" ] == "iVBORw0KGgo="
207+ assert request ["size" ] == 1024
208+ assert request [
"content_id" ]
== "<[email protected] >" 209+ assert request ["content_disposition" ] == "inline"
210+ assert request ["is_inline" ] is True
211+
212+ def test_create_attachment_request_minimal (self ):
213+ """Test creating attachment request with only required fields."""
214+ request : CreateAttachmentRequest = {
215+ "filename" : "minimal.txt" ,
216+ "content_type" : "text/plain" ,
217+ "content" : "data" ,
218+ "size" : 4 ,
219+ }
220+
221+ assert "filename" in request
222+ assert "content_type" in request
223+ assert "content" in request
224+ assert "size" in request
225+ # Optional fields should not be present
226+ assert "content_id" not in request
227+ assert "content_disposition" not in request
228+ assert "is_inline" not in request
229+
230+
231+ class TestFindAttachmentQueryParams :
232+ """Tests for the FindAttachmentQueryParams TypedDict."""
233+
234+ def test_find_attachment_query_params (self ):
235+ """Test creating find attachment query params."""
236+ params : FindAttachmentQueryParams = {
237+ "message_id" : "msg-12345" ,
238+ }
239+
240+ assert params ["message_id" ] == "msg-12345"
241+
242+ def test_find_attachment_query_params_various_message_ids (self ):
243+ """Test find attachment query params with various message ID formats."""
244+ # Simple ID
245+ params1 : FindAttachmentQueryParams = {"message_id" : "abc123" }
246+ assert params1 ["message_id" ] == "abc123"
247+
248+ # UUID format
249+ params2 : FindAttachmentQueryParams = {"message_id" : "550e8400-e29b-41d4-a716-446655440000" }
250+ assert params2 ["message_id" ] == "550e8400-e29b-41d4-a716-446655440000"
251+
252+ # Complex message ID (email message-id format)
253+ params3 :
FindAttachmentQueryParams = {
"message_id" :
"<[email protected] >" }
254+ assert params3 [
"message_id" ]
== "<[email protected] >" 255+
256+
257+ class TestAttachments :
258+ """Tests for the Attachments resource API calls."""
28259
29260 def test_find_attachment (self , http_client_response ):
30261 attachments = Attachments (http_client_response )
0 commit comments