@@ -1121,6 +1121,180 @@ def test_direct_thread_by_participants(self):
11211121 pass
11221122
11231123
1124+ class ClientDirectMessageTypesTestCase (ClientPrivateTestCase ):
1125+ """Test that DirectMessage and DirectThread fields use structured Pydantic models instead of raw dictionaries"""
1126+
1127+ def test_direct_message_reactions_model (self ):
1128+ """Test that DirectMessage.reactions field uses MessageReactions model"""
1129+ from instagrapi .types import MessageReactions , MessageReaction
1130+ from datetime import datetime
1131+
1132+ # Get some direct messages
1133+ threads = self .cl .direct_threads (amount = 5 )
1134+ if not threads :
1135+ self .skipTest ("No direct threads available for testing" )
1136+
1137+ for thread in threads :
1138+ messages = self .cl .direct_messages (thread .id , amount = 10 )
1139+ for message in messages :
1140+ if message .reactions :
1141+ # Test that reactions field is now a MessageReactions object
1142+ self .assertIsInstance (message .reactions , MessageReactions )
1143+
1144+ # Test that reactions have proper structure
1145+ if hasattr (message .reactions , 'emojis' ) and message .reactions .emojis :
1146+ for emoji_reaction in message .reactions .emojis :
1147+ self .assertIsInstance (emoji_reaction , MessageReaction )
1148+ self .assertIsInstance (emoji_reaction .emoji , str )
1149+ self .assertIsInstance (emoji_reaction .sender_id , str )
1150+ self .assertIsInstance (emoji_reaction .timestamp , datetime )
1151+
1152+ # Test backward compatibility - should still work as dict
1153+ if hasattr (message .reactions , 'likes_count' ):
1154+ self .assertIsInstance (message .reactions .likes_count , int )
1155+
1156+ return # Found one message with reactions, test passed
1157+
1158+ def test_direct_message_link_model (self ):
1159+ """Test that DirectMessage.link field uses MessageLink model"""
1160+ from instagrapi .types import MessageLink , LinkContext
1161+
1162+ # Get some direct messages
1163+ threads = self .cl .direct_threads (amount = 5 )
1164+ if not threads :
1165+ self .skipTest ("No direct threads available for testing" )
1166+
1167+ for thread in threads :
1168+ messages = self .cl .direct_messages (thread .id , amount = 10 )
1169+ for message in messages :
1170+ if message .link :
1171+ # Test that link field is now a MessageLink object
1172+ self .assertIsInstance (message .link , MessageLink )
1173+
1174+ # Test that link has proper structure
1175+ if hasattr (message .link , 'text' ):
1176+ self .assertIsInstance (message .link .text , str )
1177+
1178+ if hasattr (message .link , 'link_context' ) and message .link .link_context :
1179+ self .assertIsInstance (message .link .link_context , LinkContext )
1180+ if hasattr (message .link .link_context , 'link_url' ):
1181+ self .assertIsInstance (message .link .link_context .link_url , str )
1182+
1183+ return # Found one message with link, test passed
1184+
1185+ def test_direct_message_visual_media_model (self ):
1186+ """Test that DirectMessage.visual_media field uses VisualMedia model"""
1187+ from instagrapi .types import VisualMedia , VisualMediaContent
1188+
1189+ # Get some direct messages
1190+ threads = self .cl .direct_threads (amount = 5 )
1191+ if not threads :
1192+ self .skipTest ("No direct threads available for testing" )
1193+
1194+ for thread in threads :
1195+ messages = self .cl .direct_messages (thread .id , amount = 10 )
1196+ for message in messages :
1197+ if message .visual_media :
1198+ # Test that visual_media field is now a VisualMedia object
1199+ self .assertIsInstance (message .visual_media , VisualMedia )
1200+
1201+ # Test that visual_media has proper structure
1202+ if hasattr (message .visual_media , 'media' ) and message .visual_media .media :
1203+ self .assertIsInstance (message .visual_media .media , VisualMediaContent )
1204+
1205+ return # Found one message with visual media, test passed
1206+
1207+ def test_direct_thread_last_seen_at_model (self ):
1208+ """Test that DirectThread.last_seen_at field uses LastSeenInfo model"""
1209+ from instagrapi .types import LastSeenInfo
1210+ from datetime import datetime
1211+
1212+ # Get some direct threads
1213+ threads = self .cl .direct_threads (amount = 5 )
1214+ if not threads :
1215+ self .skipTest ("No direct threads available for testing" )
1216+
1217+ for thread in threads :
1218+ if thread .last_seen_at :
1219+ # Test that last_seen_at is now a dict of LastSeenInfo objects
1220+ for user_id , seen_info in thread .last_seen_at .items ():
1221+ self .assertIsInstance (user_id , str )
1222+ self .assertIsInstance (seen_info , LastSeenInfo )
1223+
1224+ # Test structure of LastSeenInfo
1225+ if hasattr (seen_info , 'timestamp' ):
1226+ self .assertIsInstance (seen_info .timestamp , datetime )
1227+ if hasattr (seen_info , 'created_at' ):
1228+ self .assertIsInstance (seen_info .created_at , datetime )
1229+
1230+ return # Found one thread with last_seen_at, test passed
1231+
1232+ def test_direct_message_clips_metadata_model (self ):
1233+ """Test that DirectMessage.clips_metadata field uses ClipsMetadata model"""
1234+ from instagrapi .types import ClipsMetadata
1235+
1236+ # Get some direct messages
1237+ threads = self .cl .direct_threads (amount = 5 )
1238+ if not threads :
1239+ self .skipTest ("No direct threads available for testing" )
1240+
1241+ for thread in threads :
1242+ messages = self .cl .direct_messages (thread .id , amount = 10 )
1243+ for message in messages :
1244+ if message .clips_metadata :
1245+ # Test that clips_metadata field is now a ClipsMetadata object
1246+ self .assertIsInstance (message .clips_metadata , ClipsMetadata )
1247+
1248+ return # Found one message with clips metadata, test passed
1249+
1250+ def test_thread_is_seen_datetime_compatibility (self ):
1251+ """Test that DirectThread.is_seen() works with datetime objects"""
1252+ from datetime import datetime
1253+
1254+ # Get some direct threads
1255+ threads = self .cl .direct_threads (amount = 5 )
1256+ if not threads :
1257+ self .skipTest ("No direct threads available for testing" )
1258+
1259+ for thread in threads :
1260+ if thread .last_seen_at :
1261+ # Test that is_seen method works with datetime objects
1262+ user_id = str (self .cl .user_id )
1263+ try :
1264+ is_seen = thread .is_seen (user_id )
1265+ self .assertIsInstance (is_seen , bool )
1266+ return # Successfully tested is_seen method
1267+ except Exception as e :
1268+ self .fail (f"is_seen() method failed with datetime objects: { e } " )
1269+
1270+ def test_backward_compatibility_dict_access (self ):
1271+ """Test that dict-style access patterns still work for backward compatibility"""
1272+ # Get some direct messages
1273+ threads = self .cl .direct_threads (amount = 5 )
1274+ if not threads :
1275+ self .skipTest ("No direct threads available for testing" )
1276+
1277+ for thread in threads :
1278+ messages = self .cl .direct_messages (thread .id , amount = 10 )
1279+ for message in messages :
1280+ # Test that we can still access fields as if they were dicts
1281+ # This should work due to our Pydantic model structure
1282+ try :
1283+ if message .reactions :
1284+ # Should work even though it's now a Pydantic model
1285+ likes_count = getattr (message .reactions , 'likes_count' , 0 )
1286+ self .assertIsInstance (likes_count , int )
1287+
1288+ if message .link :
1289+ # Should work even though it's now a Pydantic model
1290+ link_text = getattr (message .link , 'text' , '' )
1291+ self .assertIsInstance (link_text , str )
1292+
1293+ return # Successfully tested backward compatibility
1294+ except Exception as e :
1295+ self .fail (f"Backward compatibility test failed: { e } " )
1296+
1297+
11241298class ClientAccountTestCase (ClientPrivateTestCase ):
11251299 def test_account_edit (self ):
11261300 # current
0 commit comments