diff --git a/conf.py b/conf.py index 0692145..726d3e6 100644 --- a/conf.py +++ b/conf.py @@ -21,8 +21,8 @@ project = "X API SDK" copyright = "2024, X Developer Platform" author = "X Developer Platform" -release = "0.4.1" -version = "0.4.1" +release = "0.4.2" +version = "0.4.2" # -- General configuration ---------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index f7f2f52..610838d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ build-backend = "hatchling.build" [project] name = "xdk" -version = "0.4.1" +version = "0.4.2" description = "Python SDK for the X API" authors = [ {name = "X Developer Platform", email = "devs@x.com"}, diff --git a/scripts/process-for-mintlify.py b/scripts/process-for-mintlify.py index 4c41d50..9b62748 100644 --- a/scripts/process-for-mintlify.py +++ b/scripts/process-for-mintlify.py @@ -24,9 +24,9 @@ MINTLIFY_CONFIG = { "outputDir": "mintlify-docs", "baseUrl": "https://docs.x.com", - "title": "X API SDK v0.4.1", + "title": "X API SDK v0.4.2", "description": "Python SDK for the X API with comprehensive pagination, authentication, and streaming support.", - "version": "0.4.1", + "version": "0.4.2", "githubUrl": "https://github.com/xdevplatform/xdk", } diff --git a/tests/account_activity/test_contracts.py b/tests/account_activity/test_contracts.py index e0336f3..65d2ce7 100644 --- a/tests/account_activity/test_contracts.py +++ b/tests/account_activity/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.account_activity_client = getattr(self.client, "account_activity") - def test_validate_subscription_request_structure(self): - """Test validate_subscription request structure.""" + def test_create_replay_job_request_structure(self): + """Test create_replay_job request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -37,15 +37,17 @@ def test_validate_subscription_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["webhook_id"] = "test_value" + kwargs["from_date"] = "test_from_date" + kwargs["to_date"] = "test_to_date" # Add request body if required # Call the method try: - method = getattr(self.account_activity_client, "validate_subscription") + method = getattr(self.account_activity_client, "create_replay_job") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -64,7 +66,7 @@ def test_validate_subscription_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -73,15 +75,15 @@ def test_validate_subscription_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) expected_path = ( - "/2/account_activity/webhooks/{webhook_id}/subscriptions/all" + "/2/account_activity/replay/webhooks/{webhook_id}/subscriptions/all" ) assert expected_path.replace("{", "").replace( "}", "" @@ -98,12 +100,12 @@ def test_validate_subscription_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for validate_subscription: {e}") + pytest.fail(f"Contract test failed for create_replay_job: {e}") - def test_validate_subscription_required_parameters(self): - """Test that validate_subscription handles parameters correctly.""" - method = getattr(self.account_activity_client, "validate_subscription") + def test_create_replay_job_required_parameters(self): + """Test that create_replay_job handles parameters correctly.""" + method = getattr(self.account_activity_client, "create_replay_job") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -111,14 +113,14 @@ def test_validate_subscription_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_validate_subscription_response_structure(self): - """Test validate_subscription response structure validation.""" + def test_create_replay_job_response_structure(self): + """Test create_replay_job response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -128,13 +130,15 @@ def test_validate_subscription_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["webhook_id"] = "test" + kwargs["from_date"] = "test_value" + kwargs["to_date"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.account_activity_client, "validate_subscription") + method = getattr(self.account_activity_client, "create_replay_job") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -146,8 +150,8 @@ def test_validate_subscription_response_structure(self): ) - def test_create_subscription_request_structure(self): - """Test create_subscription request structure.""" + def test_validate_subscription_request_structure(self): + """Test validate_subscription request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -156,19 +160,15 @@ def test_create_subscription_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["webhook_id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.account_activity.models import CreateSubscriptionRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateSubscriptionRequest() # Call the method try: - method = getattr(self.account_activity_client, "create_subscription") + method = getattr(self.account_activity_client, "validate_subscription") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -187,7 +187,7 @@ def test_create_subscription_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -196,9 +196,9 @@ def test_create_subscription_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") @@ -221,12 +221,12 @@ def test_create_subscription_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create_subscription: {e}") + pytest.fail(f"Contract test failed for validate_subscription: {e}") - def test_create_subscription_required_parameters(self): - """Test that create_subscription handles parameters correctly.""" - method = getattr(self.account_activity_client, "create_subscription") + def test_validate_subscription_required_parameters(self): + """Test that validate_subscription handles parameters correctly.""" + method = getattr(self.account_activity_client, "validate_subscription") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -234,14 +234,14 @@ def test_create_subscription_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_subscription_response_structure(self): - """Test create_subscription response structure validation.""" + def test_validate_subscription_response_structure(self): + """Test validate_subscription response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -251,17 +251,13 @@ def test_create_subscription_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["webhook_id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.account_activity.models import CreateSubscriptionRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateSubscriptionRequest() # Call method and verify response structure - method = getattr(self.account_activity_client, "create_subscription") + method = getattr(self.account_activity_client, "validate_subscription") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -273,8 +269,8 @@ def test_create_subscription_response_structure(self): ) - def test_get_subscriptions_request_structure(self): - """Test get_subscriptions request structure.""" + def test_create_subscription_request_structure(self): + """Test create_subscription request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -283,15 +279,19 @@ def test_get_subscriptions_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["webhook_id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.account_activity.models import CreateSubscriptionRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateSubscriptionRequest() # Call the method try: - method = getattr(self.account_activity_client, "get_subscriptions") + method = getattr(self.account_activity_client, "create_subscription") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -310,7 +310,7 @@ def test_get_subscriptions_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -319,15 +319,15 @@ def test_get_subscriptions_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) expected_path = ( - "/2/account_activity/webhooks/{webhook_id}/subscriptions/all/list" + "/2/account_activity/webhooks/{webhook_id}/subscriptions/all" ) assert expected_path.replace("{", "").replace( "}", "" @@ -344,12 +344,12 @@ def test_get_subscriptions_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_subscriptions: {e}") + pytest.fail(f"Contract test failed for create_subscription: {e}") - def test_get_subscriptions_required_parameters(self): - """Test that get_subscriptions handles parameters correctly.""" - method = getattr(self.account_activity_client, "get_subscriptions") + def test_create_subscription_required_parameters(self): + """Test that create_subscription handles parameters correctly.""" + method = getattr(self.account_activity_client, "create_subscription") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -357,14 +357,14 @@ def test_get_subscriptions_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_subscriptions_response_structure(self): - """Test get_subscriptions response structure validation.""" + def test_create_subscription_response_structure(self): + """Test create_subscription response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -374,13 +374,17 @@ def test_get_subscriptions_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["webhook_id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.account_activity.models import CreateSubscriptionRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateSubscriptionRequest() # Call method and verify response structure - method = getattr(self.account_activity_client, "get_subscriptions") + method = getattr(self.account_activity_client, "create_subscription") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -392,8 +396,8 @@ def test_get_subscriptions_response_structure(self): ) - def test_create_replay_job_request_structure(self): - """Test create_replay_job request structure.""" + def test_get_subscriptions_request_structure(self): + """Test get_subscriptions request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -402,17 +406,15 @@ def test_create_replay_job_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["webhook_id"] = "test_value" - kwargs["from_date"] = "test_from_date" - kwargs["to_date"] = "test_to_date" # Add request body if required # Call the method try: - method = getattr(self.account_activity_client, "create_replay_job") + method = getattr(self.account_activity_client, "get_subscriptions") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -431,7 +433,7 @@ def test_create_replay_job_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -440,15 +442,15 @@ def test_create_replay_job_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) expected_path = ( - "/2/account_activity/replay/webhooks/{webhook_id}/subscriptions/all" + "/2/account_activity/webhooks/{webhook_id}/subscriptions/all/list" ) assert expected_path.replace("{", "").replace( "}", "" @@ -465,12 +467,12 @@ def test_create_replay_job_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create_replay_job: {e}") + pytest.fail(f"Contract test failed for get_subscriptions: {e}") - def test_create_replay_job_required_parameters(self): - """Test that create_replay_job handles parameters correctly.""" - method = getattr(self.account_activity_client, "create_replay_job") + def test_get_subscriptions_required_parameters(self): + """Test that get_subscriptions handles parameters correctly.""" + method = getattr(self.account_activity_client, "get_subscriptions") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -478,14 +480,14 @@ def test_create_replay_job_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_replay_job_response_structure(self): - """Test create_replay_job response structure validation.""" + def test_get_subscriptions_response_structure(self): + """Test get_subscriptions response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -495,15 +497,13 @@ def test_create_replay_job_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["webhook_id"] = "test" - kwargs["from_date"] = "test_value" - kwargs["to_date"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.account_activity_client, "create_replay_job") + method = getattr(self.account_activity_client, "get_subscriptions") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -515,8 +515,8 @@ def test_create_replay_job_response_structure(self): ) - def test_delete_subscription_request_structure(self): - """Test delete_subscription request structure.""" + def test_get_subscription_count_request_structure(self): + """Test get_subscription_count request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -525,16 +525,14 @@ def test_delete_subscription_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["webhook_id"] = "test_value" - kwargs["user_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.account_activity_client, "delete_subscription") + method = getattr(self.account_activity_client, "get_subscription_count") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -553,7 +551,7 @@ def test_delete_subscription_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -562,14 +560,14 @@ def test_delete_subscription_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/account_activity/webhooks/{webhook_id}/subscriptions/{user_id}/all" + expected_path = "/2/account_activity/subscriptions/count" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -585,27 +583,27 @@ def test_delete_subscription_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for delete_subscription: {e}") + pytest.fail(f"Contract test failed for get_subscription_count: {e}") - def test_delete_subscription_required_parameters(self): - """Test that delete_subscription handles parameters correctly.""" - method = getattr(self.account_activity_client, "delete_subscription") - # Test with missing required parameters - mock the request to avoid network calls + def test_get_subscription_count_required_parameters(self): + """Test that get_subscription_count handles parameters correctly.""" + method = getattr(self.account_activity_client, "get_subscription_count") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None + mock_session.get.return_value = mock_response + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_delete_subscription_response_structure(self): - """Test delete_subscription response structure validation.""" + def test_get_subscription_count_response_structure(self): + """Test get_subscription_count response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -615,14 +613,12 @@ def test_delete_subscription_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["webhook_id"] = "test" - kwargs["user_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.account_activity_client, "delete_subscription") + method = getattr(self.account_activity_client, "get_subscription_count") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -634,8 +630,8 @@ def test_delete_subscription_response_structure(self): ) - def test_get_subscription_count_request_structure(self): - """Test get_subscription_count request structure.""" + def test_delete_subscription_request_structure(self): + """Test delete_subscription request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -644,14 +640,16 @@ def test_get_subscription_count_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["webhook_id"] = "test_value" + kwargs["user_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.account_activity_client, "get_subscription_count") + method = getattr(self.account_activity_client, "delete_subscription") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -670,7 +668,7 @@ def test_get_subscription_count_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -679,14 +677,14 @@ def test_get_subscription_count_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/account_activity/subscriptions/count" + expected_path = "/2/account_activity/webhooks/{webhook_id}/subscriptions/{user_id}/all" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -702,27 +700,27 @@ def test_get_subscription_count_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_subscription_count: {e}") + pytest.fail(f"Contract test failed for delete_subscription: {e}") - def test_get_subscription_count_required_parameters(self): - """Test that get_subscription_count handles parameters correctly.""" - method = getattr(self.account_activity_client, "get_subscription_count") - # No required parameters, method should be callable without args + def test_delete_subscription_required_parameters(self): + """Test that delete_subscription handles parameters correctly.""" + method = getattr(self.account_activity_client, "delete_subscription") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response - try: + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_session.delete.return_value = mock_response + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_subscription_count_response_structure(self): - """Test get_subscription_count response structure validation.""" + def test_delete_subscription_response_structure(self): + """Test delete_subscription response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -732,12 +730,14 @@ def test_get_subscription_count_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["webhook_id"] = "test" + kwargs["user_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.account_activity_client, "get_subscription_count") + method = getattr(self.account_activity_client, "delete_subscription") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/account_activity/test_structure.py b/tests/account_activity/test_structure.py index ca28afd..7faab8f 100644 --- a/tests/account_activity/test_structure.py +++ b/tests/account_activity/test_structure.py @@ -28,6 +28,55 @@ def setup_class(self): self.account_activity_client = getattr(self.client, "account_activity") + def test_create_replay_job_exists(self): + """Test that create_replay_job method exists with correct signature.""" + # Check method exists + method = getattr(AccountActivityClient, "create_replay_job", None) + assert ( + method is not None + ), f"Method create_replay_job does not exist on AccountActivityClient" + # Check method is callable + assert callable(method), f"create_replay_job is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert ( + len(params) >= 1 + ), f"create_replay_job should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [ + "webhook_id", + "from_date", + "to_date", + ] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from create_replay_job" + # Check optional parameters have defaults (excluding 'self') + optional_params = [] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_create_replay_job_return_annotation(self): + """Test that create_replay_job has proper return type annotation.""" + method = getattr(AccountActivityClient, "create_replay_job") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method create_replay_job should have return type annotation" + + def test_validate_subscription_exists(self): """Test that validate_subscription method exists with correct signature.""" # Check method exists @@ -169,35 +218,31 @@ def test_get_subscriptions_return_annotation(self): ), f"Method get_subscriptions should have return type annotation" - def test_create_replay_job_exists(self): - """Test that create_replay_job method exists with correct signature.""" + def test_get_subscription_count_exists(self): + """Test that get_subscription_count method exists with correct signature.""" # Check method exists - method = getattr(AccountActivityClient, "create_replay_job", None) + method = getattr(AccountActivityClient, "get_subscription_count", None) assert ( method is not None - ), f"Method create_replay_job does not exist on AccountActivityClient" + ), f"Method get_subscription_count does not exist on AccountActivityClient" # Check method is callable - assert callable(method), f"create_replay_job is not callable" + assert callable(method), f"get_subscription_count is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"create_replay_job should have at least 'self' parameter" + ), f"get_subscription_count should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "webhook_id", - "from_date", - "to_date", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from create_replay_job" + ), f"Required parameter '{required_param}' missing from get_subscription_count" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -208,14 +253,14 @@ def test_create_replay_job_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_create_replay_job_return_annotation(self): - """Test that create_replay_job has proper return type annotation.""" - method = getattr(AccountActivityClient, "create_replay_job") + def test_get_subscription_count_return_annotation(self): + """Test that get_subscription_count has proper return type annotation.""" + method = getattr(AccountActivityClient, "get_subscription_count") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method create_replay_job should have return type annotation" + ), f"Method get_subscription_count should have return type annotation" def test_delete_subscription_exists(self): @@ -266,60 +311,15 @@ def test_delete_subscription_return_annotation(self): ), f"Method delete_subscription should have return type annotation" - def test_get_subscription_count_exists(self): - """Test that get_subscription_count method exists with correct signature.""" - # Check method exists - method = getattr(AccountActivityClient, "get_subscription_count", None) - assert ( - method is not None - ), f"Method get_subscription_count does not exist on AccountActivityClient" - # Check method is callable - assert callable(method), f"get_subscription_count is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_subscription_count should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from get_subscription_count" - # Check optional parameters have defaults (excluding 'self') - optional_params = [] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_get_subscription_count_return_annotation(self): - """Test that get_subscription_count has proper return type annotation.""" - method = getattr(AccountActivityClient, "get_subscription_count") - sig = inspect.signature(method) - # Check return annotation exists - assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method get_subscription_count should have return type annotation" - - def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ + "create_replay_job", "validate_subscription", "create_subscription", "get_subscriptions", - "create_replay_job", - "delete_subscription", "get_subscription_count", + "delete_subscription", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/activity/test_contracts.py b/tests/activity/test_contracts.py index a46470d..d79bf29 100644 --- a/tests/activity/test_contracts.py +++ b/tests/activity/test_contracts.py @@ -265,8 +265,8 @@ def test_create_subscription_response_structure(self): ) - def test_stream_request_structure(self): - """Test stream request structure.""" + def test_update_subscription_request_structure(self): + """Test update_subscription request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -275,14 +275,19 @@ def test_stream_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["subscription_id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.activity.models import UpdateSubscriptionRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = UpdateSubscriptionRequest() # Call the method try: - method = getattr(self.activity_client, "stream") + method = getattr(self.activity_client, "update_subscription") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -301,7 +306,7 @@ def test_stream_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.put.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -310,14 +315,14 @@ def test_stream_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.put.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.put.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/activity/stream" + expected_path = "/2/activity/subscriptions/{subscription_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -333,27 +338,27 @@ def test_stream_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for stream: {e}") + pytest.fail(f"Contract test failed for update_subscription: {e}") - def test_stream_required_parameters(self): - """Test that stream handles parameters correctly.""" - method = getattr(self.activity_client, "stream") - # No required parameters, method should be callable without args + def test_update_subscription_required_parameters(self): + """Test that update_subscription handles parameters correctly.""" + method = getattr(self.activity_client, "update_subscription") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response - try: + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_session.put.return_value = mock_response + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_stream_response_structure(self): - """Test stream response structure validation.""" + def test_update_subscription_response_structure(self): + """Test update_subscription response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -363,12 +368,17 @@ def test_stream_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["subscription_id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.activity.models import UpdateSubscriptionRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = UpdateSubscriptionRequest() # Call method and verify response structure - method = getattr(self.activity_client, "stream") + method = getattr(self.activity_client, "update_subscription") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -380,8 +390,8 @@ def test_stream_response_structure(self): ) - def test_update_subscription_request_structure(self): - """Test update_subscription request structure.""" + def test_delete_subscription_request_structure(self): + """Test delete_subscription request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -390,19 +400,15 @@ def test_update_subscription_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.put.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["subscription_id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.activity.models import UpdateSubscriptionRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = UpdateSubscriptionRequest() # Call the method try: - method = getattr(self.activity_client, "update_subscription") + method = getattr(self.activity_client, "delete_subscription") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -421,7 +427,7 @@ def test_update_subscription_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.put.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -430,9 +436,9 @@ def test_update_subscription_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.put.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.put.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") @@ -453,12 +459,12 @@ def test_update_subscription_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for update_subscription: {e}") + pytest.fail(f"Contract test failed for delete_subscription: {e}") - def test_update_subscription_required_parameters(self): - """Test that update_subscription handles parameters correctly.""" - method = getattr(self.activity_client, "update_subscription") + def test_delete_subscription_required_parameters(self): + """Test that delete_subscription handles parameters correctly.""" + method = getattr(self.activity_client, "delete_subscription") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -466,14 +472,14 @@ def test_update_subscription_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.put.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_update_subscription_response_structure(self): - """Test update_subscription response structure validation.""" + def test_delete_subscription_response_structure(self): + """Test delete_subscription response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -483,17 +489,13 @@ def test_update_subscription_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.put.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["subscription_id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.activity.models import UpdateSubscriptionRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = UpdateSubscriptionRequest() # Call method and verify response structure - method = getattr(self.activity_client, "update_subscription") + method = getattr(self.activity_client, "delete_subscription") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -505,8 +507,8 @@ def test_update_subscription_response_structure(self): ) - def test_delete_subscription_request_structure(self): - """Test delete_subscription request structure.""" + def test_stream_request_structure(self): + """Test stream request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -515,15 +517,14 @@ def test_delete_subscription_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["subscription_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.activity_client, "delete_subscription") + method = getattr(self.activity_client, "stream") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -542,7 +543,7 @@ def test_delete_subscription_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -551,14 +552,14 @@ def test_delete_subscription_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/activity/subscriptions/{subscription_id}" + expected_path = "/2/activity/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -574,27 +575,27 @@ def test_delete_subscription_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for delete_subscription: {e}") + pytest.fail(f"Contract test failed for stream: {e}") - def test_delete_subscription_required_parameters(self): - """Test that delete_subscription handles parameters correctly.""" - method = getattr(self.activity_client, "delete_subscription") - # Test with missing required parameters - mock the request to avoid network calls + def test_stream_required_parameters(self): + """Test that stream handles parameters correctly.""" + method = getattr(self.activity_client, "stream") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None + mock_session.get.return_value = mock_response + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_delete_subscription_response_structure(self): - """Test delete_subscription response structure validation.""" + def test_stream_response_structure(self): + """Test stream response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -604,13 +605,12 @@ def test_delete_subscription_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["subscription_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.activity_client, "delete_subscription") + method = getattr(self.activity_client, "stream") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/activity/test_structure.py b/tests/activity/test_structure.py index f0610ce..6299021 100644 --- a/tests/activity/test_structure.py +++ b/tests/activity/test_structure.py @@ -118,51 +118,6 @@ def test_create_subscription_return_annotation(self): ), f"Method create_subscription should have return type annotation" - def test_stream_exists(self): - """Test that stream method exists with correct signature.""" - # Check method exists - method = getattr(ActivityClient, "stream", None) - assert method is not None, f"Method stream does not exist on ActivityClient" - # Check method is callable - assert callable(method), f"stream is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"stream should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from stream" - # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "backfill_minutes", - "start_time", - "end_time", - ] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_stream_return_annotation(self): - """Test that stream has proper return type annotation.""" - method = getattr(ActivityClient, "stream") - sig = inspect.signature(method) - # Check return annotation exists - assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method stream should have return type annotation" - - def test_update_subscription_exists(self): """Test that update_subscription method exists with correct signature.""" # Check method exists @@ -257,14 +212,59 @@ def test_delete_subscription_return_annotation(self): ), f"Method delete_subscription should have return type annotation" + def test_stream_exists(self): + """Test that stream method exists with correct signature.""" + # Check method exists + method = getattr(ActivityClient, "stream", None) + assert method is not None, f"Method stream does not exist on ActivityClient" + # Check method is callable + assert callable(method), f"stream is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert len(params) >= 1, f"stream should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from stream" + # Check optional parameters have defaults (excluding 'self') + optional_params = [ + "backfill_minutes", + "start_time", + "end_time", + ] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_stream_return_annotation(self): + """Test that stream has proper return type annotation.""" + method = getattr(ActivityClient, "stream") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method stream should have return type annotation" + + def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ "get_subscriptions", "create_subscription", - "stream", "update_subscription", "delete_subscription", + "stream", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/community_notes/test_contracts.py b/tests/community_notes/test_contracts.py index f1dad4b..25e08fe 100644 --- a/tests/community_notes/test_contracts.py +++ b/tests/community_notes/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.community_notes_client = getattr(self.client, "community_notes") - def test_evaluate_request_structure(self): - """Test evaluate request structure.""" + def test_search_eligible_posts_request_structure(self): + """Test search_eligible_posts request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -37,18 +37,15 @@ def test_evaluate_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["test_mode"] = True # Add request body if required - # Import and create proper request model instance - from xdk.community_notes.models import EvaluateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = EvaluateRequest() # Call the method try: - method = getattr(self.community_notes_client, "evaluate") + method = getattr(self.community_notes_client, "search_eligible_posts") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -67,7 +64,7 @@ def test_evaluate_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -76,14 +73,14 @@ def test_evaluate_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/evaluate_note" + expected_path = "/2/notes/search/posts_eligible_for_notes" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -99,12 +96,12 @@ def test_evaluate_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for evaluate: {e}") + pytest.fail(f"Contract test failed for search_eligible_posts: {e}") - def test_evaluate_required_parameters(self): - """Test that evaluate handles parameters correctly.""" - method = getattr(self.community_notes_client, "evaluate") + def test_search_eligible_posts_required_parameters(self): + """Test that search_eligible_posts handles parameters correctly.""" + method = getattr(self.community_notes_client, "search_eligible_posts") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -112,14 +109,14 @@ def test_evaluate_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_evaluate_response_structure(self): - """Test evaluate response structure validation.""" + def test_search_eligible_posts_response_structure(self): + """Test search_eligible_posts response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -129,16 +126,13 @@ def test_evaluate_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["test_mode"] = True # Add request body if required - # Import and create proper request model instance - from xdk.community_notes.models import EvaluateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = EvaluateRequest() # Call method and verify response structure - method = getattr(self.community_notes_client, "evaluate") + method = getattr(self.community_notes_client, "search_eligible_posts") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -150,8 +144,8 @@ def test_evaluate_response_structure(self): ) - def test_create_request_structure(self): - """Test create request structure.""" + def test_delete_request_structure(self): + """Test delete request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -160,18 +154,15 @@ def test_create_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.community_notes.models import CreateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateRequest() # Call the method try: - method = getattr(self.community_notes_client, "create") + method = getattr(self.community_notes_client, "delete") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -190,7 +181,7 @@ def test_create_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -199,14 +190,14 @@ def test_create_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/notes" + expected_path = "/2/notes/{id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -222,12 +213,12 @@ def test_create_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create: {e}") + pytest.fail(f"Contract test failed for delete: {e}") - def test_create_required_parameters(self): - """Test that create handles parameters correctly.""" - method = getattr(self.community_notes_client, "create") + def test_delete_required_parameters(self): + """Test that delete handles parameters correctly.""" + method = getattr(self.community_notes_client, "delete") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -235,14 +226,14 @@ def test_create_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_response_structure(self): - """Test create response structure validation.""" + def test_delete_response_structure(self): + """Test delete response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -252,16 +243,13 @@ def test_create_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.community_notes.models import CreateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateRequest() # Call method and verify response structure - method = getattr(self.community_notes_client, "create") + method = getattr(self.community_notes_client, "delete") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -273,8 +261,8 @@ def test_create_response_structure(self): ) - def test_search_written_request_structure(self): - """Test search_written request structure.""" + def test_create_request_structure(self): + """Test create request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -283,15 +271,18 @@ def test_search_written_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["test_mode"] = True # Add request body if required + # Import and create proper request model instance + from xdk.community_notes.models import CreateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateRequest() # Call the method try: - method = getattr(self.community_notes_client, "search_written") + method = getattr(self.community_notes_client, "create") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -310,7 +301,7 @@ def test_search_written_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -319,14 +310,14 @@ def test_search_written_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/notes/search/notes_written" + expected_path = "/2/notes" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -342,12 +333,12 @@ def test_search_written_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for search_written: {e}") + pytest.fail(f"Contract test failed for create: {e}") - def test_search_written_required_parameters(self): - """Test that search_written handles parameters correctly.""" - method = getattr(self.community_notes_client, "search_written") + def test_create_required_parameters(self): + """Test that create handles parameters correctly.""" + method = getattr(self.community_notes_client, "create") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -355,14 +346,14 @@ def test_search_written_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_search_written_response_structure(self): - """Test search_written response structure validation.""" + def test_create_response_structure(self): + """Test create response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -372,13 +363,16 @@ def test_search_written_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["test_mode"] = True # Add request body if required + # Import and create proper request model instance + from xdk.community_notes.models import CreateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateRequest() # Call method and verify response structure - method = getattr(self.community_notes_client, "search_written") + method = getattr(self.community_notes_client, "create") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -390,8 +384,8 @@ def test_search_written_response_structure(self): ) - def test_search_eligible_posts_request_structure(self): - """Test search_eligible_posts request structure.""" + def test_evaluate_request_structure(self): + """Test evaluate request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -400,15 +394,18 @@ def test_search_eligible_posts_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["test_mode"] = True # Add request body if required + # Import and create proper request model instance + from xdk.community_notes.models import EvaluateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = EvaluateRequest() # Call the method try: - method = getattr(self.community_notes_client, "search_eligible_posts") + method = getattr(self.community_notes_client, "evaluate") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -427,7 +424,7 @@ def test_search_eligible_posts_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -436,14 +433,14 @@ def test_search_eligible_posts_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/notes/search/posts_eligible_for_notes" + expected_path = "/2/evaluate_note" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -459,12 +456,12 @@ def test_search_eligible_posts_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for search_eligible_posts: {e}") + pytest.fail(f"Contract test failed for evaluate: {e}") - def test_search_eligible_posts_required_parameters(self): - """Test that search_eligible_posts handles parameters correctly.""" - method = getattr(self.community_notes_client, "search_eligible_posts") + def test_evaluate_required_parameters(self): + """Test that evaluate handles parameters correctly.""" + method = getattr(self.community_notes_client, "evaluate") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -472,14 +469,14 @@ def test_search_eligible_posts_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_search_eligible_posts_response_structure(self): - """Test search_eligible_posts response structure validation.""" + def test_evaluate_response_structure(self): + """Test evaluate response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -489,13 +486,16 @@ def test_search_eligible_posts_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["test_mode"] = True # Add request body if required + # Import and create proper request model instance + from xdk.community_notes.models import EvaluateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = EvaluateRequest() # Call method and verify response structure - method = getattr(self.community_notes_client, "search_eligible_posts") + method = getattr(self.community_notes_client, "evaluate") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -507,8 +507,8 @@ def test_search_eligible_posts_response_structure(self): ) - def test_delete_request_structure(self): - """Test delete request structure.""" + def test_search_written_request_structure(self): + """Test search_written request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -517,15 +517,15 @@ def test_delete_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["test_mode"] = True # Add request body if required # Call the method try: - method = getattr(self.community_notes_client, "delete") + method = getattr(self.community_notes_client, "search_written") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -544,7 +544,7 @@ def test_delete_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -553,14 +553,14 @@ def test_delete_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/notes/{id}" + expected_path = "/2/notes/search/notes_written" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -576,12 +576,12 @@ def test_delete_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for delete: {e}") + pytest.fail(f"Contract test failed for search_written: {e}") - def test_delete_required_parameters(self): - """Test that delete handles parameters correctly.""" - method = getattr(self.community_notes_client, "delete") + def test_search_written_required_parameters(self): + """Test that search_written handles parameters correctly.""" + method = getattr(self.community_notes_client, "search_written") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -589,14 +589,14 @@ def test_delete_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_delete_response_structure(self): - """Test delete response structure validation.""" + def test_search_written_response_structure(self): + """Test search_written response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -606,13 +606,13 @@ def test_delete_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["test_mode"] = True # Add request body if required # Call method and verify response structure - method = getattr(self.community_notes_client, "delete") + method = getattr(self.community_notes_client, "search_written") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/community_notes/test_pagination.py b/tests/community_notes/test_pagination.py index 4c5e0d0..292fd4d 100644 --- a/tests/community_notes/test_pagination.py +++ b/tests/community_notes/test_pagination.py @@ -27,20 +27,20 @@ def setup_class(self): self.community_notes_client = getattr(self.client, "community_notes") - def test_search_written_cursor_creation(self): - """Test that search_written can be used with Cursor.""" - method = getattr(self.community_notes_client, "search_written") + def test_search_eligible_posts_cursor_creation(self): + """Test that search_eligible_posts can be used with Cursor.""" + method = getattr(self.community_notes_client, "search_eligible_posts") # Should be able to create cursor without error try: test_cursor = cursor(method, test_mode=True, max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method search_written should support pagination") + pytest.fail(f"Method search_eligible_posts should support pagination") - def test_search_written_cursor_pages(self): - """Test pagination with pages() for search_written.""" + def test_search_eligible_posts_cursor_pages(self): + """Test pagination with pages() for search_eligible_posts.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -61,7 +61,7 @@ def test_search_written_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.community_notes_client, "search_written") + method = getattr(self.community_notes_client, "search_eligible_posts") test_cursor = cursor(method, test_mode=True, max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -77,8 +77,8 @@ def test_search_written_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_search_written_cursor_items(self): - """Test pagination with items() for search_written.""" + def test_search_eligible_posts_cursor_items(self): + """Test pagination with items() for search_eligible_posts.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -97,7 +97,7 @@ def test_search_written_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.community_notes_client, "search_written") + method = getattr(self.community_notes_client, "search_eligible_posts") test_cursor = cursor(method, test_mode=True, max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -108,15 +108,15 @@ def test_search_written_cursor_items(self): ), "Items should have 'id' field" - def test_search_written_pagination_parameters(self): - """Test that pagination parameters are handled correctly for search_written.""" + def test_search_eligible_posts_pagination_parameters(self): + """Test that pagination parameters are handled correctly for search_eligible_posts.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.community_notes_client, "search_written") + method = getattr(self.community_notes_client, "search_eligible_posts") # Test with max_results parameter test_cursor = cursor(method, test_mode=True, max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -169,20 +169,20 @@ def test_search_written_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_search_eligible_posts_cursor_creation(self): - """Test that search_eligible_posts can be used with Cursor.""" - method = getattr(self.community_notes_client, "search_eligible_posts") + def test_search_written_cursor_creation(self): + """Test that search_written can be used with Cursor.""" + method = getattr(self.community_notes_client, "search_written") # Should be able to create cursor without error try: test_cursor = cursor(method, test_mode=True, max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method search_eligible_posts should support pagination") + pytest.fail(f"Method search_written should support pagination") - def test_search_eligible_posts_cursor_pages(self): - """Test pagination with pages() for search_eligible_posts.""" + def test_search_written_cursor_pages(self): + """Test pagination with pages() for search_written.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -203,7 +203,7 @@ def test_search_eligible_posts_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.community_notes_client, "search_eligible_posts") + method = getattr(self.community_notes_client, "search_written") test_cursor = cursor(method, test_mode=True, max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -219,8 +219,8 @@ def test_search_eligible_posts_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_search_eligible_posts_cursor_items(self): - """Test pagination with items() for search_eligible_posts.""" + def test_search_written_cursor_items(self): + """Test pagination with items() for search_written.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -239,7 +239,7 @@ def test_search_eligible_posts_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.community_notes_client, "search_eligible_posts") + method = getattr(self.community_notes_client, "search_written") test_cursor = cursor(method, test_mode=True, max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -250,15 +250,15 @@ def test_search_eligible_posts_cursor_items(self): ), "Items should have 'id' field" - def test_search_eligible_posts_pagination_parameters(self): - """Test that pagination parameters are handled correctly for search_eligible_posts.""" + def test_search_written_pagination_parameters(self): + """Test that pagination parameters are handled correctly for search_written.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.community_notes_client, "search_eligible_posts") + method = getattr(self.community_notes_client, "search_written") # Test with max_results parameter test_cursor = cursor(method, test_mode=True, max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -321,7 +321,7 @@ def test_pagination_edge_cases(self): empty_response.raise_for_status.return_value = None mock_session.get.return_value = empty_response # Pick first paginatable method for testing - method = getattr(self.community_notes_client, "search_written") + method = getattr(self.community_notes_client, "search_eligible_posts") test_cursor = cursor(method, test_mode=True, max_results=10) # Should handle empty responses gracefully pages = list(test_cursor.pages(1)) diff --git a/tests/community_notes/test_structure.py b/tests/community_notes/test_structure.py index 367d4fb..d27a582 100644 --- a/tests/community_notes/test_structure.py +++ b/tests/community_notes/test_structure.py @@ -28,29 +28,107 @@ def setup_class(self): self.community_notes_client = getattr(self.client, "community_notes") - def test_evaluate_exists(self): - """Test that evaluate method exists with correct signature.""" + def test_search_eligible_posts_exists(self): + """Test that search_eligible_posts method exists with correct signature.""" # Check method exists - method = getattr(CommunityNotesClient, "evaluate", None) + method = getattr(CommunityNotesClient, "search_eligible_posts", None) assert ( method is not None - ), f"Method evaluate does not exist on CommunityNotesClient" + ), f"Method search_eligible_posts does not exist on CommunityNotesClient" # Check method is callable - assert callable(method), f"evaluate is not callable" + assert callable(method), f"search_eligible_posts is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"evaluate should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"search_eligible_posts should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "test_mode", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from evaluate" + ), f"Required parameter '{required_param}' missing from search_eligible_posts" + # Check optional parameters have defaults (excluding 'self') + optional_params = [ + "pagination_token", + "max_results", + "post_selection", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", + ] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_search_eligible_posts_return_annotation(self): + """Test that search_eligible_posts has proper return type annotation.""" + method = getattr(CommunityNotesClient, "search_eligible_posts") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method search_eligible_posts should have return type annotation" + + + def test_search_eligible_posts_pagination_params(self): + """Test that search_eligible_posts has pagination parameters.""" + method = getattr(CommunityNotesClient, "search_eligible_posts") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method search_eligible_posts should have pagination parameters" + + + def test_delete_exists(self): + """Test that delete method exists with correct signature.""" + # Check method exists + method = getattr(CommunityNotesClient, "delete", None) + assert ( + method is not None + ), f"Method delete does not exist on CommunityNotesClient" + # Check method is callable + assert callable(method), f"delete is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert len(params) >= 1, f"delete should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [ + "id", + ] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from delete" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -61,14 +139,14 @@ def test_evaluate_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_evaluate_return_annotation(self): - """Test that evaluate has proper return type annotation.""" - method = getattr(CommunityNotesClient, "evaluate") + def test_delete_return_annotation(self): + """Test that delete has proper return type annotation.""" + method = getattr(CommunityNotesClient, "delete") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method evaluate should have return type annotation" + ), f"Method delete should have return type annotation" def test_create_exists(self): @@ -114,37 +192,31 @@ def test_create_return_annotation(self): ), f"Method create should have return type annotation" - def test_search_written_exists(self): - """Test that search_written method exists with correct signature.""" + def test_evaluate_exists(self): + """Test that evaluate method exists with correct signature.""" # Check method exists - method = getattr(CommunityNotesClient, "search_written", None) + method = getattr(CommunityNotesClient, "evaluate", None) assert ( method is not None - ), f"Method search_written does not exist on CommunityNotesClient" + ), f"Method evaluate does not exist on CommunityNotesClient" # Check method is callable - assert callable(method), f"search_written is not callable" + assert callable(method), f"evaluate is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"search_written should have at least 'self' parameter" + assert len(params) >= 1, f"evaluate should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "test_mode", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from search_written" + ), f"Required parameter '{required_param}' missing from evaluate" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "pagination_token", - "max_results", - "note.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -153,51 +225,30 @@ def test_search_written_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_search_written_return_annotation(self): - """Test that search_written has proper return type annotation.""" - method = getattr(CommunityNotesClient, "search_written") + def test_evaluate_return_annotation(self): + """Test that evaluate has proper return type annotation.""" + method = getattr(CommunityNotesClient, "evaluate") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method search_written should have return type annotation" - - - def test_search_written_pagination_params(self): - """Test that search_written has pagination parameters.""" - method = getattr(CommunityNotesClient, "search_written") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method search_written should have pagination parameters" + ), f"Method evaluate should have return type annotation" - def test_search_eligible_posts_exists(self): - """Test that search_eligible_posts method exists with correct signature.""" + def test_search_written_exists(self): + """Test that search_written method exists with correct signature.""" # Check method exists - method = getattr(CommunityNotesClient, "search_eligible_posts", None) + method = getattr(CommunityNotesClient, "search_written", None) assert ( method is not None - ), f"Method search_eligible_posts does not exist on CommunityNotesClient" + ), f"Method search_written does not exist on CommunityNotesClient" # Check method is callable - assert callable(method), f"search_eligible_posts is not callable" + assert callable(method), f"search_written is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"search_eligible_posts should have at least 'self' parameter" + assert len(params) >= 1, f"search_written should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -208,18 +259,12 @@ def test_search_eligible_posts_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from search_eligible_posts" + ), f"Required parameter '{required_param}' missing from search_written" # Check optional parameters have defaults (excluding 'self') optional_params = [ "pagination_token", "max_results", - "post_selection", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", - "user.fields", - "place.fields", + "note.fields", ] for optional_param in optional_params: if optional_param in params: @@ -229,19 +274,19 @@ def test_search_eligible_posts_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_search_eligible_posts_return_annotation(self): - """Test that search_eligible_posts has proper return type annotation.""" - method = getattr(CommunityNotesClient, "search_eligible_posts") + def test_search_written_return_annotation(self): + """Test that search_written has proper return type annotation.""" + method = getattr(CommunityNotesClient, "search_written") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method search_eligible_posts should have return type annotation" + ), f"Method search_written should have return type annotation" - def test_search_eligible_posts_pagination_params(self): - """Test that search_eligible_posts has pagination parameters.""" - method = getattr(CommunityNotesClient, "search_eligible_posts") + def test_search_written_pagination_params(self): + """Test that search_written has pagination parameters.""" + method = getattr(CommunityNotesClient, "search_written") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -255,62 +300,17 @@ def test_search_eligible_posts_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method search_eligible_posts should have pagination parameters" - - - def test_delete_exists(self): - """Test that delete method exists with correct signature.""" - # Check method exists - method = getattr(CommunityNotesClient, "delete", None) - assert ( - method is not None - ), f"Method delete does not exist on CommunityNotesClient" - # Check method is callable - assert callable(method), f"delete is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"delete should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [ - "id", - ] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from delete" - # Check optional parameters have defaults (excluding 'self') - optional_params = [] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_delete_return_annotation(self): - """Test that delete has proper return type annotation.""" - method = getattr(CommunityNotesClient, "delete") - sig = inspect.signature(method) - # Check return annotation exists - assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method delete should have return type annotation" + ), f"Paginated method search_written should have pagination parameters" def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "evaluate", - "create", - "search_written", "search_eligible_posts", "delete", + "create", + "evaluate", + "search_written", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/direct_messages/test_contracts.py b/tests/direct_messages/test_contracts.py index 3c05ac2..523a46c 100644 --- a/tests/direct_messages/test_contracts.py +++ b/tests/direct_messages/test_contracts.py @@ -32,7 +32,7 @@ def test_create_conversation_request_structure(self): # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() - mock_response.status_code = 200 + mock_response.status_code = 201 mock_response.json.return_value = { "data": None, } @@ -57,7 +57,7 @@ def test_create_conversation_request_structure(self): # For streaming operations, we need to set up the mock to handle streaming # Mock the streaming response mock_streaming_response = Mock() - mock_streaming_response.status_code = 200 + mock_streaming_response.status_code = 201 mock_streaming_response.raise_for_status.return_value = None mock_streaming_response.__enter__ = Mock( return_value=mock_streaming_response @@ -126,7 +126,7 @@ def test_create_conversation_response_structure(self): "data": None, } mock_response = Mock() - mock_response.status_code = 200 + mock_response.status_code = 201 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None mock_session.post.return_value = mock_response @@ -150,8 +150,8 @@ def test_create_conversation_response_structure(self): ) - def test_create_by_conversation_id_request_structure(self): - """Test create_by_conversation_id request structure.""" + def test_get_events_by_conversation_id_request_structure(self): + """Test get_events_by_conversation_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -160,20 +160,16 @@ def test_create_by_conversation_id_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["dm_conversation_id"] = "test_dm_conversation_id" + kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.direct_messages.models import CreateByConversationIdRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateByConversationIdRequest() # Call the method try: method = getattr( - self.direct_messages_client, "create_by_conversation_id" + self.direct_messages_client, "get_events_by_conversation_id" ) result = method(**kwargs) # Check if this is a streaming operation (returns Generator) @@ -193,7 +189,7 @@ def test_create_by_conversation_id_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -202,14 +198,14 @@ def test_create_by_conversation_id_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/dm_conversations/{dm_conversation_id}/messages" + expected_path = "/2/dm_conversations/{id}/dm_events" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -225,12 +221,14 @@ def test_create_by_conversation_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create_by_conversation_id: {e}") + pytest.fail( + f"Contract test failed for get_events_by_conversation_id: {e}" + ) - def test_create_by_conversation_id_required_parameters(self): - """Test that create_by_conversation_id handles parameters correctly.""" - method = getattr(self.direct_messages_client, "create_by_conversation_id") + def test_get_events_by_conversation_id_required_parameters(self): + """Test that get_events_by_conversation_id handles parameters correctly.""" + method = getattr(self.direct_messages_client, "get_events_by_conversation_id") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -238,14 +236,14 @@ def test_create_by_conversation_id_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_by_conversation_id_response_structure(self): - """Test create_by_conversation_id response structure validation.""" + def test_get_events_by_conversation_id_response_structure(self): + """Test get_events_by_conversation_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -255,17 +253,15 @@ def test_create_by_conversation_id_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["dm_conversation_id"] = "test_value" + kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.direct_messages.models import CreateByConversationIdRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateByConversationIdRequest() # Call method and verify response structure - method = getattr(self.direct_messages_client, "create_by_conversation_id") + method = getattr( + self.direct_messages_client, "get_events_by_conversation_id" + ) result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -277,8 +273,8 @@ def test_create_by_conversation_id_response_structure(self): ) - def test_get_events_by_conversation_id_request_structure(self): - """Test get_events_by_conversation_id request structure.""" + def test_get_events_request_structure(self): + """Test get_events request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -291,13 +287,10 @@ def test_get_events_by_conversation_id_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr( - self.direct_messages_client, "get_events_by_conversation_id" - ) + method = getattr(self.direct_messages_client, "get_events") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -332,7 +325,7 @@ def test_get_events_by_conversation_id_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/dm_conversations/{id}/dm_events" + expected_path = "/2/dm_events" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -348,29 +341,27 @@ def test_get_events_by_conversation_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail( - f"Contract test failed for get_events_by_conversation_id: {e}" - ) + pytest.fail(f"Contract test failed for get_events: {e}") - def test_get_events_by_conversation_id_required_parameters(self): - """Test that get_events_by_conversation_id handles parameters correctly.""" - method = getattr(self.direct_messages_client, "get_events_by_conversation_id") - # Test with missing required parameters - mock the request to avoid network calls + def test_get_events_required_parameters(self): + """Test that get_events handles parameters correctly.""" + method = getattr(self.direct_messages_client, "get_events") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_events_by_conversation_id_response_structure(self): - """Test get_events_by_conversation_id response structure validation.""" + def test_get_events_response_structure(self): + """Test get_events response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -383,12 +374,9 @@ def test_get_events_by_conversation_id_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr( - self.direct_messages_client, "get_events_by_conversation_id" - ) + method = getattr(self.direct_messages_client, "get_events") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -634,8 +622,8 @@ def test_delete_events_response_structure(self): ) - def test_get_events_by_participant_id_request_structure(self): - """Test get_events_by_participant_id request structure.""" + def test_create_by_conversation_id_request_structure(self): + """Test create_by_conversation_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -644,16 +632,20 @@ def test_get_events_by_participant_id_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["participant_id"] = "test_value" + kwargs["dm_conversation_id"] = "test_dm_conversation_id" # Add request body if required + # Import and create proper request model instance + from xdk.direct_messages.models import CreateByConversationIdRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateByConversationIdRequest() # Call the method try: method = getattr( - self.direct_messages_client, "get_events_by_participant_id" + self.direct_messages_client, "create_by_conversation_id" ) result = method(**kwargs) # Check if this is a streaming operation (returns Generator) @@ -673,7 +665,7 @@ def test_get_events_by_participant_id_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -682,14 +674,14 @@ def test_get_events_by_participant_id_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/dm_conversations/with/{participant_id}/dm_events" + expected_path = "/2/dm_conversations/{dm_conversation_id}/messages" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -705,14 +697,12 @@ def test_get_events_by_participant_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail( - f"Contract test failed for get_events_by_participant_id: {e}" - ) + pytest.fail(f"Contract test failed for create_by_conversation_id: {e}") - def test_get_events_by_participant_id_required_parameters(self): - """Test that get_events_by_participant_id handles parameters correctly.""" - method = getattr(self.direct_messages_client, "get_events_by_participant_id") + def test_create_by_conversation_id_required_parameters(self): + """Test that create_by_conversation_id handles parameters correctly.""" + method = getattr(self.direct_messages_client, "create_by_conversation_id") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -720,14 +710,14 @@ def test_get_events_by_participant_id_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_events_by_participant_id_response_structure(self): - """Test get_events_by_participant_id response structure validation.""" + def test_create_by_conversation_id_response_structure(self): + """Test create_by_conversation_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -737,15 +727,17 @@ def test_get_events_by_participant_id_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["participant_id"] = "test" + kwargs["dm_conversation_id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.direct_messages.models import CreateByConversationIdRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateByConversationIdRequest() # Call method and verify response structure - method = getattr( - self.direct_messages_client, "get_events_by_participant_id" - ) + method = getattr(self.direct_messages_client, "create_by_conversation_id") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -757,8 +749,8 @@ def test_get_events_by_participant_id_response_structure(self): ) - def test_create_by_participant_id_request_structure(self): - """Test create_by_participant_id request structure.""" + def test_get_events_by_participant_id_request_structure(self): + """Test get_events_by_participant_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -767,20 +759,16 @@ def test_create_by_participant_id_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["participant_id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.direct_messages.models import CreateByParticipantIdRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateByParticipantIdRequest() # Call the method try: method = getattr( - self.direct_messages_client, "create_by_participant_id" + self.direct_messages_client, "get_events_by_participant_id" ) result = method(**kwargs) # Check if this is a streaming operation (returns Generator) @@ -800,7 +788,7 @@ def test_create_by_participant_id_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -809,14 +797,14 @@ def test_create_by_participant_id_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/dm_conversations/with/{participant_id}/messages" + expected_path = "/2/dm_conversations/with/{participant_id}/dm_events" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -832,12 +820,14 @@ def test_create_by_participant_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create_by_participant_id: {e}") + pytest.fail( + f"Contract test failed for get_events_by_participant_id: {e}" + ) - def test_create_by_participant_id_required_parameters(self): - """Test that create_by_participant_id handles parameters correctly.""" - method = getattr(self.direct_messages_client, "create_by_participant_id") + def test_get_events_by_participant_id_required_parameters(self): + """Test that get_events_by_participant_id handles parameters correctly.""" + method = getattr(self.direct_messages_client, "get_events_by_participant_id") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -845,14 +835,14 @@ def test_create_by_participant_id_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_by_participant_id_response_structure(self): - """Test create_by_participant_id response structure validation.""" + def test_get_events_by_participant_id_response_structure(self): + """Test get_events_by_participant_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -862,17 +852,15 @@ def test_create_by_participant_id_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["participant_id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.direct_messages.models import CreateByParticipantIdRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateByParticipantIdRequest() # Call method and verify response structure - method = getattr(self.direct_messages_client, "create_by_participant_id") + method = getattr( + self.direct_messages_client, "get_events_by_participant_id" + ) result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -884,8 +872,8 @@ def test_create_by_participant_id_response_structure(self): ) - def test_get_events_request_structure(self): - """Test get_events request structure.""" + def test_create_by_participant_id_request_structure(self): + """Test create_by_participant_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -894,14 +882,21 @@ def test_get_events_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["participant_id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.direct_messages.models import CreateByParticipantIdRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateByParticipantIdRequest() # Call the method try: - method = getattr(self.direct_messages_client, "get_events") + method = getattr( + self.direct_messages_client, "create_by_participant_id" + ) result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -920,7 +915,7 @@ def test_get_events_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -929,14 +924,14 @@ def test_get_events_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/dm_events" + expected_path = "/2/dm_conversations/with/{participant_id}/messages" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -952,27 +947,27 @@ def test_get_events_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_events: {e}") + pytest.fail(f"Contract test failed for create_by_participant_id: {e}") - def test_get_events_required_parameters(self): - """Test that get_events handles parameters correctly.""" - method = getattr(self.direct_messages_client, "get_events") - # No required parameters, method should be callable without args + def test_create_by_participant_id_required_parameters(self): + """Test that create_by_participant_id handles parameters correctly.""" + method = getattr(self.direct_messages_client, "create_by_participant_id") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response - try: + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_session.post.return_value = mock_response + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_events_response_structure(self): - """Test get_events response structure validation.""" + def test_create_by_participant_id_response_structure(self): + """Test create_by_participant_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -982,12 +977,17 @@ def test_get_events_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["participant_id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.direct_messages.models import CreateByParticipantIdRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateByParticipantIdRequest() # Call method and verify response structure - method = getattr(self.direct_messages_client, "get_events") + method = getattr(self.direct_messages_client, "create_by_participant_id") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/direct_messages/test_pagination.py b/tests/direct_messages/test_pagination.py index 30c5f2e..0203bbf 100644 --- a/tests/direct_messages/test_pagination.py +++ b/tests/direct_messages/test_pagination.py @@ -177,22 +177,20 @@ def test_get_events_by_conversation_id_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_events_by_participant_id_cursor_creation(self): - """Test that get_events_by_participant_id can be used with Cursor.""" - method = getattr(self.direct_messages_client, "get_events_by_participant_id") + def test_get_events_cursor_creation(self): + """Test that get_events can be used with Cursor.""" + method = getattr(self.direct_messages_client, "get_events") # Should be able to create cursor without error try: - test_cursor = cursor(method, "test_value", max_results=10) + test_cursor = cursor(method, max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail( - f"Method get_events_by_participant_id should support pagination" - ) + pytest.fail(f"Method get_events should support pagination") - def test_get_events_by_participant_id_cursor_pages(self): - """Test pagination with pages() for get_events_by_participant_id.""" + def test_get_events_cursor_pages(self): + """Test pagination with pages() for get_events.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -213,10 +211,8 @@ def test_get_events_by_participant_id_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr( - self.direct_messages_client, "get_events_by_participant_id" - ) - test_cursor = cursor(method, "test_value", max_results=2) + method = getattr(self.direct_messages_client, "get_events") + test_cursor = cursor(method, max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" # Verify first page @@ -231,8 +227,8 @@ def test_get_events_by_participant_id_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_events_by_participant_id_cursor_items(self): - """Test pagination with items() for get_events_by_participant_id.""" + def test_get_events_cursor_items(self): + """Test pagination with items() for get_events.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -251,10 +247,8 @@ def test_get_events_by_participant_id_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr( - self.direct_messages_client, "get_events_by_participant_id" - ) - test_cursor = cursor(method, "test_value", max_results=10) + method = getattr(self.direct_messages_client, "get_events") + test_cursor = cursor(method, max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" # Verify items have expected structure @@ -264,19 +258,17 @@ def test_get_events_by_participant_id_cursor_items(self): ), "Items should have 'id' field" - def test_get_events_by_participant_id_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_events_by_participant_id.""" + def test_get_events_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_events.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr( - self.direct_messages_client, "get_events_by_participant_id" - ) + method = getattr(self.direct_messages_client, "get_events") # Test with max_results parameter - test_cursor = cursor(method, "test_value", max_results=5) + test_cursor = cursor(method, max_results=5) list(test_cursor.pages(1)) # Trigger one request # Verify max_results was passed in request call_args = mock_session.get.call_args @@ -305,7 +297,7 @@ def test_get_events_by_participant_id_pagination_parameters(self): mock_response_with_token, second_page_response, ] - test_cursor = cursor(method, "test_value", max_results=1) + test_cursor = cursor(method, max_results=1) pages = list(test_cursor.pages(2)) # Should have made 2 requests assert ( @@ -327,20 +319,22 @@ def test_get_events_by_participant_id_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_events_cursor_creation(self): - """Test that get_events can be used with Cursor.""" - method = getattr(self.direct_messages_client, "get_events") + def test_get_events_by_participant_id_cursor_creation(self): + """Test that get_events_by_participant_id can be used with Cursor.""" + method = getattr(self.direct_messages_client, "get_events_by_participant_id") # Should be able to create cursor without error try: - test_cursor = cursor(method, max_results=10) + test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_events should support pagination") + pytest.fail( + f"Method get_events_by_participant_id should support pagination" + ) - def test_get_events_cursor_pages(self): - """Test pagination with pages() for get_events.""" + def test_get_events_by_participant_id_cursor_pages(self): + """Test pagination with pages() for get_events_by_participant_id.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -361,8 +355,10 @@ def test_get_events_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.direct_messages_client, "get_events") - test_cursor = cursor(method, max_results=2) + method = getattr( + self.direct_messages_client, "get_events_by_participant_id" + ) + test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" # Verify first page @@ -377,8 +373,8 @@ def test_get_events_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_events_cursor_items(self): - """Test pagination with items() for get_events.""" + def test_get_events_by_participant_id_cursor_items(self): + """Test pagination with items() for get_events_by_participant_id.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -397,8 +393,10 @@ def test_get_events_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.direct_messages_client, "get_events") - test_cursor = cursor(method, max_results=10) + method = getattr( + self.direct_messages_client, "get_events_by_participant_id" + ) + test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" # Verify items have expected structure @@ -408,17 +406,19 @@ def test_get_events_cursor_items(self): ), "Items should have 'id' field" - def test_get_events_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_events.""" + def test_get_events_by_participant_id_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_events_by_participant_id.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.direct_messages_client, "get_events") + method = getattr( + self.direct_messages_client, "get_events_by_participant_id" + ) # Test with max_results parameter - test_cursor = cursor(method, max_results=5) + test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request # Verify max_results was passed in request call_args = mock_session.get.call_args @@ -447,7 +447,7 @@ def test_get_events_pagination_parameters(self): mock_response_with_token, second_page_response, ] - test_cursor = cursor(method, max_results=1) + test_cursor = cursor(method, "test_value", max_results=1) pages = list(test_cursor.pages(2)) # Should have made 2 requests assert ( diff --git a/tests/direct_messages/test_structure.py b/tests/direct_messages/test_structure.py index 1425db5..d00a8b6 100644 --- a/tests/direct_messages/test_structure.py +++ b/tests/direct_messages/test_structure.py @@ -73,35 +73,44 @@ def test_create_conversation_return_annotation(self): ), f"Method create_conversation should have return type annotation" - def test_create_by_conversation_id_exists(self): - """Test that create_by_conversation_id method exists with correct signature.""" + def test_get_events_by_conversation_id_exists(self): + """Test that get_events_by_conversation_id method exists with correct signature.""" # Check method exists - method = getattr(DirectMessagesClient, "create_by_conversation_id", None) + method = getattr(DirectMessagesClient, "get_events_by_conversation_id", None) assert ( method is not None - ), f"Method create_by_conversation_id does not exist on DirectMessagesClient" + ), f"Method get_events_by_conversation_id does not exist on DirectMessagesClient" # Check method is callable - assert callable(method), f"create_by_conversation_id is not callable" + assert callable(method), f"get_events_by_conversation_id is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"create_by_conversation_id should have at least 'self' parameter" + ), f"get_events_by_conversation_id should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "dm_conversation_id", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from create_by_conversation_id" + ), f"Required parameter '{required_param}' missing from get_events_by_conversation_id" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "max_results", + "pagination_token", + "event_types", + "dm_event.fields", + "expansions", + "media.fields", + "user.fields", + "tweet.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -110,43 +119,58 @@ def test_create_by_conversation_id_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_create_by_conversation_id_return_annotation(self): - """Test that create_by_conversation_id has proper return type annotation.""" - method = getattr(DirectMessagesClient, "create_by_conversation_id") + def test_get_events_by_conversation_id_return_annotation(self): + """Test that get_events_by_conversation_id has proper return type annotation.""" + method = getattr(DirectMessagesClient, "get_events_by_conversation_id") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method create_by_conversation_id should have return type annotation" + ), f"Method get_events_by_conversation_id should have return type annotation" - def test_get_events_by_conversation_id_exists(self): - """Test that get_events_by_conversation_id method exists with correct signature.""" + def test_get_events_by_conversation_id_pagination_params(self): + """Test that get_events_by_conversation_id has pagination parameters.""" + method = getattr(DirectMessagesClient, "get_events_by_conversation_id") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_events_by_conversation_id should have pagination parameters" + + + def test_get_events_exists(self): + """Test that get_events method exists with correct signature.""" # Check method exists - method = getattr(DirectMessagesClient, "get_events_by_conversation_id", None) + method = getattr(DirectMessagesClient, "get_events", None) assert ( method is not None - ), f"Method get_events_by_conversation_id does not exist on DirectMessagesClient" + ), f"Method get_events does not exist on DirectMessagesClient" # Check method is callable - assert callable(method), f"get_events_by_conversation_id is not callable" + assert callable(method), f"get_events is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_events_by_conversation_id should have at least 'self' parameter" + assert len(params) >= 1, f"get_events should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "id", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_events_by_conversation_id" + ), f"Required parameter '{required_param}' missing from get_events" # Check optional parameters have defaults (excluding 'self') optional_params = [ "max_results", @@ -166,19 +190,19 @@ def test_get_events_by_conversation_id_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_events_by_conversation_id_return_annotation(self): - """Test that get_events_by_conversation_id has proper return type annotation.""" - method = getattr(DirectMessagesClient, "get_events_by_conversation_id") + def test_get_events_return_annotation(self): + """Test that get_events has proper return type annotation.""" + method = getattr(DirectMessagesClient, "get_events") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_events_by_conversation_id should have return type annotation" + ), f"Method get_events should have return type annotation" - def test_get_events_by_conversation_id_pagination_params(self): - """Test that get_events_by_conversation_id has pagination parameters.""" - method = getattr(DirectMessagesClient, "get_events_by_conversation_id") + def test_get_events_pagination_params(self): + """Test that get_events has pagination parameters.""" + method = getattr(DirectMessagesClient, "get_events") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -192,7 +216,7 @@ def test_get_events_by_conversation_id_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_events_by_conversation_id should have pagination parameters" + ), f"Paginated method get_events should have pagination parameters" def test_get_events_by_id_exists(self): @@ -293,6 +317,53 @@ def test_delete_events_return_annotation(self): ), f"Method delete_events should have return type annotation" + def test_create_by_conversation_id_exists(self): + """Test that create_by_conversation_id method exists with correct signature.""" + # Check method exists + method = getattr(DirectMessagesClient, "create_by_conversation_id", None) + assert ( + method is not None + ), f"Method create_by_conversation_id does not exist on DirectMessagesClient" + # Check method is callable + assert callable(method), f"create_by_conversation_id is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert ( + len(params) >= 1 + ), f"create_by_conversation_id should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [ + "dm_conversation_id", + ] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from create_by_conversation_id" + # Check optional parameters have defaults (excluding 'self') + optional_params = [] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_create_by_conversation_id_return_annotation(self): + """Test that create_by_conversation_id has proper return type annotation.""" + method = getattr(DirectMessagesClient, "create_by_conversation_id") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method create_by_conversation_id should have return type annotation" + + def test_get_events_by_participant_id_exists(self): """Test that get_events_by_participant_id method exists with correct signature.""" # Check method exists @@ -415,88 +486,17 @@ def test_create_by_participant_id_return_annotation(self): ), f"Method create_by_participant_id should have return type annotation" - def test_get_events_exists(self): - """Test that get_events method exists with correct signature.""" - # Check method exists - method = getattr(DirectMessagesClient, "get_events", None) - assert ( - method is not None - ), f"Method get_events does not exist on DirectMessagesClient" - # Check method is callable - assert callable(method), f"get_events is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"get_events should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from get_events" - # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "max_results", - "pagination_token", - "event_types", - "dm_event.fields", - "expansions", - "media.fields", - "user.fields", - "tweet.fields", - ] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_get_events_return_annotation(self): - """Test that get_events has proper return type annotation.""" - method = getattr(DirectMessagesClient, "get_events") - sig = inspect.signature(method) - # Check return annotation exists - assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method get_events should have return type annotation" - - - def test_get_events_pagination_params(self): - """Test that get_events has pagination parameters.""" - method = getattr(DirectMessagesClient, "get_events") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_events should have pagination parameters" - - def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ "create_conversation", - "create_by_conversation_id", "get_events_by_conversation_id", + "get_events", "get_events_by_id", "delete_events", + "create_by_conversation_id", "get_events_by_participant_id", "create_by_participant_id", - "get_events", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/lists/test_contracts.py b/tests/lists/test_contracts.py index aa7dc1d..fa33208 100644 --- a/tests/lists/test_contracts.py +++ b/tests/lists/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.lists_client = getattr(self.client, "lists") - def test_get_posts_request_structure(self): - """Test get_posts request structure.""" + def test_create_request_structure(self): + """Test create request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -37,15 +37,18 @@ def test_get_posts_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.lists.models import CreateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateRequest() # Call the method try: - method = getattr(self.lists_client, "get_posts") + method = getattr(self.lists_client, "create") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -64,7 +67,7 @@ def test_get_posts_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -73,14 +76,14 @@ def test_get_posts_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/lists/{id}/tweets" + expected_path = "/2/lists" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -96,12 +99,12 @@ def test_get_posts_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_posts: {e}") + pytest.fail(f"Contract test failed for create: {e}") - def test_get_posts_required_parameters(self): - """Test that get_posts handles parameters correctly.""" - method = getattr(self.lists_client, "get_posts") + def test_create_required_parameters(self): + """Test that create handles parameters correctly.""" + method = getattr(self.lists_client, "create") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -109,14 +112,14 @@ def test_get_posts_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_posts_response_structure(self): - """Test get_posts response structure validation.""" + def test_create_response_structure(self): + """Test create response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -126,13 +129,16 @@ def test_get_posts_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.lists.models import CreateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateRequest() # Call method and verify response structure - method = getattr(self.lists_client, "get_posts") + method = getattr(self.lists_client, "create") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -144,8 +150,8 @@ def test_get_posts_response_structure(self): ) - def test_create_request_structure(self): - """Test create request structure.""" + def test_get_posts_request_structure(self): + """Test get_posts request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -154,18 +160,15 @@ def test_create_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.lists.models import CreateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateRequest() # Call the method try: - method = getattr(self.lists_client, "create") + method = getattr(self.lists_client, "get_posts") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -184,7 +187,7 @@ def test_create_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -193,14 +196,14 @@ def test_create_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/lists" + expected_path = "/2/lists/{id}/tweets" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -216,12 +219,12 @@ def test_create_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create: {e}") + pytest.fail(f"Contract test failed for get_posts: {e}") - def test_create_required_parameters(self): - """Test that create handles parameters correctly.""" - method = getattr(self.lists_client, "create") + def test_get_posts_required_parameters(self): + """Test that get_posts handles parameters correctly.""" + method = getattr(self.lists_client, "get_posts") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -229,14 +232,14 @@ def test_create_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_response_structure(self): - """Test create response structure validation.""" + def test_get_posts_response_structure(self): + """Test get_posts response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -246,16 +249,13 @@ def test_create_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.lists.models import CreateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateRequest() # Call method and verify response structure - method = getattr(self.lists_client, "create") + method = getattr(self.lists_client, "get_posts") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -628,8 +628,8 @@ def test_add_member_response_structure(self): ) - def test_get_followers_request_structure(self): - """Test get_followers request structure.""" + def test_get_by_id_request_structure(self): + """Test get_by_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -646,7 +646,7 @@ def test_get_followers_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.lists_client, "get_followers") + method = getattr(self.lists_client, "get_by_id") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -681,7 +681,7 @@ def test_get_followers_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/lists/{id}/followers" + expected_path = "/2/lists/{id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -697,12 +697,12 @@ def test_get_followers_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_followers: {e}") + pytest.fail(f"Contract test failed for get_by_id: {e}") - def test_get_followers_required_parameters(self): - """Test that get_followers handles parameters correctly.""" - method = getattr(self.lists_client, "get_followers") + def test_get_by_id_required_parameters(self): + """Test that get_by_id handles parameters correctly.""" + method = getattr(self.lists_client, "get_by_id") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -716,8 +716,8 @@ def test_get_followers_required_parameters(self): method() - def test_get_followers_response_structure(self): - """Test get_followers response structure validation.""" + def test_get_by_id_response_structure(self): + """Test get_by_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -733,7 +733,7 @@ def test_get_followers_response_structure(self): kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.lists_client, "get_followers") + method = getattr(self.lists_client, "get_by_id") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -745,8 +745,8 @@ def test_get_followers_response_structure(self): ) - def test_get_by_id_request_structure(self): - """Test get_by_id request structure.""" + def test_update_request_structure(self): + """Test update request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -755,15 +755,19 @@ def test_get_by_id_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.lists.models import UpdateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = UpdateRequest() # Call the method try: - method = getattr(self.lists_client, "get_by_id") + method = getattr(self.lists_client, "update") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -782,7 +786,7 @@ def test_get_by_id_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.put.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -791,9 +795,9 @@ def test_get_by_id_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.put.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.put.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") @@ -814,12 +818,12 @@ def test_get_by_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_id: {e}") + pytest.fail(f"Contract test failed for update: {e}") - def test_get_by_id_required_parameters(self): - """Test that get_by_id handles parameters correctly.""" - method = getattr(self.lists_client, "get_by_id") + def test_update_required_parameters(self): + """Test that update handles parameters correctly.""" + method = getattr(self.lists_client, "update") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -827,14 +831,14 @@ def test_get_by_id_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_by_id_response_structure(self): - """Test get_by_id response structure validation.""" + def test_update_response_structure(self): + """Test update response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -844,13 +848,17 @@ def test_get_by_id_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.lists.models import UpdateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = UpdateRequest() # Call method and verify response structure - method = getattr(self.lists_client, "get_by_id") + method = getattr(self.lists_client, "update") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -862,8 +870,8 @@ def test_get_by_id_response_structure(self): ) - def test_update_request_structure(self): - """Test update request structure.""" + def test_delete_request_structure(self): + """Test delete request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -872,19 +880,15 @@ def test_update_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.put.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.lists.models import UpdateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = UpdateRequest() # Call the method try: - method = getattr(self.lists_client, "update") + method = getattr(self.lists_client, "delete") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -903,7 +907,7 @@ def test_update_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.put.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -912,9 +916,9 @@ def test_update_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.put.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.put.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") @@ -935,12 +939,12 @@ def test_update_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for update: {e}") + pytest.fail(f"Contract test failed for delete: {e}") - def test_update_required_parameters(self): - """Test that update handles parameters correctly.""" - method = getattr(self.lists_client, "update") + def test_delete_required_parameters(self): + """Test that delete handles parameters correctly.""" + method = getattr(self.lists_client, "delete") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -948,14 +952,14 @@ def test_update_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.put.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_update_response_structure(self): - """Test update response structure validation.""" + def test_delete_response_structure(self): + """Test delete response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -965,17 +969,13 @@ def test_update_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.put.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.lists.models import UpdateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = UpdateRequest() # Call method and verify response structure - method = getattr(self.lists_client, "update") + method = getattr(self.lists_client, "delete") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -987,8 +987,8 @@ def test_update_response_structure(self): ) - def test_delete_request_structure(self): - """Test delete request structure.""" + def test_get_followers_request_structure(self): + """Test get_followers request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -997,7 +997,7 @@ def test_delete_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters @@ -1005,7 +1005,7 @@ def test_delete_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.lists_client, "delete") + method = getattr(self.lists_client, "get_followers") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1024,7 +1024,7 @@ def test_delete_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1033,14 +1033,14 @@ def test_delete_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/lists/{id}" + expected_path = "/2/lists/{id}/followers" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1056,12 +1056,12 @@ def test_delete_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for delete: {e}") + pytest.fail(f"Contract test failed for get_followers: {e}") - def test_delete_required_parameters(self): - """Test that delete handles parameters correctly.""" - method = getattr(self.lists_client, "delete") + def test_get_followers_required_parameters(self): + """Test that get_followers handles parameters correctly.""" + method = getattr(self.lists_client, "get_followers") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1069,14 +1069,14 @@ def test_delete_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_delete_response_structure(self): - """Test delete response structure validation.""" + def test_get_followers_response_structure(self): + """Test get_followers response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1086,13 +1086,13 @@ def test_delete_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.lists_client, "delete") + method = getattr(self.lists_client, "get_followers") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/lists/test_structure.py b/tests/lists/test_structure.py index 073a6a9..b40ee60 100644 --- a/tests/lists/test_structure.py +++ b/tests/lists/test_structure.py @@ -28,6 +28,47 @@ def setup_class(self): self.lists_client = getattr(self.client, "lists") + def test_create_exists(self): + """Test that create method exists with correct signature.""" + # Check method exists + method = getattr(ListsClient, "create", None) + assert method is not None, f"Method create does not exist on ListsClient" + # Check method is callable + assert callable(method), f"create is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert len(params) >= 1, f"create should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from create" + # Check optional parameters have defaults (excluding 'self') + optional_params = [] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_create_return_annotation(self): + """Test that create has proper return type annotation.""" + method = getattr(ListsClient, "create") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method create should have return type annotation" + + def test_get_posts_exists(self): """Test that get_posts method exists with correct signature.""" # Check method exists @@ -99,47 +140,6 @@ def test_get_posts_pagination_params(self): ), f"Paginated method get_posts should have pagination parameters" - def test_create_exists(self): - """Test that create method exists with correct signature.""" - # Check method exists - method = getattr(ListsClient, "create", None) - assert method is not None, f"Method create does not exist on ListsClient" - # Check method is callable - assert callable(method), f"create is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"create should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from create" - # Check optional parameters have defaults (excluding 'self') - optional_params = [] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_create_return_annotation(self): - """Test that create has proper return type annotation.""" - method = getattr(ListsClient, "create") - sig = inspect.signature(method) - # Check return annotation exists - assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method create should have return type annotation" - - def test_remove_member_by_user_id_exists(self): """Test that remove_member_by_user_id method exists with correct signature.""" # Check method exists @@ -299,74 +299,6 @@ def test_add_member_return_annotation(self): ), f"Method add_member should have return type annotation" - def test_get_followers_exists(self): - """Test that get_followers method exists with correct signature.""" - # Check method exists - method = getattr(ListsClient, "get_followers", None) - assert method is not None, f"Method get_followers does not exist on ListsClient" - # Check method is callable - assert callable(method), f"get_followers is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"get_followers should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [ - "id", - ] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from get_followers" - # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "max_results", - "pagination_token", - "user.fields", - "expansions", - "tweet.fields", - ] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_get_followers_return_annotation(self): - """Test that get_followers has proper return type annotation.""" - method = getattr(ListsClient, "get_followers") - sig = inspect.signature(method) - # Check return annotation exists - assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method get_followers should have return type annotation" - - - def test_get_followers_pagination_params(self): - """Test that get_followers has pagination parameters.""" - method = getattr(ListsClient, "get_followers") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_followers should have pagination parameters" - - def test_get_by_id_exists(self): """Test that get_by_id method exists with correct signature.""" # Check method exists @@ -500,18 +432,86 @@ def test_delete_return_annotation(self): ), f"Method delete should have return type annotation" + def test_get_followers_exists(self): + """Test that get_followers method exists with correct signature.""" + # Check method exists + method = getattr(ListsClient, "get_followers", None) + assert method is not None, f"Method get_followers does not exist on ListsClient" + # Check method is callable + assert callable(method), f"get_followers is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert len(params) >= 1, f"get_followers should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [ + "id", + ] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from get_followers" + # Check optional parameters have defaults (excluding 'self') + optional_params = [ + "max_results", + "pagination_token", + "user.fields", + "expansions", + "tweet.fields", + ] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_get_followers_return_annotation(self): + """Test that get_followers has proper return type annotation.""" + method = getattr(ListsClient, "get_followers") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method get_followers should have return type annotation" + + + def test_get_followers_pagination_params(self): + """Test that get_followers has pagination parameters.""" + method = getattr(ListsClient, "get_followers") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_followers should have pagination parameters" + + def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "get_posts", "create", + "get_posts", "remove_member_by_user_id", "get_members", "add_member", - "get_followers", "get_by_id", "update", "delete", + "get_followers", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/media/test_contracts.py b/tests/media/test_contracts.py index d444304..0d7b9fa 100644 --- a/tests/media/test_contracts.py +++ b/tests/media/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.media_client = getattr(self.client, "media") - def test_get_by_key_request_structure(self): - """Test get_by_key request structure.""" + def test_append_upload_request_structure(self): + """Test append_upload request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -37,15 +37,19 @@ def test_get_by_key_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["media_key"] = "test_value" + kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.media.models import AppendUploadRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = AppendUploadRequest() # Call the method try: - method = getattr(self.media_client, "get_by_key") + method = getattr(self.media_client, "append_upload") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -64,7 +68,7 @@ def test_get_by_key_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -73,14 +77,14 @@ def test_get_by_key_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/media/{media_key}" + expected_path = "/2/media/upload/{id}/append" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -96,12 +100,12 @@ def test_get_by_key_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_key: {e}") + pytest.fail(f"Contract test failed for append_upload: {e}") - def test_get_by_key_required_parameters(self): - """Test that get_by_key handles parameters correctly.""" - method = getattr(self.media_client, "get_by_key") + def test_append_upload_required_parameters(self): + """Test that append_upload handles parameters correctly.""" + method = getattr(self.media_client, "append_upload") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -109,14 +113,14 @@ def test_get_by_key_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_by_key_response_structure(self): - """Test get_by_key response structure validation.""" + def test_append_upload_response_structure(self): + """Test append_upload response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -126,13 +130,17 @@ def test_get_by_key_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["media_key"] = "test" + kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.media.models import AppendUploadRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = AppendUploadRequest() # Call method and verify response structure - method = getattr(self.media_client, "get_by_key") + method = getattr(self.media_client, "append_upload") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -390,8 +398,8 @@ def test_delete_subtitles_response_structure(self): ) - def test_get_analytics_request_structure(self): - """Test get_analytics request structure.""" + def test_finalize_upload_request_structure(self): + """Test finalize_upload request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -400,18 +408,15 @@ def test_get_analytics_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["media_keys"] = ["test_item"] - kwargs["end_time"] = "test_end_time" - kwargs["start_time"] = "test_start_time" - kwargs["granularity"] = "test_granularity" + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.media_client, "get_analytics") + method = getattr(self.media_client, "finalize_upload") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -430,7 +435,7 @@ def test_get_analytics_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -439,14 +444,14 @@ def test_get_analytics_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/media/analytics" + expected_path = "/2/media/upload/{id}/finalize" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -462,12 +467,12 @@ def test_get_analytics_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_analytics: {e}") + pytest.fail(f"Contract test failed for finalize_upload: {e}") - def test_get_analytics_required_parameters(self): - """Test that get_analytics handles parameters correctly.""" - method = getattr(self.media_client, "get_analytics") + def test_finalize_upload_required_parameters(self): + """Test that finalize_upload handles parameters correctly.""" + method = getattr(self.media_client, "finalize_upload") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -475,14 +480,14 @@ def test_get_analytics_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_analytics_response_structure(self): - """Test get_analytics response structure validation.""" + def test_finalize_upload_response_structure(self): + """Test finalize_upload response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -492,16 +497,13 @@ def test_get_analytics_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["media_keys"] = ["test"] - kwargs["end_time"] = "test_value" - kwargs["start_time"] = "test_value" - kwargs["granularity"] = "test_value" + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.media_client, "get_analytics") + method = getattr(self.media_client, "finalize_upload") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -513,8 +515,8 @@ def test_get_analytics_response_structure(self): ) - def test_get_upload_status_request_structure(self): - """Test get_upload_status request structure.""" + def test_get_analytics_request_structure(self): + """Test get_analytics request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -527,11 +529,14 @@ def test_get_upload_status_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["media_id"] = "test_value" + kwargs["media_keys"] = ["test_item"] + kwargs["end_time"] = "test_end_time" + kwargs["start_time"] = "test_start_time" + kwargs["granularity"] = "test_granularity" # Add request body if required # Call the method try: - method = getattr(self.media_client, "get_upload_status") + method = getattr(self.media_client, "get_analytics") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -566,7 +571,7 @@ def test_get_upload_status_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/media/upload" + expected_path = "/2/media/analytics" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -582,12 +587,12 @@ def test_get_upload_status_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_upload_status: {e}") + pytest.fail(f"Contract test failed for get_analytics: {e}") - def test_get_upload_status_required_parameters(self): - """Test that get_upload_status handles parameters correctly.""" - method = getattr(self.media_client, "get_upload_status") + def test_get_analytics_required_parameters(self): + """Test that get_analytics handles parameters correctly.""" + method = getattr(self.media_client, "get_analytics") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -601,8 +606,8 @@ def test_get_upload_status_required_parameters(self): method() - def test_get_upload_status_response_structure(self): - """Test get_upload_status response structure validation.""" + def test_get_analytics_response_structure(self): + """Test get_analytics response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -615,10 +620,13 @@ def test_get_upload_status_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["media_id"] = "test" + kwargs["media_keys"] = ["test"] + kwargs["end_time"] = "test_value" + kwargs["start_time"] = "test_value" + kwargs["granularity"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.media_client, "get_upload_status") + method = getattr(self.media_client, "get_analytics") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -630,8 +638,8 @@ def test_get_upload_status_response_structure(self): ) - def test_upload_request_structure(self): - """Test upload request structure.""" + def test_get_by_keys_request_structure(self): + """Test get_by_keys request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -640,18 +648,15 @@ def test_upload_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["media_keys"] = ["test_item"] # Add request body if required - # Import and create proper request model instance - from xdk.media.models import UploadRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = UploadRequest() # Call the method try: - method = getattr(self.media_client, "upload") + method = getattr(self.media_client, "get_by_keys") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -670,7 +675,7 @@ def test_upload_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -679,14 +684,14 @@ def test_upload_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/media/upload" + expected_path = "/2/media" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -702,12 +707,12 @@ def test_upload_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for upload: {e}") + pytest.fail(f"Contract test failed for get_by_keys: {e}") - def test_upload_required_parameters(self): - """Test that upload handles parameters correctly.""" - method = getattr(self.media_client, "upload") + def test_get_by_keys_required_parameters(self): + """Test that get_by_keys handles parameters correctly.""" + method = getattr(self.media_client, "get_by_keys") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -715,14 +720,14 @@ def test_upload_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_upload_response_structure(self): - """Test upload response structure validation.""" + def test_get_by_keys_response_structure(self): + """Test get_by_keys response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -732,16 +737,13 @@ def test_upload_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["media_keys"] = ["test"] # Add request body if required - # Import and create proper request model instance - from xdk.media.models import UploadRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = UploadRequest() # Call method and verify response structure - method = getattr(self.media_client, "upload") + method = getattr(self.media_client, "get_by_keys") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -753,8 +755,8 @@ def test_upload_response_structure(self): ) - def test_append_upload_request_structure(self): - """Test append_upload request structure.""" + def test_get_upload_status_request_structure(self): + """Test get_upload_status request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -763,19 +765,15 @@ def test_append_upload_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["media_id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.media.models import AppendUploadRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = AppendUploadRequest() # Call the method try: - method = getattr(self.media_client, "append_upload") + method = getattr(self.media_client, "get_upload_status") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -794,7 +792,7 @@ def test_append_upload_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -803,14 +801,14 @@ def test_append_upload_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/media/upload/{id}/append" + expected_path = "/2/media/upload" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -826,12 +824,12 @@ def test_append_upload_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for append_upload: {e}") + pytest.fail(f"Contract test failed for get_upload_status: {e}") - def test_append_upload_required_parameters(self): - """Test that append_upload handles parameters correctly.""" - method = getattr(self.media_client, "append_upload") + def test_get_upload_status_required_parameters(self): + """Test that get_upload_status handles parameters correctly.""" + method = getattr(self.media_client, "get_upload_status") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -839,14 +837,14 @@ def test_append_upload_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_append_upload_response_structure(self): - """Test append_upload response structure validation.""" + def test_get_upload_status_response_structure(self): + """Test get_upload_status response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -856,17 +854,13 @@ def test_append_upload_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["media_id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.media.models import AppendUploadRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = AppendUploadRequest() # Call method and verify response structure - method = getattr(self.media_client, "append_upload") + method = getattr(self.media_client, "get_upload_status") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -878,8 +872,8 @@ def test_append_upload_response_structure(self): ) - def test_get_by_keys_request_structure(self): - """Test get_by_keys request structure.""" + def test_upload_request_structure(self): + """Test upload request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -888,15 +882,18 @@ def test_get_by_keys_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["media_keys"] = ["test_item"] # Add request body if required + # Import and create proper request model instance + from xdk.media.models import UploadRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = UploadRequest() # Call the method try: - method = getattr(self.media_client, "get_by_keys") + method = getattr(self.media_client, "upload") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -915,7 +912,7 @@ def test_get_by_keys_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -924,14 +921,14 @@ def test_get_by_keys_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/media" + expected_path = "/2/media/upload" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -947,12 +944,12 @@ def test_get_by_keys_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_keys: {e}") + pytest.fail(f"Contract test failed for upload: {e}") - def test_get_by_keys_required_parameters(self): - """Test that get_by_keys handles parameters correctly.""" - method = getattr(self.media_client, "get_by_keys") + def test_upload_required_parameters(self): + """Test that upload handles parameters correctly.""" + method = getattr(self.media_client, "upload") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -960,14 +957,14 @@ def test_get_by_keys_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_by_keys_response_structure(self): - """Test get_by_keys response structure validation.""" + def test_upload_response_structure(self): + """Test upload response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -977,13 +974,16 @@ def test_get_by_keys_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["media_keys"] = ["test"] # Add request body if required + # Import and create proper request model instance + from xdk.media.models import UploadRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = UploadRequest() # Call method and verify response structure - method = getattr(self.media_client, "get_by_keys") + method = getattr(self.media_client, "upload") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -995,8 +995,8 @@ def test_get_by_keys_response_structure(self): ) - def test_create_metadata_request_structure(self): - """Test create_metadata request structure.""" + def test_get_by_key_request_structure(self): + """Test get_by_key request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1005,18 +1005,15 @@ def test_create_metadata_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["media_key"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.media.models import CreateMetadataRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateMetadataRequest() # Call the method try: - method = getattr(self.media_client, "create_metadata") + method = getattr(self.media_client, "get_by_key") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1035,7 +1032,7 @@ def test_create_metadata_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1044,14 +1041,14 @@ def test_create_metadata_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/media/metadata" + expected_path = "/2/media/{media_key}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1067,12 +1064,12 @@ def test_create_metadata_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create_metadata: {e}") + pytest.fail(f"Contract test failed for get_by_key: {e}") - def test_create_metadata_required_parameters(self): - """Test that create_metadata handles parameters correctly.""" - method = getattr(self.media_client, "create_metadata") + def test_get_by_key_required_parameters(self): + """Test that get_by_key handles parameters correctly.""" + method = getattr(self.media_client, "get_by_key") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1080,14 +1077,14 @@ def test_create_metadata_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_metadata_response_structure(self): - """Test create_metadata response structure validation.""" + def test_get_by_key_response_structure(self): + """Test get_by_key response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1097,16 +1094,13 @@ def test_create_metadata_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["media_key"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.media.models import CreateMetadataRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateMetadataRequest() # Call method and verify response structure - method = getattr(self.media_client, "create_metadata") + method = getattr(self.media_client, "get_by_key") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1241,8 +1235,8 @@ def test_initialize_upload_response_structure(self): ) - def test_finalize_upload_request_structure(self): - """Test finalize_upload request structure.""" + def test_create_metadata_request_structure(self): + """Test create_metadata request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1255,11 +1249,14 @@ def test_finalize_upload_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.media.models import CreateMetadataRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateMetadataRequest() # Call the method try: - method = getattr(self.media_client, "finalize_upload") + method = getattr(self.media_client, "create_metadata") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1294,7 +1291,7 @@ def test_finalize_upload_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/media/upload/{id}/finalize" + expected_path = "/2/media/metadata" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1310,12 +1307,12 @@ def test_finalize_upload_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for finalize_upload: {e}") + pytest.fail(f"Contract test failed for create_metadata: {e}") - def test_finalize_upload_required_parameters(self): - """Test that finalize_upload handles parameters correctly.""" - method = getattr(self.media_client, "finalize_upload") + def test_create_metadata_required_parameters(self): + """Test that create_metadata handles parameters correctly.""" + method = getattr(self.media_client, "create_metadata") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1329,8 +1326,8 @@ def test_finalize_upload_required_parameters(self): method() - def test_finalize_upload_response_structure(self): - """Test finalize_upload response structure validation.""" + def test_create_metadata_response_structure(self): + """Test create_metadata response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1343,10 +1340,13 @@ def test_finalize_upload_response_structure(self): mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.media.models import CreateMetadataRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateMetadataRequest() # Call method and verify response structure - method = getattr(self.media_client, "finalize_upload") + method = getattr(self.media_client, "create_metadata") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/media/test_structure.py b/tests/media/test_structure.py index f912ed1..e479932 100644 --- a/tests/media/test_structure.py +++ b/tests/media/test_structure.py @@ -28,33 +28,31 @@ def setup_class(self): self.media_client = getattr(self.client, "media") - def test_get_by_key_exists(self): - """Test that get_by_key method exists with correct signature.""" + def test_append_upload_exists(self): + """Test that append_upload method exists with correct signature.""" # Check method exists - method = getattr(MediaClient, "get_by_key", None) - assert method is not None, f"Method get_by_key does not exist on MediaClient" + method = getattr(MediaClient, "append_upload", None) + assert method is not None, f"Method append_upload does not exist on MediaClient" # Check method is callable - assert callable(method), f"get_by_key is not callable" + assert callable(method), f"append_upload is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_by_key should have at least 'self' parameter" + assert len(params) >= 1, f"append_upload should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "media_key", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_key" + ), f"Required parameter '{required_param}' missing from append_upload" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "media.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -63,14 +61,14 @@ def test_get_by_key_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_key_return_annotation(self): - """Test that get_by_key has proper return type annotation.""" - method = getattr(MediaClient, "get_by_key") + def test_append_upload_return_annotation(self): + """Test that append_upload has proper return type annotation.""" + method = getattr(MediaClient, "append_upload") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_key should have return type annotation" + ), f"Method append_upload should have return type annotation" def test_create_subtitles_exists(self): @@ -163,36 +161,35 @@ def test_delete_subtitles_return_annotation(self): ), f"Method delete_subtitles should have return type annotation" - def test_get_analytics_exists(self): - """Test that get_analytics method exists with correct signature.""" + def test_finalize_upload_exists(self): + """Test that finalize_upload method exists with correct signature.""" # Check method exists - method = getattr(MediaClient, "get_analytics", None) - assert method is not None, f"Method get_analytics does not exist on MediaClient" + method = getattr(MediaClient, "finalize_upload", None) + assert ( + method is not None + ), f"Method finalize_upload does not exist on MediaClient" # Check method is callable - assert callable(method), f"get_analytics is not callable" + assert callable(method), f"finalize_upload is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_analytics should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"finalize_upload should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "media_keys", - "end_time", - "start_time", - "granularity", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_analytics" + ), f"Required parameter '{required_param}' missing from finalize_upload" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "media_analytics.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -201,46 +198,45 @@ def test_get_analytics_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_analytics_return_annotation(self): - """Test that get_analytics has proper return type annotation.""" - method = getattr(MediaClient, "get_analytics") + def test_finalize_upload_return_annotation(self): + """Test that finalize_upload has proper return type annotation.""" + method = getattr(MediaClient, "finalize_upload") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_analytics should have return type annotation" + ), f"Method finalize_upload should have return type annotation" - def test_get_upload_status_exists(self): - """Test that get_upload_status method exists with correct signature.""" + def test_get_analytics_exists(self): + """Test that get_analytics method exists with correct signature.""" # Check method exists - method = getattr(MediaClient, "get_upload_status", None) - assert ( - method is not None - ), f"Method get_upload_status does not exist on MediaClient" + method = getattr(MediaClient, "get_analytics", None) + assert method is not None, f"Method get_analytics does not exist on MediaClient" # Check method is callable - assert callable(method), f"get_upload_status is not callable" + assert callable(method), f"get_analytics is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_upload_status should have at least 'self' parameter" + assert len(params) >= 1, f"get_analytics should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "media_id", + "media_keys", + "end_time", + "start_time", + "granularity", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_upload_status" + ), f"Required parameter '{required_param}' missing from get_analytics" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "command", + "media_analytics.fields", ] for optional_param in optional_params: if optional_param in params: @@ -250,39 +246,43 @@ def test_get_upload_status_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_upload_status_return_annotation(self): - """Test that get_upload_status has proper return type annotation.""" - method = getattr(MediaClient, "get_upload_status") + def test_get_analytics_return_annotation(self): + """Test that get_analytics has proper return type annotation.""" + method = getattr(MediaClient, "get_analytics") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_upload_status should have return type annotation" + ), f"Method get_analytics should have return type annotation" - def test_upload_exists(self): - """Test that upload method exists with correct signature.""" + def test_get_by_keys_exists(self): + """Test that get_by_keys method exists with correct signature.""" # Check method exists - method = getattr(MediaClient, "upload", None) - assert method is not None, f"Method upload does not exist on MediaClient" + method = getattr(MediaClient, "get_by_keys", None) + assert method is not None, f"Method get_by_keys does not exist on MediaClient" # Check method is callable - assert callable(method), f"upload is not callable" + assert callable(method), f"get_by_keys is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"upload should have at least 'self' parameter" + assert len(params) >= 1, f"get_by_keys should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "media_keys", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from upload" + ), f"Required parameter '{required_param}' missing from get_by_keys" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "media.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -291,41 +291,47 @@ def test_upload_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_upload_return_annotation(self): - """Test that upload has proper return type annotation.""" - method = getattr(MediaClient, "upload") + def test_get_by_keys_return_annotation(self): + """Test that get_by_keys has proper return type annotation.""" + method = getattr(MediaClient, "get_by_keys") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method upload should have return type annotation" + ), f"Method get_by_keys should have return type annotation" - def test_append_upload_exists(self): - """Test that append_upload method exists with correct signature.""" + def test_get_upload_status_exists(self): + """Test that get_upload_status method exists with correct signature.""" # Check method exists - method = getattr(MediaClient, "append_upload", None) - assert method is not None, f"Method append_upload does not exist on MediaClient" + method = getattr(MediaClient, "get_upload_status", None) + assert ( + method is not None + ), f"Method get_upload_status does not exist on MediaClient" # Check method is callable - assert callable(method), f"append_upload is not callable" + assert callable(method), f"get_upload_status is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"append_upload should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_upload_status should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "media_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from append_upload" + ), f"Required parameter '{required_param}' missing from get_upload_status" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "command", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -334,43 +340,39 @@ def test_append_upload_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_append_upload_return_annotation(self): - """Test that append_upload has proper return type annotation.""" - method = getattr(MediaClient, "append_upload") + def test_get_upload_status_return_annotation(self): + """Test that get_upload_status has proper return type annotation.""" + method = getattr(MediaClient, "get_upload_status") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method append_upload should have return type annotation" + ), f"Method get_upload_status should have return type annotation" - def test_get_by_keys_exists(self): - """Test that get_by_keys method exists with correct signature.""" + def test_upload_exists(self): + """Test that upload method exists with correct signature.""" # Check method exists - method = getattr(MediaClient, "get_by_keys", None) - assert method is not None, f"Method get_by_keys does not exist on MediaClient" + method = getattr(MediaClient, "upload", None) + assert method is not None, f"Method upload does not exist on MediaClient" # Check method is callable - assert callable(method), f"get_by_keys is not callable" + assert callable(method), f"upload is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_by_keys should have at least 'self' parameter" + assert len(params) >= 1, f"upload should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "media_keys", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_keys" + ), f"Required parameter '{required_param}' missing from upload" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "media.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -379,43 +381,43 @@ def test_get_by_keys_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_keys_return_annotation(self): - """Test that get_by_keys has proper return type annotation.""" - method = getattr(MediaClient, "get_by_keys") + def test_upload_return_annotation(self): + """Test that upload has proper return type annotation.""" + method = getattr(MediaClient, "upload") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_keys should have return type annotation" + ), f"Method upload should have return type annotation" - def test_create_metadata_exists(self): - """Test that create_metadata method exists with correct signature.""" + def test_get_by_key_exists(self): + """Test that get_by_key method exists with correct signature.""" # Check method exists - method = getattr(MediaClient, "create_metadata", None) - assert ( - method is not None - ), f"Method create_metadata does not exist on MediaClient" + method = getattr(MediaClient, "get_by_key", None) + assert method is not None, f"Method get_by_key does not exist on MediaClient" # Check method is callable - assert callable(method), f"create_metadata is not callable" + assert callable(method), f"get_by_key is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"create_metadata should have at least 'self' parameter" + assert len(params) >= 1, f"get_by_key should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "media_key", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from create_metadata" + ), f"Required parameter '{required_param}' missing from get_by_key" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "media.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -424,14 +426,14 @@ def test_create_metadata_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_create_metadata_return_annotation(self): - """Test that create_metadata has proper return type annotation.""" - method = getattr(MediaClient, "create_metadata") + def test_get_by_key_return_annotation(self): + """Test that get_by_key has proper return type annotation.""" + method = getattr(MediaClient, "get_by_key") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method create_metadata should have return type annotation" + ), f"Method get_by_key should have return type annotation" def test_initialize_upload_exists(self): @@ -479,33 +481,31 @@ def test_initialize_upload_return_annotation(self): ), f"Method initialize_upload should have return type annotation" - def test_finalize_upload_exists(self): - """Test that finalize_upload method exists with correct signature.""" + def test_create_metadata_exists(self): + """Test that create_metadata method exists with correct signature.""" # Check method exists - method = getattr(MediaClient, "finalize_upload", None) + method = getattr(MediaClient, "create_metadata", None) assert ( method is not None - ), f"Method finalize_upload does not exist on MediaClient" + ), f"Method create_metadata does not exist on MediaClient" # Check method is callable - assert callable(method), f"finalize_upload is not callable" + assert callable(method), f"create_metadata is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"finalize_upload should have at least 'self' parameter" + ), f"create_metadata should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "id", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from finalize_upload" + ), f"Required parameter '{required_param}' missing from create_metadata" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -516,30 +516,30 @@ def test_finalize_upload_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_finalize_upload_return_annotation(self): - """Test that finalize_upload has proper return type annotation.""" - method = getattr(MediaClient, "finalize_upload") + def test_create_metadata_return_annotation(self): + """Test that create_metadata has proper return type annotation.""" + method = getattr(MediaClient, "create_metadata") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method finalize_upload should have return type annotation" + ), f"Method create_metadata should have return type annotation" def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "get_by_key", + "append_upload", "create_subtitles", "delete_subtitles", + "finalize_upload", "get_analytics", + "get_by_keys", "get_upload_status", "upload", - "append_upload", - "get_by_keys", - "create_metadata", + "get_by_key", "initialize_upload", - "finalize_upload", + "create_metadata", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/news/test_contracts.py b/tests/news/test_contracts.py index 6b2aeb1..964ac82 100644 --- a/tests/news/test_contracts.py +++ b/tests/news/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.news_client = getattr(self.client, "news") - def test_get_request_structure(self): - """Test get request structure.""" + def test_search_request_structure(self): + """Test search request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -41,11 +41,11 @@ def test_get_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["query"] = "test_query" # Add request body if required # Call the method try: - method = getattr(self.news_client, "get") + method = getattr(self.news_client, "search") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -80,7 +80,7 @@ def test_get_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/news/{id}" + expected_path = "/2/news/search" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -96,12 +96,12 @@ def test_get_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get: {e}") + pytest.fail(f"Contract test failed for search: {e}") - def test_get_required_parameters(self): - """Test that get handles parameters correctly.""" - method = getattr(self.news_client, "get") + def test_search_required_parameters(self): + """Test that search handles parameters correctly.""" + method = getattr(self.news_client, "search") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -115,8 +115,8 @@ def test_get_required_parameters(self): method() - def test_get_response_structure(self): - """Test get response structure validation.""" + def test_search_response_structure(self): + """Test search response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -129,10 +129,10 @@ def test_get_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["query"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.news_client, "get") + method = getattr(self.news_client, "search") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -144,8 +144,8 @@ def test_get_response_structure(self): ) - def test_search_request_structure(self): - """Test search request structure.""" + def test_get_request_structure(self): + """Test get request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -158,11 +158,11 @@ def test_search_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["query"] = "test_query" + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.news_client, "search") + method = getattr(self.news_client, "get") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -197,7 +197,7 @@ def test_search_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/news/search" + expected_path = "/2/news/{id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -213,12 +213,12 @@ def test_search_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for search: {e}") + pytest.fail(f"Contract test failed for get: {e}") - def test_search_required_parameters(self): - """Test that search handles parameters correctly.""" - method = getattr(self.news_client, "search") + def test_get_required_parameters(self): + """Test that get handles parameters correctly.""" + method = getattr(self.news_client, "get") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -232,8 +232,8 @@ def test_search_required_parameters(self): method() - def test_search_response_structure(self): - """Test search response structure validation.""" + def test_get_response_structure(self): + """Test get response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -246,10 +246,10 @@ def test_search_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["query"] = "test_value" + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.news_client, "search") + method = getattr(self.news_client, "get") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/news/test_structure.py b/tests/news/test_structure.py index 9525942..53e0a39 100644 --- a/tests/news/test_structure.py +++ b/tests/news/test_structure.py @@ -28,31 +28,33 @@ def setup_class(self): self.news_client = getattr(self.client, "news") - def test_get_exists(self): - """Test that get method exists with correct signature.""" + def test_search_exists(self): + """Test that search method exists with correct signature.""" # Check method exists - method = getattr(NewsClient, "get", None) - assert method is not None, f"Method get does not exist on NewsClient" + method = getattr(NewsClient, "search", None) + assert method is not None, f"Method search does not exist on NewsClient" # Check method is callable - assert callable(method), f"get is not callable" + assert callable(method), f"search is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get should have at least 'self' parameter" + assert len(params) >= 1, f"search should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "query", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get" + ), f"Required parameter '{required_param}' missing from search" # Check optional parameters have defaults (excluding 'self') optional_params = [ + "max_results", + "max_age_hours", "news.fields", ] for optional_param in optional_params: @@ -63,43 +65,41 @@ def test_get_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_return_annotation(self): - """Test that get has proper return type annotation.""" - method = getattr(NewsClient, "get") + def test_search_return_annotation(self): + """Test that search has proper return type annotation.""" + method = getattr(NewsClient, "search") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get should have return type annotation" + ), f"Method search should have return type annotation" - def test_search_exists(self): - """Test that search method exists with correct signature.""" + def test_get_exists(self): + """Test that get method exists with correct signature.""" # Check method exists - method = getattr(NewsClient, "search", None) - assert method is not None, f"Method search does not exist on NewsClient" + method = getattr(NewsClient, "get", None) + assert method is not None, f"Method get does not exist on NewsClient" # Check method is callable - assert callable(method), f"search is not callable" + assert callable(method), f"get is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"search should have at least 'self' parameter" + assert len(params) >= 1, f"get should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "query", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from search" + ), f"Required parameter '{required_param}' missing from get" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "max_results", - "max_age_hours", "news.fields", ] for optional_param in optional_params: @@ -110,21 +110,21 @@ def test_search_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_search_return_annotation(self): - """Test that search has proper return type annotation.""" - method = getattr(NewsClient, "search") + def test_get_return_annotation(self): + """Test that get has proper return type annotation.""" + method = getattr(NewsClient, "get") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method search should have return type annotation" + ), f"Method get should have return type annotation" def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "get", "search", + "get", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/posts/test_contracts.py b/tests/posts/test_contracts.py index 3824f2b..743bffd 100644 --- a/tests/posts/test_contracts.py +++ b/tests/posts/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.posts_client = getattr(self.client, "posts") - def test_search_all_request_structure(self): - """Test search_all request structure.""" + def test_get_liking_users_request_structure(self): + """Test get_liking_users request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -41,11 +41,11 @@ def test_search_all_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["query"] = "test_query" + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.posts_client, "search_all") + method = getattr(self.posts_client, "get_liking_users") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -80,7 +80,7 @@ def test_search_all_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/search/all" + expected_path = "/2/tweets/{id}/liking_users" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -96,12 +96,12 @@ def test_search_all_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for search_all: {e}") + pytest.fail(f"Contract test failed for get_liking_users: {e}") - def test_search_all_required_parameters(self): - """Test that search_all handles parameters correctly.""" - method = getattr(self.posts_client, "search_all") + def test_get_liking_users_required_parameters(self): + """Test that get_liking_users handles parameters correctly.""" + method = getattr(self.posts_client, "get_liking_users") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -115,8 +115,8 @@ def test_search_all_required_parameters(self): method() - def test_search_all_response_structure(self): - """Test search_all response structure validation.""" + def test_get_liking_users_response_structure(self): + """Test get_liking_users response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -129,10 +129,10 @@ def test_search_all_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["query"] = "test_value" + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "search_all") + method = getattr(self.posts_client, "get_liking_users") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -144,8 +144,8 @@ def test_search_all_response_structure(self): ) - def test_get_by_id_request_structure(self): - """Test get_by_id request structure.""" + def test_get_quoted_request_structure(self): + """Test get_quoted request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -162,7 +162,7 @@ def test_get_by_id_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_by_id") + method = getattr(self.posts_client, "get_quoted") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -197,7 +197,7 @@ def test_get_by_id_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/{id}" + expected_path = "/2/tweets/{id}/quote_tweets" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -213,12 +213,12 @@ def test_get_by_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_id: {e}") + pytest.fail(f"Contract test failed for get_quoted: {e}") - def test_get_by_id_required_parameters(self): - """Test that get_by_id handles parameters correctly.""" - method = getattr(self.posts_client, "get_by_id") + def test_get_quoted_required_parameters(self): + """Test that get_quoted handles parameters correctly.""" + method = getattr(self.posts_client, "get_quoted") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -232,8 +232,8 @@ def test_get_by_id_required_parameters(self): method() - def test_get_by_id_response_structure(self): - """Test get_by_id response structure validation.""" + def test_get_quoted_response_structure(self): + """Test get_quoted response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -249,7 +249,7 @@ def test_get_by_id_response_structure(self): kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_by_id") + method = getattr(self.posts_client, "get_quoted") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -261,8 +261,8 @@ def test_get_by_id_response_structure(self): ) - def test_delete_request_structure(self): - """Test delete request structure.""" + def test_get_counts_recent_request_structure(self): + """Test get_counts_recent request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -271,15 +271,15 @@ def test_delete_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["query"] = "test_query" # Add request body if required # Call the method try: - method = getattr(self.posts_client, "delete") + method = getattr(self.posts_client, "get_counts_recent") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -298,7 +298,7 @@ def test_delete_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -307,14 +307,14 @@ def test_delete_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/{id}" + expected_path = "/2/tweets/counts/recent" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -330,12 +330,12 @@ def test_delete_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for delete: {e}") + pytest.fail(f"Contract test failed for get_counts_recent: {e}") - def test_delete_required_parameters(self): - """Test that delete handles parameters correctly.""" - method = getattr(self.posts_client, "delete") + def test_get_counts_recent_required_parameters(self): + """Test that get_counts_recent handles parameters correctly.""" + method = getattr(self.posts_client, "get_counts_recent") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -343,14 +343,14 @@ def test_delete_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_delete_response_structure(self): - """Test delete response structure validation.""" + def test_get_counts_recent_response_structure(self): + """Test get_counts_recent response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -360,13 +360,13 @@ def test_delete_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["query"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "delete") + method = getattr(self.posts_client, "get_counts_recent") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -378,8 +378,8 @@ def test_delete_response_structure(self): ) - def test_get_counts_recent_request_structure(self): - """Test get_counts_recent request structure.""" + def test_hide_reply_request_structure(self): + """Test hide_reply request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -388,15 +388,19 @@ def test_get_counts_recent_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["query"] = "test_query" + kwargs["tweet_id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.posts.models import HideReplyRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = HideReplyRequest() # Call the method try: - method = getattr(self.posts_client, "get_counts_recent") + method = getattr(self.posts_client, "hide_reply") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -415,7 +419,7 @@ def test_get_counts_recent_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.put.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -424,14 +428,14 @@ def test_get_counts_recent_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.put.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.put.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/counts/recent" + expected_path = "/2/tweets/{tweet_id}/hidden" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -447,12 +451,12 @@ def test_get_counts_recent_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_counts_recent: {e}") + pytest.fail(f"Contract test failed for hide_reply: {e}") - def test_get_counts_recent_required_parameters(self): - """Test that get_counts_recent handles parameters correctly.""" - method = getattr(self.posts_client, "get_counts_recent") + def test_hide_reply_required_parameters(self): + """Test that hide_reply handles parameters correctly.""" + method = getattr(self.posts_client, "hide_reply") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -460,14 +464,14 @@ def test_get_counts_recent_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_counts_recent_response_structure(self): - """Test get_counts_recent response structure validation.""" + def test_hide_reply_response_structure(self): + """Test hide_reply response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -477,13 +481,17 @@ def test_get_counts_recent_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["query"] = "test_value" + kwargs["tweet_id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.posts.models import HideReplyRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = HideReplyRequest() # Call method and verify response structure - method = getattr(self.posts_client, "get_counts_recent") + method = getattr(self.posts_client, "hide_reply") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -495,8 +503,8 @@ def test_get_counts_recent_response_structure(self): ) - def test_hide_reply_request_structure(self): - """Test hide_reply request structure.""" + def test_get_counts_all_request_structure(self): + """Test get_counts_all request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -505,19 +513,15 @@ def test_hide_reply_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.put.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["tweet_id"] = "test_value" + kwargs["query"] = "test_query" # Add request body if required - # Import and create proper request model instance - from xdk.posts.models import HideReplyRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = HideReplyRequest() # Call the method try: - method = getattr(self.posts_client, "hide_reply") + method = getattr(self.posts_client, "get_counts_all") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -536,7 +540,7 @@ def test_hide_reply_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.put.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -545,14 +549,14 @@ def test_hide_reply_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.put.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.put.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/{tweet_id}/hidden" + expected_path = "/2/tweets/counts/all" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -568,12 +572,12 @@ def test_hide_reply_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for hide_reply: {e}") + pytest.fail(f"Contract test failed for get_counts_all: {e}") - def test_hide_reply_required_parameters(self): - """Test that hide_reply handles parameters correctly.""" - method = getattr(self.posts_client, "hide_reply") + def test_get_counts_all_required_parameters(self): + """Test that get_counts_all handles parameters correctly.""" + method = getattr(self.posts_client, "get_counts_all") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -581,14 +585,14 @@ def test_hide_reply_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.put.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_hide_reply_response_structure(self): - """Test hide_reply response structure validation.""" + def test_get_counts_all_response_structure(self): + """Test get_counts_all response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -598,17 +602,13 @@ def test_hide_reply_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.put.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["tweet_id"] = "test" + kwargs["query"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.posts.models import HideReplyRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = HideReplyRequest() # Call method and verify response structure - method = getattr(self.posts_client, "hide_reply") + method = getattr(self.posts_client, "get_counts_all") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -620,8 +620,8 @@ def test_hide_reply_response_structure(self): ) - def test_get_liking_users_request_structure(self): - """Test get_liking_users request structure.""" + def test_get_analytics_request_structure(self): + """Test get_analytics request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -634,11 +634,14 @@ def test_get_liking_users_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["ids"] = ["test_item"] + kwargs["end_time"] = "test_end_time" + kwargs["start_time"] = "test_start_time" + kwargs["granularity"] = "test_granularity" # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_liking_users") + method = getattr(self.posts_client, "get_analytics") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -673,7 +676,7 @@ def test_get_liking_users_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/{id}/liking_users" + expected_path = "/2/tweets/analytics" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -689,12 +692,12 @@ def test_get_liking_users_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_liking_users: {e}") + pytest.fail(f"Contract test failed for get_analytics: {e}") - def test_get_liking_users_required_parameters(self): - """Test that get_liking_users handles parameters correctly.""" - method = getattr(self.posts_client, "get_liking_users") + def test_get_analytics_required_parameters(self): + """Test that get_analytics handles parameters correctly.""" + method = getattr(self.posts_client, "get_analytics") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -708,8 +711,8 @@ def test_get_liking_users_required_parameters(self): method() - def test_get_liking_users_response_structure(self): - """Test get_liking_users response structure validation.""" + def test_get_analytics_response_structure(self): + """Test get_analytics response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -722,10 +725,13 @@ def test_get_liking_users_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["ids"] = ["test"] + kwargs["end_time"] = "test_value" + kwargs["start_time"] = "test_value" + kwargs["granularity"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_liking_users") + method = getattr(self.posts_client, "get_analytics") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -737,8 +743,8 @@ def test_get_liking_users_response_structure(self): ) - def test_get_quoted_request_structure(self): - """Test get_quoted request structure.""" + def test_search_all_request_structure(self): + """Test search_all request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -751,11 +757,11 @@ def test_get_quoted_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["query"] = "test_query" # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_quoted") + method = getattr(self.posts_client, "search_all") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -790,7 +796,7 @@ def test_get_quoted_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/{id}/quote_tweets" + expected_path = "/2/tweets/search/all" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -806,12 +812,12 @@ def test_get_quoted_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_quoted: {e}") + pytest.fail(f"Contract test failed for search_all: {e}") - def test_get_quoted_required_parameters(self): - """Test that get_quoted handles parameters correctly.""" - method = getattr(self.posts_client, "get_quoted") + def test_search_all_required_parameters(self): + """Test that search_all handles parameters correctly.""" + method = getattr(self.posts_client, "search_all") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -825,8 +831,8 @@ def test_get_quoted_required_parameters(self): method() - def test_get_quoted_response_structure(self): - """Test get_quoted response structure validation.""" + def test_search_all_response_structure(self): + """Test search_all response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -839,10 +845,10 @@ def test_get_quoted_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["query"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_quoted") + method = getattr(self.posts_client, "search_all") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -854,8 +860,8 @@ def test_get_quoted_response_structure(self): ) - def test_get_insights_historical_request_structure(self): - """Test get_insights_historical request structure.""" + def test_get_reposts_request_structure(self): + """Test get_reposts request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -868,15 +874,11 @@ def test_get_insights_historical_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["tweet_ids"] = ["test_item"] - kwargs["end_time"] = "test_end_time" - kwargs["start_time"] = "test_start_time" - kwargs["granularity"] = "test_granularity" - kwargs["requested_metrics"] = ["test_item"] + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_insights_historical") + method = getattr(self.posts_client, "get_reposts") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -911,7 +913,7 @@ def test_get_insights_historical_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/insights/historical" + expected_path = "/2/tweets/{id}/retweets" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -927,12 +929,12 @@ def test_get_insights_historical_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_insights_historical: {e}") + pytest.fail(f"Contract test failed for get_reposts: {e}") - def test_get_insights_historical_required_parameters(self): - """Test that get_insights_historical handles parameters correctly.""" - method = getattr(self.posts_client, "get_insights_historical") + def test_get_reposts_required_parameters(self): + """Test that get_reposts handles parameters correctly.""" + method = getattr(self.posts_client, "get_reposts") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -946,8 +948,8 @@ def test_get_insights_historical_required_parameters(self): method() - def test_get_insights_historical_response_structure(self): - """Test get_insights_historical response structure validation.""" + def test_get_reposts_response_structure(self): + """Test get_reposts response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -960,14 +962,10 @@ def test_get_insights_historical_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["tweet_ids"] = ["test"] - kwargs["end_time"] = "test_value" - kwargs["start_time"] = "test_value" - kwargs["granularity"] = "test_value" - kwargs["requested_metrics"] = ["test"] + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_insights_historical") + method = getattr(self.posts_client, "get_reposts") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -979,8 +977,8 @@ def test_get_insights_historical_response_structure(self): ) - def test_get_analytics_request_structure(self): - """Test get_analytics request structure.""" + def test_get_by_id_request_structure(self): + """Test get_by_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -993,14 +991,11 @@ def test_get_analytics_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["ids"] = ["test_item"] - kwargs["end_time"] = "test_end_time" - kwargs["start_time"] = "test_start_time" - kwargs["granularity"] = "test_granularity" + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_analytics") + method = getattr(self.posts_client, "get_by_id") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1035,7 +1030,7 @@ def test_get_analytics_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/analytics" + expected_path = "/2/tweets/{id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1051,12 +1046,12 @@ def test_get_analytics_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_analytics: {e}") + pytest.fail(f"Contract test failed for get_by_id: {e}") - def test_get_analytics_required_parameters(self): - """Test that get_analytics handles parameters correctly.""" - method = getattr(self.posts_client, "get_analytics") + def test_get_by_id_required_parameters(self): + """Test that get_by_id handles parameters correctly.""" + method = getattr(self.posts_client, "get_by_id") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1070,8 +1065,8 @@ def test_get_analytics_required_parameters(self): method() - def test_get_analytics_response_structure(self): - """Test get_analytics response structure validation.""" + def test_get_by_id_response_structure(self): + """Test get_by_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1084,13 +1079,10 @@ def test_get_analytics_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["ids"] = ["test"] - kwargs["end_time"] = "test_value" - kwargs["start_time"] = "test_value" - kwargs["granularity"] = "test_value" + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_analytics") + method = getattr(self.posts_client, "get_by_id") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1102,8 +1094,8 @@ def test_get_analytics_response_structure(self): ) - def test_get_reposted_by_request_structure(self): - """Test get_reposted_by request structure.""" + def test_delete_request_structure(self): + """Test delete request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1112,7 +1104,7 @@ def test_get_reposted_by_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters @@ -1120,7 +1112,7 @@ def test_get_reposted_by_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_reposted_by") + method = getattr(self.posts_client, "delete") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1139,7 +1131,7 @@ def test_get_reposted_by_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1148,14 +1140,14 @@ def test_get_reposted_by_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/{id}/retweeted_by" + expected_path = "/2/tweets/{id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1171,12 +1163,12 @@ def test_get_reposted_by_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_reposted_by: {e}") + pytest.fail(f"Contract test failed for delete: {e}") - def test_get_reposted_by_required_parameters(self): - """Test that get_reposted_by handles parameters correctly.""" - method = getattr(self.posts_client, "get_reposted_by") + def test_delete_required_parameters(self): + """Test that delete handles parameters correctly.""" + method = getattr(self.posts_client, "delete") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1184,14 +1176,14 @@ def test_get_reposted_by_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_reposted_by_response_structure(self): - """Test get_reposted_by response structure validation.""" + def test_delete_response_structure(self): + """Test delete response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1201,13 +1193,13 @@ def test_get_reposted_by_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_reposted_by") + method = getattr(self.posts_client, "delete") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1340,8 +1332,8 @@ def test_get_insights28hr_response_structure(self): ) - def test_get_reposts_request_structure(self): - """Test get_reposts request structure.""" + def test_get_by_ids_request_structure(self): + """Test get_by_ids request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1354,11 +1346,11 @@ def test_get_reposts_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["ids"] = ["test_item"] # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_reposts") + method = getattr(self.posts_client, "get_by_ids") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1393,7 +1385,7 @@ def test_get_reposts_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/{id}/retweets" + expected_path = "/2/tweets" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1409,12 +1401,12 @@ def test_get_reposts_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_reposts: {e}") + pytest.fail(f"Contract test failed for get_by_ids: {e}") - def test_get_reposts_required_parameters(self): - """Test that get_reposts handles parameters correctly.""" - method = getattr(self.posts_client, "get_reposts") + def test_get_by_ids_required_parameters(self): + """Test that get_by_ids handles parameters correctly.""" + method = getattr(self.posts_client, "get_by_ids") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1428,8 +1420,8 @@ def test_get_reposts_required_parameters(self): method() - def test_get_reposts_response_structure(self): - """Test get_reposts response structure validation.""" + def test_get_by_ids_response_structure(self): + """Test get_by_ids response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1442,10 +1434,10 @@ def test_get_reposts_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["ids"] = ["test"] # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_reposts") + method = getattr(self.posts_client, "get_by_ids") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1457,25 +1449,28 @@ def test_get_reposts_response_structure(self): ) - def test_search_recent_request_structure(self): - """Test search_recent request structure.""" + def test_create_request_structure(self): + """Test create request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() - mock_response.status_code = 200 + mock_response.status_code = 201 mock_response.json.return_value = { "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["query"] = "test_query" # Add request body if required + # Import and create proper request model instance + from xdk.posts.models import CreateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateRequest() # Call the method try: - method = getattr(self.posts_client, "search_recent") + method = getattr(self.posts_client, "create") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1484,7 +1479,7 @@ def test_search_recent_request_structure(self): # For streaming operations, we need to set up the mock to handle streaming # Mock the streaming response mock_streaming_response = Mock() - mock_streaming_response.status_code = 200 + mock_streaming_response.status_code = 201 mock_streaming_response.raise_for_status.return_value = None mock_streaming_response.__enter__ = Mock( return_value=mock_streaming_response @@ -1494,7 +1489,7 @@ def test_search_recent_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1503,14 +1498,14 @@ def test_search_recent_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/search/recent" + expected_path = "/2/tweets" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1526,12 +1521,12 @@ def test_search_recent_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for search_recent: {e}") + pytest.fail(f"Contract test failed for create: {e}") - def test_search_recent_required_parameters(self): - """Test that search_recent handles parameters correctly.""" - method = getattr(self.posts_client, "search_recent") + def test_create_required_parameters(self): + """Test that create handles parameters correctly.""" + method = getattr(self.posts_client, "create") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1539,30 +1534,33 @@ def test_search_recent_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_search_recent_response_structure(self): - """Test search_recent response structure validation.""" + def test_create_response_structure(self): + """Test create response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { "data": None, } mock_response = Mock() - mock_response.status_code = 200 + mock_response.status_code = 201 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["query"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.posts.models import CreateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateRequest() # Call method and verify response structure - method = getattr(self.posts_client, "search_recent") + method = getattr(self.posts_client, "create") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1574,8 +1572,8 @@ def test_search_recent_response_structure(self): ) - def test_get_by_ids_request_structure(self): - """Test get_by_ids request structure.""" + def test_get_reposted_by_request_structure(self): + """Test get_reposted_by request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1588,11 +1586,11 @@ def test_get_by_ids_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["ids"] = ["test_item"] + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_by_ids") + method = getattr(self.posts_client, "get_reposted_by") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1627,7 +1625,7 @@ def test_get_by_ids_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets" + expected_path = "/2/tweets/{id}/retweeted_by" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1643,12 +1641,12 @@ def test_get_by_ids_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_ids: {e}") + pytest.fail(f"Contract test failed for get_reposted_by: {e}") - def test_get_by_ids_required_parameters(self): - """Test that get_by_ids handles parameters correctly.""" - method = getattr(self.posts_client, "get_by_ids") + def test_get_reposted_by_required_parameters(self): + """Test that get_reposted_by handles parameters correctly.""" + method = getattr(self.posts_client, "get_reposted_by") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1662,8 +1660,8 @@ def test_get_by_ids_required_parameters(self): method() - def test_get_by_ids_response_structure(self): - """Test get_by_ids response structure validation.""" + def test_get_reposted_by_response_structure(self): + """Test get_reposted_by response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1676,10 +1674,10 @@ def test_get_by_ids_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["ids"] = ["test"] + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_by_ids") + method = getattr(self.posts_client, "get_reposted_by") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1691,8 +1689,8 @@ def test_get_by_ids_response_structure(self): ) - def test_create_request_structure(self): - """Test create request structure.""" + def test_search_recent_request_structure(self): + """Test search_recent request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1701,18 +1699,15 @@ def test_create_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["query"] = "test_query" # Add request body if required - # Import and create proper request model instance - from xdk.posts.models import CreateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateRequest() # Call the method try: - method = getattr(self.posts_client, "create") + method = getattr(self.posts_client, "search_recent") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1731,7 +1726,7 @@ def test_create_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1740,14 +1735,14 @@ def test_create_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets" + expected_path = "/2/tweets/search/recent" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1763,12 +1758,12 @@ def test_create_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create: {e}") + pytest.fail(f"Contract test failed for search_recent: {e}") - def test_create_required_parameters(self): - """Test that create handles parameters correctly.""" - method = getattr(self.posts_client, "create") + def test_search_recent_required_parameters(self): + """Test that search_recent handles parameters correctly.""" + method = getattr(self.posts_client, "search_recent") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1776,14 +1771,14 @@ def test_create_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_response_structure(self): - """Test create response structure validation.""" + def test_search_recent_response_structure(self): + """Test search_recent response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1793,16 +1788,13 @@ def test_create_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["query"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.posts.models import CreateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateRequest() # Call method and verify response structure - method = getattr(self.posts_client, "create") + method = getattr(self.posts_client, "search_recent") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1814,8 +1806,8 @@ def test_create_response_structure(self): ) - def test_get_counts_all_request_structure(self): - """Test get_counts_all request structure.""" + def test_get_insights_historical_request_structure(self): + """Test get_insights_historical request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1828,11 +1820,15 @@ def test_get_counts_all_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["query"] = "test_query" + kwargs["tweet_ids"] = ["test_item"] + kwargs["end_time"] = "test_end_time" + kwargs["start_time"] = "test_start_time" + kwargs["granularity"] = "test_granularity" + kwargs["requested_metrics"] = ["test_item"] # Add request body if required # Call the method try: - method = getattr(self.posts_client, "get_counts_all") + method = getattr(self.posts_client, "get_insights_historical") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1867,7 +1863,7 @@ def test_get_counts_all_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/counts/all" + expected_path = "/2/insights/historical" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1883,12 +1879,12 @@ def test_get_counts_all_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_counts_all: {e}") + pytest.fail(f"Contract test failed for get_insights_historical: {e}") - def test_get_counts_all_required_parameters(self): - """Test that get_counts_all handles parameters correctly.""" - method = getattr(self.posts_client, "get_counts_all") + def test_get_insights_historical_required_parameters(self): + """Test that get_insights_historical handles parameters correctly.""" + method = getattr(self.posts_client, "get_insights_historical") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1902,8 +1898,8 @@ def test_get_counts_all_required_parameters(self): method() - def test_get_counts_all_response_structure(self): - """Test get_counts_all response structure validation.""" + def test_get_insights_historical_response_structure(self): + """Test get_insights_historical response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1916,10 +1912,14 @@ def test_get_counts_all_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["query"] = "test_value" + kwargs["tweet_ids"] = ["test"] + kwargs["end_time"] = "test_value" + kwargs["start_time"] = "test_value" + kwargs["granularity"] = "test_value" + kwargs["requested_metrics"] = ["test"] # Add request body if required # Call method and verify response structure - method = getattr(self.posts_client, "get_counts_all") + method = getattr(self.posts_client, "get_insights_historical") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/posts/test_pagination.py b/tests/posts/test_pagination.py index 055c958..b373821 100644 --- a/tests/posts/test_pagination.py +++ b/tests/posts/test_pagination.py @@ -27,20 +27,20 @@ def setup_class(self): self.posts_client = getattr(self.client, "posts") - def test_search_all_cursor_creation(self): - """Test that search_all can be used with Cursor.""" - method = getattr(self.posts_client, "search_all") + def test_get_liking_users_cursor_creation(self): + """Test that get_liking_users can be used with Cursor.""" + method = getattr(self.posts_client, "get_liking_users") # Should be able to create cursor without error try: - test_cursor = cursor(method, "test_query", max_results=10) + test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method search_all should support pagination") + pytest.fail(f"Method get_liking_users should support pagination") - def test_search_all_cursor_pages(self): - """Test pagination with pages() for search_all.""" + def test_get_liking_users_cursor_pages(self): + """Test pagination with pages() for get_liking_users.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -61,8 +61,8 @@ def test_search_all_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.posts_client, "search_all") - test_cursor = cursor(method, "test_query", max_results=2) + method = getattr(self.posts_client, "get_liking_users") + test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" # Verify first page @@ -77,8 +77,8 @@ def test_search_all_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_search_all_cursor_items(self): - """Test pagination with items() for search_all.""" + def test_get_liking_users_cursor_items(self): + """Test pagination with items() for get_liking_users.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -97,8 +97,8 @@ def test_search_all_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.posts_client, "search_all") - test_cursor = cursor(method, "test_query", max_results=10) + method = getattr(self.posts_client, "get_liking_users") + test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" # Verify items have expected structure @@ -108,17 +108,17 @@ def test_search_all_cursor_items(self): ), "Items should have 'id' field" - def test_search_all_pagination_parameters(self): - """Test that pagination parameters are handled correctly for search_all.""" + def test_get_liking_users_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_liking_users.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.posts_client, "search_all") + method = getattr(self.posts_client, "get_liking_users") # Test with max_results parameter - test_cursor = cursor(method, "test_query", max_results=5) + test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request # Verify max_results was passed in request call_args = mock_session.get.call_args @@ -147,7 +147,7 @@ def test_search_all_pagination_parameters(self): mock_response_with_token, second_page_response, ] - test_cursor = cursor(method, "test_query", max_results=1) + test_cursor = cursor(method, "test_value", max_results=1) pages = list(test_cursor.pages(2)) # Should have made 2 requests assert ( @@ -169,20 +169,20 @@ def test_search_all_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_liking_users_cursor_creation(self): - """Test that get_liking_users can be used with Cursor.""" - method = getattr(self.posts_client, "get_liking_users") + def test_get_quoted_cursor_creation(self): + """Test that get_quoted can be used with Cursor.""" + method = getattr(self.posts_client, "get_quoted") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_liking_users should support pagination") + pytest.fail(f"Method get_quoted should support pagination") - def test_get_liking_users_cursor_pages(self): - """Test pagination with pages() for get_liking_users.""" + def test_get_quoted_cursor_pages(self): + """Test pagination with pages() for get_quoted.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -203,7 +203,7 @@ def test_get_liking_users_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.posts_client, "get_liking_users") + method = getattr(self.posts_client, "get_quoted") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -219,8 +219,8 @@ def test_get_liking_users_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_liking_users_cursor_items(self): - """Test pagination with items() for get_liking_users.""" + def test_get_quoted_cursor_items(self): + """Test pagination with items() for get_quoted.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -239,7 +239,7 @@ def test_get_liking_users_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.posts_client, "get_liking_users") + method = getattr(self.posts_client, "get_quoted") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -250,15 +250,15 @@ def test_get_liking_users_cursor_items(self): ), "Items should have 'id' field" - def test_get_liking_users_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_liking_users.""" + def test_get_quoted_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_quoted.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.posts_client, "get_liking_users") + method = getattr(self.posts_client, "get_quoted") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -311,20 +311,20 @@ def test_get_liking_users_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_quoted_cursor_creation(self): - """Test that get_quoted can be used with Cursor.""" - method = getattr(self.posts_client, "get_quoted") + def test_search_all_cursor_creation(self): + """Test that search_all can be used with Cursor.""" + method = getattr(self.posts_client, "search_all") # Should be able to create cursor without error try: - test_cursor = cursor(method, "test_value", max_results=10) + test_cursor = cursor(method, "test_query", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_quoted should support pagination") + pytest.fail(f"Method search_all should support pagination") - def test_get_quoted_cursor_pages(self): - """Test pagination with pages() for get_quoted.""" + def test_search_all_cursor_pages(self): + """Test pagination with pages() for search_all.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -345,8 +345,8 @@ def test_get_quoted_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.posts_client, "get_quoted") - test_cursor = cursor(method, "test_value", max_results=2) + method = getattr(self.posts_client, "search_all") + test_cursor = cursor(method, "test_query", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" # Verify first page @@ -361,8 +361,8 @@ def test_get_quoted_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_quoted_cursor_items(self): - """Test pagination with items() for get_quoted.""" + def test_search_all_cursor_items(self): + """Test pagination with items() for search_all.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -381,8 +381,8 @@ def test_get_quoted_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.posts_client, "get_quoted") - test_cursor = cursor(method, "test_value", max_results=10) + method = getattr(self.posts_client, "search_all") + test_cursor = cursor(method, "test_query", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" # Verify items have expected structure @@ -392,17 +392,17 @@ def test_get_quoted_cursor_items(self): ), "Items should have 'id' field" - def test_get_quoted_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_quoted.""" + def test_search_all_pagination_parameters(self): + """Test that pagination parameters are handled correctly for search_all.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.posts_client, "get_quoted") + method = getattr(self.posts_client, "search_all") # Test with max_results parameter - test_cursor = cursor(method, "test_value", max_results=5) + test_cursor = cursor(method, "test_query", max_results=5) list(test_cursor.pages(1)) # Trigger one request # Verify max_results was passed in request call_args = mock_session.get.call_args @@ -431,7 +431,7 @@ def test_get_quoted_pagination_parameters(self): mock_response_with_token, second_page_response, ] - test_cursor = cursor(method, "test_value", max_results=1) + test_cursor = cursor(method, "test_query", max_results=1) pages = list(test_cursor.pages(2)) # Should have made 2 requests assert ( @@ -453,20 +453,20 @@ def test_get_quoted_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_reposted_by_cursor_creation(self): - """Test that get_reposted_by can be used with Cursor.""" - method = getattr(self.posts_client, "get_reposted_by") + def test_get_reposts_cursor_creation(self): + """Test that get_reposts can be used with Cursor.""" + method = getattr(self.posts_client, "get_reposts") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_reposted_by should support pagination") + pytest.fail(f"Method get_reposts should support pagination") - def test_get_reposted_by_cursor_pages(self): - """Test pagination with pages() for get_reposted_by.""" + def test_get_reposts_cursor_pages(self): + """Test pagination with pages() for get_reposts.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -487,7 +487,7 @@ def test_get_reposted_by_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.posts_client, "get_reposted_by") + method = getattr(self.posts_client, "get_reposts") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -503,8 +503,8 @@ def test_get_reposted_by_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_reposted_by_cursor_items(self): - """Test pagination with items() for get_reposted_by.""" + def test_get_reposts_cursor_items(self): + """Test pagination with items() for get_reposts.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -523,7 +523,7 @@ def test_get_reposted_by_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.posts_client, "get_reposted_by") + method = getattr(self.posts_client, "get_reposts") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -534,15 +534,15 @@ def test_get_reposted_by_cursor_items(self): ), "Items should have 'id' field" - def test_get_reposted_by_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_reposted_by.""" + def test_get_reposts_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_reposts.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.posts_client, "get_reposted_by") + method = getattr(self.posts_client, "get_reposts") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -595,20 +595,20 @@ def test_get_reposted_by_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_reposts_cursor_creation(self): - """Test that get_reposts can be used with Cursor.""" - method = getattr(self.posts_client, "get_reposts") + def test_get_reposted_by_cursor_creation(self): + """Test that get_reposted_by can be used with Cursor.""" + method = getattr(self.posts_client, "get_reposted_by") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_reposts should support pagination") + pytest.fail(f"Method get_reposted_by should support pagination") - def test_get_reposts_cursor_pages(self): - """Test pagination with pages() for get_reposts.""" + def test_get_reposted_by_cursor_pages(self): + """Test pagination with pages() for get_reposted_by.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -629,7 +629,7 @@ def test_get_reposts_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.posts_client, "get_reposts") + method = getattr(self.posts_client, "get_reposted_by") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -645,8 +645,8 @@ def test_get_reposts_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_reposts_cursor_items(self): - """Test pagination with items() for get_reposts.""" + def test_get_reposted_by_cursor_items(self): + """Test pagination with items() for get_reposted_by.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -665,7 +665,7 @@ def test_get_reposts_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.posts_client, "get_reposts") + method = getattr(self.posts_client, "get_reposted_by") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -676,15 +676,15 @@ def test_get_reposts_cursor_items(self): ), "Items should have 'id' field" - def test_get_reposts_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_reposts.""" + def test_get_reposted_by_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_reposted_by.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.posts_client, "get_reposts") + method = getattr(self.posts_client, "get_reposted_by") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -889,8 +889,8 @@ def test_pagination_edge_cases(self): empty_response.raise_for_status.return_value = None mock_session.get.return_value = empty_response # Pick first paginatable method for testing - method = getattr(self.posts_client, "search_all") - test_cursor = cursor(method, "test_query", max_results=10) + method = getattr(self.posts_client, "get_liking_users") + test_cursor = cursor(method, "test_value", max_results=10) # Should handle empty responses gracefully pages = list(test_cursor.pages(1)) assert len(pages) == 1, "Should get one page even if empty" diff --git a/tests/posts/test_structure.py b/tests/posts/test_structure.py index b8914e4..eabce91 100644 --- a/tests/posts/test_structure.py +++ b/tests/posts/test_structure.py @@ -28,45 +28,40 @@ def setup_class(self): self.posts_client = getattr(self.client, "posts") - def test_search_all_exists(self): - """Test that search_all method exists with correct signature.""" + def test_get_liking_users_exists(self): + """Test that get_liking_users method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "search_all", None) - assert method is not None, f"Method search_all does not exist on PostsClient" + method = getattr(PostsClient, "get_liking_users", None) + assert ( + method is not None + ), f"Method get_liking_users does not exist on PostsClient" # Check method is callable - assert callable(method), f"search_all is not callable" + assert callable(method), f"get_liking_users is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"search_all should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_liking_users should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "query", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from search_all" + ), f"Required parameter '{required_param}' missing from get_liking_users" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "start_time", - "end_time", - "since_id", - "until_id", "max_results", - "next_token", "pagination_token", - "sort_order", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", "user.fields", - "place.fields", + "expansions", + "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -76,19 +71,19 @@ def test_search_all_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_search_all_return_annotation(self): - """Test that search_all has proper return type annotation.""" - method = getattr(PostsClient, "search_all") + def test_get_liking_users_return_annotation(self): + """Test that get_liking_users has proper return type annotation.""" + method = getattr(PostsClient, "get_liking_users") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method search_all should have return type annotation" + ), f"Method get_liking_users should have return type annotation" - def test_search_all_pagination_params(self): - """Test that search_all has pagination parameters.""" - method = getattr(PostsClient, "search_all") + def test_get_liking_users_pagination_params(self): + """Test that get_liking_users has pagination parameters.""" + method = getattr(PostsClient, "get_liking_users") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -102,21 +97,21 @@ def test_search_all_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method search_all should have pagination parameters" + ), f"Paginated method get_liking_users should have pagination parameters" - def test_get_by_id_exists(self): - """Test that get_by_id method exists with correct signature.""" + def test_get_quoted_exists(self): + """Test that get_quoted method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_by_id", None) - assert method is not None, f"Method get_by_id does not exist on PostsClient" + method = getattr(PostsClient, "get_quoted", None) + assert method is not None, f"Method get_quoted does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_by_id is not callable" + assert callable(method), f"get_quoted is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_by_id should have at least 'self' parameter" + assert len(params) >= 1, f"get_quoted should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -127,9 +122,12 @@ def test_get_by_id_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_id" + ), f"Required parameter '{required_param}' missing from get_quoted" # Check optional parameters have defaults (excluding 'self') optional_params = [ + "max_results", + "pagination_token", + "exclude", "tweet.fields", "expansions", "media.fields", @@ -145,57 +143,33 @@ def test_get_by_id_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_id_return_annotation(self): - """Test that get_by_id has proper return type annotation.""" - method = getattr(PostsClient, "get_by_id") + def test_get_quoted_return_annotation(self): + """Test that get_quoted has proper return type annotation.""" + method = getattr(PostsClient, "get_quoted") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_id should have return type annotation" + ), f"Method get_quoted should have return type annotation" - def test_delete_exists(self): - """Test that delete method exists with correct signature.""" - # Check method exists - method = getattr(PostsClient, "delete", None) - assert method is not None, f"Method delete does not exist on PostsClient" - # Check method is callable - assert callable(method), f"delete is not callable" - # Check method signature + def test_get_quoted_pagination_params(self): + """Test that get_quoted has pagination parameters.""" + method = getattr(PostsClient, "get_quoted") sig = inspect.signature(method) params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"delete should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [ - "id", + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", ] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from delete" - # Check optional parameters have defaults (excluding 'self') - optional_params = [] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_delete_return_annotation(self): - """Test that delete has proper return type annotation.""" - method = getattr(PostsClient, "delete") - sig = inspect.signature(method) - # Check return annotation exists + has_pagination_param = any(param in params for param in pagination_params) assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method delete should have return type annotation" + has_pagination_param + ), f"Paginated method get_quoted should have pagination parameters" def test_get_counts_recent_exists(self): @@ -297,40 +271,41 @@ def test_hide_reply_return_annotation(self): ), f"Method hide_reply should have return type annotation" - def test_get_liking_users_exists(self): - """Test that get_liking_users method exists with correct signature.""" + def test_get_counts_all_exists(self): + """Test that get_counts_all method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_liking_users", None) + method = getattr(PostsClient, "get_counts_all", None) assert ( method is not None - ), f"Method get_liking_users does not exist on PostsClient" + ), f"Method get_counts_all does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_liking_users is not callable" + assert callable(method), f"get_counts_all is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_liking_users should have at least 'self' parameter" + assert len(params) >= 1, f"get_counts_all should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "query", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_liking_users" + ), f"Required parameter '{required_param}' missing from get_counts_all" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "max_results", + "start_time", + "end_time", + "since_id", + "until_id", + "next_token", "pagination_token", - "user.fields", - "expansions", - "tweet.fields", + "granularity", + "search_count.fields", ] for optional_param in optional_params: if optional_param in params: @@ -340,63 +315,97 @@ def test_get_liking_users_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_liking_users_return_annotation(self): - """Test that get_liking_users has proper return type annotation.""" - method = getattr(PostsClient, "get_liking_users") + def test_get_counts_all_return_annotation(self): + """Test that get_counts_all has proper return type annotation.""" + method = getattr(PostsClient, "get_counts_all") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_liking_users should have return type annotation" + ), f"Method get_counts_all should have return type annotation" - def test_get_liking_users_pagination_params(self): - """Test that get_liking_users has pagination parameters.""" - method = getattr(PostsClient, "get_liking_users") + def test_get_analytics_exists(self): + """Test that get_analytics method exists with correct signature.""" + # Check method exists + method = getattr(PostsClient, "get_analytics", None) + assert method is not None, f"Method get_analytics does not exist on PostsClient" + # Check method is callable + assert callable(method), f"get_analytics is not callable" + # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", + # Should have 'self' as first parameter + assert len(params) >= 1, f"get_analytics should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [ + "ids", + "end_time", + "start_time", + "granularity", ] - has_pagination_param = any(param in params for param in pagination_params) + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from get_analytics" + # Check optional parameters have defaults (excluding 'self') + optional_params = [ + "analytics.fields", + ] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_get_analytics_return_annotation(self): + """Test that get_analytics has proper return type annotation.""" + method = getattr(PostsClient, "get_analytics") + sig = inspect.signature(method) + # Check return annotation exists assert ( - has_pagination_param - ), f"Paginated method get_liking_users should have pagination parameters" + sig.return_annotation is not inspect.Signature.empty + ), f"Method get_analytics should have return type annotation" - def test_get_quoted_exists(self): - """Test that get_quoted method exists with correct signature.""" + def test_search_all_exists(self): + """Test that search_all method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_quoted", None) - assert method is not None, f"Method get_quoted does not exist on PostsClient" + method = getattr(PostsClient, "search_all", None) + assert method is not None, f"Method search_all does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_quoted is not callable" + assert callable(method), f"search_all is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_quoted should have at least 'self' parameter" + assert len(params) >= 1, f"search_all should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "query", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_quoted" + ), f"Required parameter '{required_param}' missing from search_all" # Check optional parameters have defaults (excluding 'self') optional_params = [ + "start_time", + "end_time", + "since_id", + "until_id", "max_results", + "next_token", "pagination_token", - "exclude", + "sort_order", "tweet.fields", "expansions", "media.fields", @@ -412,19 +421,19 @@ def test_get_quoted_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_quoted_return_annotation(self): - """Test that get_quoted has proper return type annotation.""" - method = getattr(PostsClient, "get_quoted") + def test_search_all_return_annotation(self): + """Test that search_all has proper return type annotation.""" + method = getattr(PostsClient, "search_all") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_quoted should have return type annotation" + ), f"Method search_all should have return type annotation" - def test_get_quoted_pagination_params(self): - """Test that get_quoted has pagination parameters.""" - method = getattr(PostsClient, "get_quoted") + def test_search_all_pagination_params(self): + """Test that search_all has pagination parameters.""" + method = getattr(PostsClient, "search_all") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -438,43 +447,42 @@ def test_get_quoted_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_quoted should have pagination parameters" + ), f"Paginated method search_all should have pagination parameters" - def test_get_insights_historical_exists(self): - """Test that get_insights_historical method exists with correct signature.""" + def test_get_reposts_exists(self): + """Test that get_reposts method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_insights_historical", None) - assert ( - method is not None - ), f"Method get_insights_historical does not exist on PostsClient" + method = getattr(PostsClient, "get_reposts", None) + assert method is not None, f"Method get_reposts does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_insights_historical is not callable" + assert callable(method), f"get_reposts is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_insights_historical should have at least 'self' parameter" + assert len(params) >= 1, f"get_reposts should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "tweet_ids", - "end_time", - "start_time", - "granularity", - "requested_metrics", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_insights_historical" + ), f"Required parameter '{required_param}' missing from get_reposts" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "engagement.fields", + "max_results", + "pagination_token", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -484,45 +492,66 @@ def test_get_insights_historical_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_insights_historical_return_annotation(self): - """Test that get_insights_historical has proper return type annotation.""" - method = getattr(PostsClient, "get_insights_historical") + def test_get_reposts_return_annotation(self): + """Test that get_reposts has proper return type annotation.""" + method = getattr(PostsClient, "get_reposts") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_insights_historical should have return type annotation" + ), f"Method get_reposts should have return type annotation" - def test_get_analytics_exists(self): - """Test that get_analytics method exists with correct signature.""" + def test_get_reposts_pagination_params(self): + """Test that get_reposts has pagination parameters.""" + method = getattr(PostsClient, "get_reposts") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_reposts should have pagination parameters" + + + def test_get_by_id_exists(self): + """Test that get_by_id method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_analytics", None) - assert method is not None, f"Method get_analytics does not exist on PostsClient" + method = getattr(PostsClient, "get_by_id", None) + assert method is not None, f"Method get_by_id does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_analytics is not callable" + assert callable(method), f"get_by_id is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_analytics should have at least 'self' parameter" + assert len(params) >= 1, f"get_by_id should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "ids", - "end_time", - "start_time", - "granularity", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_analytics" + ), f"Required parameter '{required_param}' missing from get_by_id" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "analytics.fields", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -532,32 +561,28 @@ def test_get_analytics_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_analytics_return_annotation(self): - """Test that get_analytics has proper return type annotation.""" - method = getattr(PostsClient, "get_analytics") + def test_get_by_id_return_annotation(self): + """Test that get_by_id has proper return type annotation.""" + method = getattr(PostsClient, "get_by_id") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_analytics should have return type annotation" + ), f"Method get_by_id should have return type annotation" - def test_get_reposted_by_exists(self): - """Test that get_reposted_by method exists with correct signature.""" + def test_delete_exists(self): + """Test that delete method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_reposted_by", None) - assert ( - method is not None - ), f"Method get_reposted_by does not exist on PostsClient" + method = getattr(PostsClient, "delete", None) + assert method is not None, f"Method delete does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_reposted_by is not callable" + assert callable(method), f"delete is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_reposted_by should have at least 'self' parameter" + assert len(params) >= 1, f"delete should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -568,15 +593,9 @@ def test_get_reposted_by_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_reposted_by" + ), f"Required parameter '{required_param}' missing from delete" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "max_results", - "pagination_token", - "user.fields", - "expansions", - "tweet.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -585,33 +604,14 @@ def test_get_reposted_by_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_reposted_by_return_annotation(self): - """Test that get_reposted_by has proper return type annotation.""" - method = getattr(PostsClient, "get_reposted_by") + def test_delete_return_annotation(self): + """Test that delete has proper return type annotation.""" + method = getattr(PostsClient, "delete") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_reposted_by should have return type annotation" - - - def test_get_reposted_by_pagination_params(self): - """Test that get_reposted_by has pagination parameters.""" - method = getattr(PostsClient, "get_reposted_by") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_reposted_by should have pagination parameters" + ), f"Method delete should have return type annotation" def test_get_insights28hr_exists(self): @@ -665,33 +665,31 @@ def test_get_insights28hr_return_annotation(self): ), f"Method get_insights28hr should have return type annotation" - def test_get_reposts_exists(self): - """Test that get_reposts method exists with correct signature.""" + def test_get_by_ids_exists(self): + """Test that get_by_ids method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_reposts", None) - assert method is not None, f"Method get_reposts does not exist on PostsClient" + method = getattr(PostsClient, "get_by_ids", None) + assert method is not None, f"Method get_by_ids does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_reposts is not callable" + assert callable(method), f"get_by_ids is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_reposts should have at least 'self' parameter" + assert len(params) >= 1, f"get_by_ids should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "ids", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_reposts" + ), f"Required parameter '{required_param}' missing from get_by_ids" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "max_results", - "pagination_token", "tweet.fields", "expansions", "media.fields", @@ -707,74 +705,91 @@ def test_get_reposts_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_reposts_return_annotation(self): - """Test that get_reposts has proper return type annotation.""" - method = getattr(PostsClient, "get_reposts") + def test_get_by_ids_return_annotation(self): + """Test that get_by_ids has proper return type annotation.""" + method = getattr(PostsClient, "get_by_ids") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_reposts should have return type annotation" + ), f"Method get_by_ids should have return type annotation" - def test_get_reposts_pagination_params(self): - """Test that get_reposts has pagination parameters.""" - method = getattr(PostsClient, "get_reposts") + def test_create_exists(self): + """Test that create method exists with correct signature.""" + # Check method exists + method = getattr(PostsClient, "create", None) + assert method is not None, f"Method create does not exist on PostsClient" + # Check method is callable + assert callable(method), f"create is not callable" + # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) + # Should have 'self' as first parameter + assert len(params) >= 1, f"create should have at least 'self' parameter" assert ( - has_pagination_param - ), f"Paginated method get_reposts should have pagination parameters" + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from create" + # Check optional parameters have defaults (excluding 'self') + optional_params = [] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" - def test_search_recent_exists(self): - """Test that search_recent method exists with correct signature.""" + def test_create_return_annotation(self): + """Test that create has proper return type annotation.""" + method = getattr(PostsClient, "create") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method create should have return type annotation" + + + def test_get_reposted_by_exists(self): + """Test that get_reposted_by method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "search_recent", None) - assert method is not None, f"Method search_recent does not exist on PostsClient" + method = getattr(PostsClient, "get_reposted_by", None) + assert ( + method is not None + ), f"Method get_reposted_by does not exist on PostsClient" # Check method is callable - assert callable(method), f"search_recent is not callable" + assert callable(method), f"get_reposted_by is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"search_recent should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_reposted_by should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "query", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from search_recent" + ), f"Required parameter '{required_param}' missing from get_reposted_by" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "start_time", - "end_time", - "since_id", - "until_id", "max_results", - "next_token", "pagination_token", - "sort_order", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", "user.fields", - "place.fields", + "expansions", + "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -784,19 +799,19 @@ def test_search_recent_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_search_recent_return_annotation(self): - """Test that search_recent has proper return type annotation.""" - method = getattr(PostsClient, "search_recent") + def test_get_reposted_by_return_annotation(self): + """Test that get_reposted_by has proper return type annotation.""" + method = getattr(PostsClient, "get_reposted_by") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method search_recent should have return type annotation" + ), f"Method get_reposted_by should have return type annotation" - def test_search_recent_pagination_params(self): - """Test that search_recent has pagination parameters.""" - method = getattr(PostsClient, "search_recent") + def test_get_reposted_by_pagination_params(self): + """Test that get_reposted_by has pagination parameters.""" + method = getattr(PostsClient, "get_reposted_by") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -810,34 +825,42 @@ def test_search_recent_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method search_recent should have pagination parameters" + ), f"Paginated method get_reposted_by should have pagination parameters" - def test_get_by_ids_exists(self): - """Test that get_by_ids method exists with correct signature.""" + def test_search_recent_exists(self): + """Test that search_recent method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_by_ids", None) - assert method is not None, f"Method get_by_ids does not exist on PostsClient" + method = getattr(PostsClient, "search_recent", None) + assert method is not None, f"Method search_recent does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_by_ids is not callable" + assert callable(method), f"search_recent is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_by_ids should have at least 'self' parameter" + assert len(params) >= 1, f"search_recent should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "ids", + "query", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_ids" + ), f"Required parameter '{required_param}' missing from search_recent" # Check optional parameters have defaults (excluding 'self') optional_params = [ + "start_time", + "end_time", + "since_id", + "until_id", + "max_results", + "next_token", + "pagination_token", + "sort_order", "tweet.fields", "expansions", "media.fields", @@ -853,92 +876,69 @@ def test_get_by_ids_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_ids_return_annotation(self): - """Test that get_by_ids has proper return type annotation.""" - method = getattr(PostsClient, "get_by_ids") + def test_search_recent_return_annotation(self): + """Test that search_recent has proper return type annotation.""" + method = getattr(PostsClient, "search_recent") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_ids should have return type annotation" + ), f"Method search_recent should have return type annotation" - def test_create_exists(self): - """Test that create method exists with correct signature.""" - # Check method exists - method = getattr(PostsClient, "create", None) - assert method is not None, f"Method create does not exist on PostsClient" - # Check method is callable - assert callable(method), f"create is not callable" - # Check method signature + def test_search_recent_pagination_params(self): + """Test that search_recent has pagination parameters.""" + method = getattr(PostsClient, "search_recent") sig = inspect.signature(method) params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"create should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from create" - # Check optional parameters have defaults (excluding 'self') - optional_params = [] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_create_return_annotation(self): - """Test that create has proper return type annotation.""" - method = getattr(PostsClient, "create") - sig = inspect.signature(method) - # Check return annotation exists + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method create should have return type annotation" + has_pagination_param + ), f"Paginated method search_recent should have pagination parameters" - def test_get_counts_all_exists(self): - """Test that get_counts_all method exists with correct signature.""" + def test_get_insights_historical_exists(self): + """Test that get_insights_historical method exists with correct signature.""" # Check method exists - method = getattr(PostsClient, "get_counts_all", None) + method = getattr(PostsClient, "get_insights_historical", None) assert ( method is not None - ), f"Method get_counts_all does not exist on PostsClient" + ), f"Method get_insights_historical does not exist on PostsClient" # Check method is callable - assert callable(method), f"get_counts_all is not callable" + assert callable(method), f"get_insights_historical is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_counts_all should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_insights_historical should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "query", + "tweet_ids", + "end_time", + "start_time", + "granularity", + "requested_metrics", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_counts_all" + ), f"Required parameter '{required_param}' missing from get_insights_historical" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "start_time", - "end_time", - "since_id", - "until_id", - "next_token", - "pagination_token", - "granularity", - "search_count.fields", + "engagement.fields", ] for optional_param in optional_params: if optional_param in params: @@ -948,35 +948,35 @@ def test_get_counts_all_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_counts_all_return_annotation(self): - """Test that get_counts_all has proper return type annotation.""" - method = getattr(PostsClient, "get_counts_all") + def test_get_insights_historical_return_annotation(self): + """Test that get_insights_historical has proper return type annotation.""" + method = getattr(PostsClient, "get_insights_historical") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_counts_all should have return type annotation" + ), f"Method get_insights_historical should have return type annotation" def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "search_all", - "get_by_id", - "delete", - "get_counts_recent", - "hide_reply", "get_liking_users", "get_quoted", - "get_insights_historical", + "get_counts_recent", + "hide_reply", + "get_counts_all", "get_analytics", - "get_reposted_by", - "get_insights28hr", + "search_all", "get_reposts", - "search_recent", + "get_by_id", + "delete", + "get_insights28hr", "get_by_ids", "create", - "get_counts_all", + "get_reposted_by", + "search_recent", + "get_insights_historical", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/spaces/test_contracts.py b/tests/spaces/test_contracts.py index 2bb156a..cc5547d 100644 --- a/tests/spaces/test_contracts.py +++ b/tests/spaces/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.spaces_client = getattr(self.client, "spaces") - def test_get_buyers_request_structure(self): - """Test get_buyers request structure.""" + def test_get_by_creator_ids_request_structure(self): + """Test get_by_creator_ids request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -41,11 +41,11 @@ def test_get_buyers_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_id" + kwargs["user_ids"] = ["test_item"] # Add request body if required # Call the method try: - method = getattr(self.spaces_client, "get_buyers") + method = getattr(self.spaces_client, "get_by_creator_ids") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -80,7 +80,7 @@ def test_get_buyers_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/spaces/{id}/buyers" + expected_path = "/2/spaces/by/creator_ids" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -96,12 +96,12 @@ def test_get_buyers_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_buyers: {e}") + pytest.fail(f"Contract test failed for get_by_creator_ids: {e}") - def test_get_buyers_required_parameters(self): - """Test that get_buyers handles parameters correctly.""" - method = getattr(self.spaces_client, "get_buyers") + def test_get_by_creator_ids_required_parameters(self): + """Test that get_by_creator_ids handles parameters correctly.""" + method = getattr(self.spaces_client, "get_by_creator_ids") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -115,8 +115,8 @@ def test_get_buyers_required_parameters(self): method() - def test_get_buyers_response_structure(self): - """Test get_buyers response structure validation.""" + def test_get_by_creator_ids_response_structure(self): + """Test get_by_creator_ids response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -129,10 +129,10 @@ def test_get_buyers_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test_value" + kwargs["user_ids"] = ["test"] # Add request body if required # Call method and verify response structure - method = getattr(self.spaces_client, "get_buyers") + method = getattr(self.spaces_client, "get_by_creator_ids") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -495,8 +495,8 @@ def test_get_posts_response_structure(self): ) - def test_get_by_creator_ids_request_structure(self): - """Test get_by_creator_ids request structure.""" + def test_get_by_id_request_structure(self): + """Test get_by_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -509,11 +509,11 @@ def test_get_by_creator_ids_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["user_ids"] = ["test_item"] + kwargs["id"] = "test_id" # Add request body if required # Call the method try: - method = getattr(self.spaces_client, "get_by_creator_ids") + method = getattr(self.spaces_client, "get_by_id") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -548,7 +548,7 @@ def test_get_by_creator_ids_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/spaces/by/creator_ids" + expected_path = "/2/spaces/{id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -564,12 +564,12 @@ def test_get_by_creator_ids_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_creator_ids: {e}") + pytest.fail(f"Contract test failed for get_by_id: {e}") - def test_get_by_creator_ids_required_parameters(self): - """Test that get_by_creator_ids handles parameters correctly.""" - method = getattr(self.spaces_client, "get_by_creator_ids") + def test_get_by_id_required_parameters(self): + """Test that get_by_id handles parameters correctly.""" + method = getattr(self.spaces_client, "get_by_id") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -583,8 +583,8 @@ def test_get_by_creator_ids_required_parameters(self): method() - def test_get_by_creator_ids_response_structure(self): - """Test get_by_creator_ids response structure validation.""" + def test_get_by_id_response_structure(self): + """Test get_by_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -597,10 +597,10 @@ def test_get_by_creator_ids_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["user_ids"] = ["test"] + kwargs["id"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.spaces_client, "get_by_creator_ids") + method = getattr(self.spaces_client, "get_by_id") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -612,8 +612,8 @@ def test_get_by_creator_ids_response_structure(self): ) - def test_get_by_id_request_structure(self): - """Test get_by_id request structure.""" + def test_get_buyers_request_structure(self): + """Test get_buyers request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -630,7 +630,7 @@ def test_get_by_id_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.spaces_client, "get_by_id") + method = getattr(self.spaces_client, "get_buyers") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -665,7 +665,7 @@ def test_get_by_id_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/spaces/{id}" + expected_path = "/2/spaces/{id}/buyers" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -681,12 +681,12 @@ def test_get_by_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_id: {e}") + pytest.fail(f"Contract test failed for get_buyers: {e}") - def test_get_by_id_required_parameters(self): - """Test that get_by_id handles parameters correctly.""" - method = getattr(self.spaces_client, "get_by_id") + def test_get_buyers_required_parameters(self): + """Test that get_buyers handles parameters correctly.""" + method = getattr(self.spaces_client, "get_buyers") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -700,8 +700,8 @@ def test_get_by_id_required_parameters(self): method() - def test_get_by_id_response_structure(self): - """Test get_by_id response structure validation.""" + def test_get_buyers_response_structure(self): + """Test get_buyers response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -717,7 +717,7 @@ def test_get_by_id_response_structure(self): kwargs["id"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.spaces_client, "get_by_id") + method = getattr(self.spaces_client, "get_buyers") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/spaces/test_structure.py b/tests/spaces/test_structure.py index 5e3fca5..6f2d91c 100644 --- a/tests/spaces/test_structure.py +++ b/tests/spaces/test_structure.py @@ -28,36 +28,39 @@ def setup_class(self): self.spaces_client = getattr(self.client, "spaces") - def test_get_buyers_exists(self): - """Test that get_buyers method exists with correct signature.""" + def test_get_by_creator_ids_exists(self): + """Test that get_by_creator_ids method exists with correct signature.""" # Check method exists - method = getattr(SpacesClient, "get_buyers", None) - assert method is not None, f"Method get_buyers does not exist on SpacesClient" + method = getattr(SpacesClient, "get_by_creator_ids", None) + assert ( + method is not None + ), f"Method get_by_creator_ids does not exist on SpacesClient" # Check method is callable - assert callable(method), f"get_buyers is not callable" + assert callable(method), f"get_by_creator_ids is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_buyers should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_by_creator_ids should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "user_ids", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_buyers" + ), f"Required parameter '{required_param}' missing from get_by_creator_ids" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "pagination_token", - "max_results", - "user.fields", + "space.fields", "expansions", - "tweet.fields", + "user.fields", + "topic.fields", ] for optional_param in optional_params: if optional_param in params: @@ -67,33 +70,14 @@ def test_get_buyers_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_buyers_return_annotation(self): - """Test that get_buyers has proper return type annotation.""" - method = getattr(SpacesClient, "get_buyers") + def test_get_by_creator_ids_return_annotation(self): + """Test that get_by_creator_ids has proper return type annotation.""" + method = getattr(SpacesClient, "get_by_creator_ids") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_buyers should have return type annotation" - - - def test_get_buyers_pagination_params(self): - """Test that get_buyers has pagination parameters.""" - method = getattr(SpacesClient, "get_buyers") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_buyers should have pagination parameters" + ), f"Method get_by_creator_ids should have return type annotation" def test_get_by_ids_exists(self): @@ -245,33 +229,29 @@ def test_get_posts_return_annotation(self): ), f"Method get_posts should have return type annotation" - def test_get_by_creator_ids_exists(self): - """Test that get_by_creator_ids method exists with correct signature.""" + def test_get_by_id_exists(self): + """Test that get_by_id method exists with correct signature.""" # Check method exists - method = getattr(SpacesClient, "get_by_creator_ids", None) - assert ( - method is not None - ), f"Method get_by_creator_ids does not exist on SpacesClient" + method = getattr(SpacesClient, "get_by_id", None) + assert method is not None, f"Method get_by_id does not exist on SpacesClient" # Check method is callable - assert callable(method), f"get_by_creator_ids is not callable" + assert callable(method), f"get_by_id is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_by_creator_ids should have at least 'self' parameter" + assert len(params) >= 1, f"get_by_id should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "user_ids", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_creator_ids" + ), f"Required parameter '{required_param}' missing from get_by_id" # Check optional parameters have defaults (excluding 'self') optional_params = [ "space.fields", @@ -287,28 +267,28 @@ def test_get_by_creator_ids_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_creator_ids_return_annotation(self): - """Test that get_by_creator_ids has proper return type annotation.""" - method = getattr(SpacesClient, "get_by_creator_ids") + def test_get_by_id_return_annotation(self): + """Test that get_by_id has proper return type annotation.""" + method = getattr(SpacesClient, "get_by_id") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_creator_ids should have return type annotation" + ), f"Method get_by_id should have return type annotation" - def test_get_by_id_exists(self): - """Test that get_by_id method exists with correct signature.""" + def test_get_buyers_exists(self): + """Test that get_buyers method exists with correct signature.""" # Check method exists - method = getattr(SpacesClient, "get_by_id", None) - assert method is not None, f"Method get_by_id does not exist on SpacesClient" + method = getattr(SpacesClient, "get_buyers", None) + assert method is not None, f"Method get_buyers does not exist on SpacesClient" # Check method is callable - assert callable(method), f"get_by_id is not callable" + assert callable(method), f"get_buyers is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_by_id should have at least 'self' parameter" + assert len(params) >= 1, f"get_buyers should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -319,13 +299,14 @@ def test_get_by_id_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_id" + ), f"Required parameter '{required_param}' missing from get_buyers" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "space.fields", - "expansions", + "pagination_token", + "max_results", "user.fields", - "topic.fields", + "expansions", + "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -335,25 +316,44 @@ def test_get_by_id_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_id_return_annotation(self): - """Test that get_by_id has proper return type annotation.""" - method = getattr(SpacesClient, "get_by_id") + def test_get_buyers_return_annotation(self): + """Test that get_buyers has proper return type annotation.""" + method = getattr(SpacesClient, "get_buyers") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_id should have return type annotation" + ), f"Method get_buyers should have return type annotation" + + + def test_get_buyers_pagination_params(self): + """Test that get_buyers has pagination parameters.""" + method = getattr(SpacesClient, "get_buyers") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_buyers should have pagination parameters" def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "get_buyers", + "get_by_creator_ids", "get_by_ids", "search", "get_posts", - "get_by_creator_ids", "get_by_id", + "get_buyers", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/stream/test_contracts.py b/tests/stream/test_contracts.py index c04ba75..dbdecac 100644 --- a/tests/stream/test_contracts.py +++ b/tests/stream/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.stream_client = getattr(self.client, "stream") - def test_labels_compliance_request_structure(self): - """Test labels_compliance request structure.""" + def test_likes_compliance_request_structure(self): + """Test likes_compliance request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -44,7 +44,7 @@ def test_labels_compliance_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "labels_compliance") + method = getattr(self.stream_client, "likes_compliance") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -79,7 +79,7 @@ def test_labels_compliance_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/label/stream" + expected_path = "/2/likes/compliance/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -95,12 +95,12 @@ def test_labels_compliance_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for labels_compliance: {e}") + pytest.fail(f"Contract test failed for likes_compliance: {e}") - def test_labels_compliance_required_parameters(self): - """Test that labels_compliance handles parameters correctly.""" - method = getattr(self.stream_client, "labels_compliance") + def test_likes_compliance_required_parameters(self): + """Test that likes_compliance handles parameters correctly.""" + method = getattr(self.stream_client, "likes_compliance") # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -114,8 +114,8 @@ def test_labels_compliance_required_parameters(self): pytest.fail(f"Method with no required params should be callable: {e}") - def test_labels_compliance_response_structure(self): - """Test labels_compliance response structure validation.""" + def test_likes_compliance_response_structure(self): + """Test likes_compliance response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -130,7 +130,7 @@ def test_labels_compliance_response_structure(self): kwargs = {} # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "labels_compliance") + method = getattr(self.stream_client, "likes_compliance") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -142,8 +142,8 @@ def test_labels_compliance_response_structure(self): ) - def test_posts_firehose_ja_request_structure(self): - """Test posts_firehose_ja request structure.""" + def test_get_rules_request_structure(self): + """Test get_rules request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -156,11 +156,10 @@ def test_posts_firehose_ja_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["partition"] = 42 # Add request body if required # Call the method try: - method = getattr(self.stream_client, "posts_firehose_ja") + method = getattr(self.stream_client, "get_rules") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -195,7 +194,7 @@ def test_posts_firehose_ja_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/firehose/stream/lang/ja" + expected_path = "/2/tweets/search/stream/rules" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -211,27 +210,27 @@ def test_posts_firehose_ja_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for posts_firehose_ja: {e}") + pytest.fail(f"Contract test failed for get_rules: {e}") - def test_posts_firehose_ja_required_parameters(self): - """Test that posts_firehose_ja handles parameters correctly.""" - method = getattr(self.stream_client, "posts_firehose_ja") - # Test with missing required parameters - mock the request to avoid network calls + def test_get_rules_required_parameters(self): + """Test that get_rules handles parameters correctly.""" + method = getattr(self.stream_client, "get_rules") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_posts_firehose_ja_response_structure(self): - """Test posts_firehose_ja response structure validation.""" + def test_get_rules_response_structure(self): + """Test get_rules response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -244,10 +243,9 @@ def test_posts_firehose_ja_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "posts_firehose_ja") + method = getattr(self.stream_client, "get_rules") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -259,8 +257,8 @@ def test_posts_firehose_ja_response_structure(self): ) - def test_likes_compliance_request_structure(self): - """Test likes_compliance request structure.""" + def test_update_rules_request_structure(self): + """Test update_rules request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -269,14 +267,18 @@ def test_likes_compliance_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters # Add request body if required + # Import and create proper request model instance + from xdk.stream.models import UpdateRulesRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = UpdateRulesRequest() # Call the method try: - method = getattr(self.stream_client, "likes_compliance") + method = getattr(self.stream_client, "update_rules") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -295,7 +297,7 @@ def test_likes_compliance_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -304,14 +306,14 @@ def test_likes_compliance_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/likes/compliance/stream" + expected_path = "/2/tweets/search/stream/rules" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -327,27 +329,27 @@ def test_likes_compliance_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for likes_compliance: {e}") + pytest.fail(f"Contract test failed for update_rules: {e}") - def test_likes_compliance_required_parameters(self): - """Test that likes_compliance handles parameters correctly.""" - method = getattr(self.stream_client, "likes_compliance") - # No required parameters, method should be callable without args + def test_update_rules_required_parameters(self): + """Test that update_rules handles parameters correctly.""" + method = getattr(self.stream_client, "update_rules") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response - try: + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_session.post.return_value = mock_response + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_likes_compliance_response_structure(self): - """Test likes_compliance response structure validation.""" + def test_update_rules_response_structure(self): + """Test update_rules response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -357,12 +359,16 @@ def test_likes_compliance_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} # Add request body if required + # Import and create proper request model instance + from xdk.stream.models import UpdateRulesRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = UpdateRulesRequest() # Call method and verify response structure - method = getattr(self.stream_client, "likes_compliance") + method = getattr(self.stream_client, "update_rules") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -374,8 +380,8 @@ def test_likes_compliance_response_structure(self): ) - def test_posts_firehose_request_structure(self): - """Test posts_firehose request structure.""" + def test_posts_firehose_en_request_structure(self): + """Test posts_firehose_en request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -392,7 +398,7 @@ def test_posts_firehose_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "posts_firehose") + method = getattr(self.stream_client, "posts_firehose_en") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -427,7 +433,7 @@ def test_posts_firehose_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/firehose/stream" + expected_path = "/2/tweets/firehose/stream/lang/en" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -443,12 +449,12 @@ def test_posts_firehose_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for posts_firehose: {e}") + pytest.fail(f"Contract test failed for posts_firehose_en: {e}") - def test_posts_firehose_required_parameters(self): - """Test that posts_firehose handles parameters correctly.""" - method = getattr(self.stream_client, "posts_firehose") + def test_posts_firehose_en_required_parameters(self): + """Test that posts_firehose_en handles parameters correctly.""" + method = getattr(self.stream_client, "posts_firehose_en") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -462,8 +468,8 @@ def test_posts_firehose_required_parameters(self): method() - def test_posts_firehose_response_structure(self): - """Test posts_firehose response structure validation.""" + def test_posts_firehose_en_response_structure(self): + """Test posts_firehose_en response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -479,7 +485,7 @@ def test_posts_firehose_response_structure(self): kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "posts_firehose") + method = getattr(self.stream_client, "posts_firehose_en") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -491,8 +497,8 @@ def test_posts_firehose_response_structure(self): ) - def test_get_rules_request_structure(self): - """Test get_rules request structure.""" + def test_posts_request_structure(self): + """Test posts request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -508,7 +514,7 @@ def test_get_rules_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "get_rules") + method = getattr(self.stream_client, "posts") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -543,7 +549,7 @@ def test_get_rules_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/search/stream/rules" + expected_path = "/2/tweets/search/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -559,12 +565,12 @@ def test_get_rules_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_rules: {e}") + pytest.fail(f"Contract test failed for posts: {e}") - def test_get_rules_required_parameters(self): - """Test that get_rules handles parameters correctly.""" - method = getattr(self.stream_client, "get_rules") + def test_posts_required_parameters(self): + """Test that posts handles parameters correctly.""" + method = getattr(self.stream_client, "posts") # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -578,8 +584,8 @@ def test_get_rules_required_parameters(self): pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_rules_response_structure(self): - """Test get_rules response structure validation.""" + def test_posts_response_structure(self): + """Test posts response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -594,7 +600,7 @@ def test_get_rules_response_structure(self): kwargs = {} # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "get_rules") + method = getattr(self.stream_client, "posts") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -606,8 +612,8 @@ def test_get_rules_response_structure(self): ) - def test_update_rules_request_structure(self): - """Test update_rules request structure.""" + def test_posts_firehose_request_structure(self): + """Test posts_firehose request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -616,18 +622,15 @@ def test_update_rules_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["partition"] = 42 # Add request body if required - # Import and create proper request model instance - from xdk.stream.models import UpdateRulesRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = UpdateRulesRequest() # Call the method try: - method = getattr(self.stream_client, "update_rules") + method = getattr(self.stream_client, "posts_firehose") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -646,7 +649,7 @@ def test_update_rules_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -655,14 +658,14 @@ def test_update_rules_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/search/stream/rules" + expected_path = "/2/tweets/firehose/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -678,12 +681,12 @@ def test_update_rules_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for update_rules: {e}") + pytest.fail(f"Contract test failed for posts_firehose: {e}") - def test_update_rules_required_parameters(self): - """Test that update_rules handles parameters correctly.""" - method = getattr(self.stream_client, "update_rules") + def test_posts_firehose_required_parameters(self): + """Test that posts_firehose handles parameters correctly.""" + method = getattr(self.stream_client, "posts_firehose") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -691,14 +694,14 @@ def test_update_rules_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_update_rules_response_structure(self): - """Test update_rules response structure validation.""" + def test_posts_firehose_response_structure(self): + """Test posts_firehose response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -708,16 +711,13 @@ def test_update_rules_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["partition"] = 1 # Add request body if required - # Import and create proper request model instance - from xdk.stream.models import UpdateRulesRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = UpdateRulesRequest() # Call method and verify response structure - method = getattr(self.stream_client, "update_rules") + method = getattr(self.stream_client, "posts_firehose") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -729,8 +729,8 @@ def test_update_rules_response_structure(self): ) - def test_posts_sample_request_structure(self): - """Test posts_sample request structure.""" + def test_posts_firehose_ja_request_structure(self): + """Test posts_firehose_ja request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -743,10 +743,11 @@ def test_posts_sample_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters + kwargs["partition"] = 42 # Add request body if required # Call the method try: - method = getattr(self.stream_client, "posts_sample") + method = getattr(self.stream_client, "posts_firehose_ja") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -781,7 +782,7 @@ def test_posts_sample_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/sample/stream" + expected_path = "/2/tweets/firehose/stream/lang/ja" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -797,27 +798,27 @@ def test_posts_sample_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for posts_sample: {e}") + pytest.fail(f"Contract test failed for posts_firehose_ja: {e}") - def test_posts_sample_required_parameters(self): - """Test that posts_sample handles parameters correctly.""" - method = getattr(self.stream_client, "posts_sample") - # No required parameters, method should be callable without args + def test_posts_firehose_ja_required_parameters(self): + """Test that posts_firehose_ja handles parameters correctly.""" + method = getattr(self.stream_client, "posts_firehose_ja") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") mock_session.get.return_value = mock_response - try: + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_posts_sample_response_structure(self): - """Test posts_sample response structure validation.""" + def test_posts_firehose_ja_response_structure(self): + """Test posts_firehose_ja response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -830,9 +831,10 @@ def test_posts_sample_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "posts_sample") + method = getattr(self.stream_client, "posts_firehose_ja") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -844,8 +846,8 @@ def test_posts_sample_response_structure(self): ) - def test_posts_firehose_en_request_structure(self): - """Test posts_firehose_en request structure.""" + def test_likes_sample10_request_structure(self): + """Test likes_sample10 request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -862,7 +864,7 @@ def test_posts_firehose_en_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "posts_firehose_en") + method = getattr(self.stream_client, "likes_sample10") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -897,7 +899,7 @@ def test_posts_firehose_en_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/firehose/stream/lang/en" + expected_path = "/2/likes/sample10/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -913,12 +915,12 @@ def test_posts_firehose_en_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for posts_firehose_en: {e}") + pytest.fail(f"Contract test failed for likes_sample10: {e}") - def test_posts_firehose_en_required_parameters(self): - """Test that posts_firehose_en handles parameters correctly.""" - method = getattr(self.stream_client, "posts_firehose_en") + def test_likes_sample10_required_parameters(self): + """Test that likes_sample10 handles parameters correctly.""" + method = getattr(self.stream_client, "likes_sample10") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -932,8 +934,8 @@ def test_posts_firehose_en_required_parameters(self): method() - def test_posts_firehose_en_response_structure(self): - """Test posts_firehose_en response structure validation.""" + def test_likes_sample10_response_structure(self): + """Test likes_sample10 response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -949,7 +951,7 @@ def test_posts_firehose_en_response_structure(self): kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "posts_firehose_en") + method = getattr(self.stream_client, "likes_sample10") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -961,8 +963,8 @@ def test_posts_firehose_en_response_structure(self): ) - def test_posts_sample10_request_structure(self): - """Test posts_sample10 request structure.""" + def test_posts_compliance_request_structure(self): + """Test posts_compliance request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -979,7 +981,7 @@ def test_posts_sample10_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "posts_sample10") + method = getattr(self.stream_client, "posts_compliance") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1014,7 +1016,7 @@ def test_posts_sample10_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/sample10/stream" + expected_path = "/2/tweets/compliance/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1030,12 +1032,12 @@ def test_posts_sample10_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for posts_sample10: {e}") + pytest.fail(f"Contract test failed for posts_compliance: {e}") - def test_posts_sample10_required_parameters(self): - """Test that posts_sample10 handles parameters correctly.""" - method = getattr(self.stream_client, "posts_sample10") + def test_posts_compliance_required_parameters(self): + """Test that posts_compliance handles parameters correctly.""" + method = getattr(self.stream_client, "posts_compliance") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1049,8 +1051,8 @@ def test_posts_sample10_required_parameters(self): method() - def test_posts_sample10_response_structure(self): - """Test posts_sample10 response structure validation.""" + def test_posts_compliance_response_structure(self): + """Test posts_compliance response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1066,7 +1068,7 @@ def test_posts_sample10_response_structure(self): kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "posts_sample10") + method = getattr(self.stream_client, "posts_compliance") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1078,8 +1080,8 @@ def test_posts_sample10_response_structure(self): ) - def test_get_rule_counts_request_structure(self): - """Test get_rule_counts request structure.""" + def test_users_compliance_request_structure(self): + """Test users_compliance request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1092,10 +1094,11 @@ def test_get_rule_counts_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters + kwargs["partition"] = 42 # Add request body if required # Call the method try: - method = getattr(self.stream_client, "get_rule_counts") + method = getattr(self.stream_client, "users_compliance") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1130,7 +1133,7 @@ def test_get_rule_counts_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/search/stream/rules/counts" + expected_path = "/2/users/compliance/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1146,27 +1149,27 @@ def test_get_rule_counts_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_rule_counts: {e}") + pytest.fail(f"Contract test failed for users_compliance: {e}") - def test_get_rule_counts_required_parameters(self): - """Test that get_rule_counts handles parameters correctly.""" - method = getattr(self.stream_client, "get_rule_counts") - # No required parameters, method should be callable without args + def test_users_compliance_required_parameters(self): + """Test that users_compliance handles parameters correctly.""" + method = getattr(self.stream_client, "users_compliance") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") mock_session.get.return_value = mock_response - try: + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_rule_counts_response_structure(self): - """Test get_rule_counts response structure validation.""" + def test_users_compliance_response_structure(self): + """Test users_compliance response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1179,9 +1182,10 @@ def test_get_rule_counts_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "get_rule_counts") + method = getattr(self.stream_client, "users_compliance") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1193,8 +1197,8 @@ def test_get_rule_counts_response_structure(self): ) - def test_posts_firehose_pt_request_structure(self): - """Test posts_firehose_pt request structure.""" + def test_likes_firehose_request_structure(self): + """Test likes_firehose request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1211,7 +1215,7 @@ def test_posts_firehose_pt_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "posts_firehose_pt") + method = getattr(self.stream_client, "likes_firehose") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1246,7 +1250,7 @@ def test_posts_firehose_pt_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/firehose/stream/lang/pt" + expected_path = "/2/likes/firehose/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1262,12 +1266,12 @@ def test_posts_firehose_pt_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for posts_firehose_pt: {e}") + pytest.fail(f"Contract test failed for likes_firehose: {e}") - def test_posts_firehose_pt_required_parameters(self): - """Test that posts_firehose_pt handles parameters correctly.""" - method = getattr(self.stream_client, "posts_firehose_pt") + def test_likes_firehose_required_parameters(self): + """Test that likes_firehose handles parameters correctly.""" + method = getattr(self.stream_client, "likes_firehose") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1281,8 +1285,8 @@ def test_posts_firehose_pt_required_parameters(self): method() - def test_posts_firehose_pt_response_structure(self): - """Test posts_firehose_pt response structure validation.""" + def test_likes_firehose_response_structure(self): + """Test likes_firehose response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1298,7 +1302,7 @@ def test_posts_firehose_pt_response_structure(self): kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "posts_firehose_pt") + method = getattr(self.stream_client, "likes_firehose") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1427,8 +1431,8 @@ def test_posts_firehose_ko_response_structure(self): ) - def test_likes_firehose_request_structure(self): - """Test likes_firehose request structure.""" + def test_labels_compliance_request_structure(self): + """Test labels_compliance request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1441,11 +1445,10 @@ def test_likes_firehose_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["partition"] = 42 # Add request body if required # Call the method try: - method = getattr(self.stream_client, "likes_firehose") + method = getattr(self.stream_client, "labels_compliance") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1480,7 +1483,7 @@ def test_likes_firehose_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/likes/firehose/stream" + expected_path = "/2/tweets/label/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1496,27 +1499,27 @@ def test_likes_firehose_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for likes_firehose: {e}") + pytest.fail(f"Contract test failed for labels_compliance: {e}") - def test_likes_firehose_required_parameters(self): - """Test that likes_firehose handles parameters correctly.""" - method = getattr(self.stream_client, "likes_firehose") - # Test with missing required parameters - mock the request to avoid network calls + def test_labels_compliance_required_parameters(self): + """Test that labels_compliance handles parameters correctly.""" + method = getattr(self.stream_client, "labels_compliance") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_likes_firehose_response_structure(self): - """Test likes_firehose response structure validation.""" + def test_labels_compliance_response_structure(self): + """Test labels_compliance response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1529,10 +1532,9 @@ def test_likes_firehose_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "likes_firehose") + method = getattr(self.stream_client, "labels_compliance") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1544,8 +1546,8 @@ def test_likes_firehose_response_structure(self): ) - def test_likes_sample10_request_structure(self): - """Test likes_sample10 request structure.""" + def test_posts_sample10_request_structure(self): + """Test posts_sample10 request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1562,7 +1564,7 @@ def test_likes_sample10_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "likes_sample10") + method = getattr(self.stream_client, "posts_sample10") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1597,7 +1599,7 @@ def test_likes_sample10_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/likes/sample10/stream" + expected_path = "/2/tweets/sample10/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1613,12 +1615,12 @@ def test_likes_sample10_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for likes_sample10: {e}") + pytest.fail(f"Contract test failed for posts_sample10: {e}") - def test_likes_sample10_required_parameters(self): - """Test that likes_sample10 handles parameters correctly.""" - method = getattr(self.stream_client, "likes_sample10") + def test_posts_sample10_required_parameters(self): + """Test that posts_sample10 handles parameters correctly.""" + method = getattr(self.stream_client, "posts_sample10") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1632,8 +1634,8 @@ def test_likes_sample10_required_parameters(self): method() - def test_likes_sample10_response_structure(self): - """Test likes_sample10 response structure validation.""" + def test_posts_sample10_response_structure(self): + """Test posts_sample10 response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1649,7 +1651,7 @@ def test_likes_sample10_response_structure(self): kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "likes_sample10") + method = getattr(self.stream_client, "posts_sample10") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1661,8 +1663,8 @@ def test_likes_sample10_response_structure(self): ) - def test_users_compliance_request_structure(self): - """Test users_compliance request structure.""" + def test_posts_firehose_pt_request_structure(self): + """Test posts_firehose_pt request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1679,7 +1681,7 @@ def test_users_compliance_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "users_compliance") + method = getattr(self.stream_client, "posts_firehose_pt") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1714,7 +1716,7 @@ def test_users_compliance_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/compliance/stream" + expected_path = "/2/tweets/firehose/stream/lang/pt" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1730,12 +1732,12 @@ def test_users_compliance_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for users_compliance: {e}") + pytest.fail(f"Contract test failed for posts_firehose_pt: {e}") - def test_users_compliance_required_parameters(self): - """Test that users_compliance handles parameters correctly.""" - method = getattr(self.stream_client, "users_compliance") + def test_posts_firehose_pt_required_parameters(self): + """Test that posts_firehose_pt handles parameters correctly.""" + method = getattr(self.stream_client, "posts_firehose_pt") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1749,8 +1751,8 @@ def test_users_compliance_required_parameters(self): method() - def test_users_compliance_response_structure(self): - """Test users_compliance response structure validation.""" + def test_posts_firehose_pt_response_structure(self): + """Test posts_firehose_pt response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1766,7 +1768,7 @@ def test_users_compliance_response_structure(self): kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "users_compliance") + method = getattr(self.stream_client, "posts_firehose_pt") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1778,8 +1780,8 @@ def test_users_compliance_response_structure(self): ) - def test_posts_request_structure(self): - """Test posts request structure.""" + def test_get_rule_counts_request_structure(self): + """Test get_rule_counts request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1795,7 +1797,7 @@ def test_posts_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.stream_client, "posts") + method = getattr(self.stream_client, "get_rule_counts") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1830,7 +1832,7 @@ def test_posts_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/search/stream" + expected_path = "/2/tweets/search/stream/rules/counts" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1846,12 +1848,12 @@ def test_posts_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for posts: {e}") + pytest.fail(f"Contract test failed for get_rule_counts: {e}") - def test_posts_required_parameters(self): - """Test that posts handles parameters correctly.""" - method = getattr(self.stream_client, "posts") + def test_get_rule_counts_required_parameters(self): + """Test that get_rule_counts handles parameters correctly.""" + method = getattr(self.stream_client, "get_rule_counts") # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1865,8 +1867,8 @@ def test_posts_required_parameters(self): pytest.fail(f"Method with no required params should be callable: {e}") - def test_posts_response_structure(self): - """Test posts response structure validation.""" + def test_get_rule_counts_response_structure(self): + """Test get_rule_counts response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1881,7 +1883,7 @@ def test_posts_response_structure(self): kwargs = {} # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "posts") + method = getattr(self.stream_client, "get_rule_counts") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1893,8 +1895,8 @@ def test_posts_response_structure(self): ) - def test_posts_compliance_request_structure(self): - """Test posts_compliance request structure.""" + def test_posts_sample_request_structure(self): + """Test posts_sample request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1907,11 +1909,10 @@ def test_posts_compliance_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["partition"] = 42 # Add request body if required # Call the method try: - method = getattr(self.stream_client, "posts_compliance") + method = getattr(self.stream_client, "posts_sample") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1946,7 +1947,7 @@ def test_posts_compliance_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/compliance/stream" + expected_path = "/2/tweets/sample/stream" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1962,27 +1963,27 @@ def test_posts_compliance_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for posts_compliance: {e}") + pytest.fail(f"Contract test failed for posts_sample: {e}") - def test_posts_compliance_required_parameters(self): - """Test that posts_compliance handles parameters correctly.""" - method = getattr(self.stream_client, "posts_compliance") - # Test with missing required parameters - mock the request to avoid network calls + def test_posts_sample_required_parameters(self): + """Test that posts_sample handles parameters correctly.""" + method = getattr(self.stream_client, "posts_sample") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_posts_compliance_response_structure(self): - """Test posts_compliance response structure validation.""" + def test_posts_sample_response_structure(self): + """Test posts_sample response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1995,10 +1996,9 @@ def test_posts_compliance_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["partition"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.stream_client, "posts_compliance") + method = getattr(self.stream_client, "posts_sample") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/stream/test_structure.py b/tests/stream/test_structure.py index 772e61f..26b2ee5 100644 --- a/tests/stream/test_structure.py +++ b/tests/stream/test_structure.py @@ -28,22 +28,22 @@ def setup_class(self): self.stream_client = getattr(self.client, "stream") - def test_labels_compliance_exists(self): - """Test that labels_compliance method exists with correct signature.""" + def test_likes_compliance_exists(self): + """Test that likes_compliance method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "labels_compliance", None) + method = getattr(StreamClient, "likes_compliance", None) assert ( method is not None - ), f"Method labels_compliance does not exist on StreamClient" + ), f"Method likes_compliance does not exist on StreamClient" # Check method is callable - assert callable(method), f"labels_compliance is not callable" + assert callable(method), f"likes_compliance is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"labels_compliance should have at least 'self' parameter" + ), f"likes_compliance should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -52,7 +52,7 @@ def test_labels_compliance_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from labels_compliance" + ), f"Required parameter '{required_param}' missing from likes_compliance" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", @@ -67,54 +67,42 @@ def test_labels_compliance_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_labels_compliance_return_annotation(self): - """Test that labels_compliance has proper return type annotation.""" - method = getattr(StreamClient, "labels_compliance") + def test_likes_compliance_return_annotation(self): + """Test that likes_compliance has proper return type annotation.""" + method = getattr(StreamClient, "likes_compliance") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method labels_compliance should have return type annotation" + ), f"Method likes_compliance should have return type annotation" - def test_posts_firehose_ja_exists(self): - """Test that posts_firehose_ja method exists with correct signature.""" + def test_get_rules_exists(self): + """Test that get_rules method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "posts_firehose_ja", None) - assert ( - method is not None - ), f"Method posts_firehose_ja does not exist on StreamClient" + method = getattr(StreamClient, "get_rules", None) + assert method is not None, f"Method get_rules does not exist on StreamClient" # Check method is callable - assert callable(method), f"posts_firehose_ja is not callable" + assert callable(method), f"get_rules is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"posts_firehose_ja should have at least 'self' parameter" + assert len(params) >= 1, f"get_rules should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "partition", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from posts_firehose_ja" + ), f"Required parameter '{required_param}' missing from get_rules" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "backfill_minutes", - "start_time", - "end_time", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", - "user.fields", - "place.fields", + "ids", + "max_results", + "pagination_token", ] for optional_param in optional_params: if optional_param in params: @@ -124,32 +112,47 @@ def test_posts_firehose_ja_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_posts_firehose_ja_return_annotation(self): - """Test that posts_firehose_ja has proper return type annotation.""" - method = getattr(StreamClient, "posts_firehose_ja") + def test_get_rules_return_annotation(self): + """Test that get_rules has proper return type annotation.""" + method = getattr(StreamClient, "get_rules") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method posts_firehose_ja should have return type annotation" + ), f"Method get_rules should have return type annotation" - def test_likes_compliance_exists(self): - """Test that likes_compliance method exists with correct signature.""" - # Check method exists - method = getattr(StreamClient, "likes_compliance", None) + def test_get_rules_pagination_params(self): + """Test that get_rules has pagination parameters.""" + method = getattr(StreamClient, "get_rules") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) assert ( - method is not None - ), f"Method likes_compliance does not exist on StreamClient" + has_pagination_param + ), f"Paginated method get_rules should have pagination parameters" + + + def test_update_rules_exists(self): + """Test that update_rules method exists with correct signature.""" + # Check method exists + method = getattr(StreamClient, "update_rules", None) + assert method is not None, f"Method update_rules does not exist on StreamClient" # Check method is callable - assert callable(method), f"likes_compliance is not callable" + assert callable(method), f"update_rules is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"likes_compliance should have at least 'self' parameter" + assert len(params) >= 1, f"update_rules should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -158,12 +161,11 @@ def test_likes_compliance_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from likes_compliance" + ), f"Required parameter '{required_param}' missing from update_rules" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "backfill_minutes", - "start_time", - "end_time", + "dry_run", + "delete_all", ] for optional_param in optional_params: if optional_param in params: @@ -173,30 +175,32 @@ def test_likes_compliance_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_likes_compliance_return_annotation(self): - """Test that likes_compliance has proper return type annotation.""" - method = getattr(StreamClient, "likes_compliance") + def test_update_rules_return_annotation(self): + """Test that update_rules has proper return type annotation.""" + method = getattr(StreamClient, "update_rules") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method likes_compliance should have return type annotation" + ), f"Method update_rules should have return type annotation" - def test_posts_firehose_exists(self): - """Test that posts_firehose method exists with correct signature.""" + def test_posts_firehose_en_exists(self): + """Test that posts_firehose_en method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "posts_firehose", None) + method = getattr(StreamClient, "posts_firehose_en", None) assert ( method is not None - ), f"Method posts_firehose does not exist on StreamClient" + ), f"Method posts_firehose_en does not exist on StreamClient" # Check method is callable - assert callable(method), f"posts_firehose is not callable" + assert callable(method), f"posts_firehose_en is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"posts_firehose should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"posts_firehose_en should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -207,7 +211,7 @@ def test_posts_firehose_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from posts_firehose" + ), f"Required parameter '{required_param}' missing from posts_firehose_en" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", @@ -228,28 +232,28 @@ def test_posts_firehose_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_posts_firehose_return_annotation(self): - """Test that posts_firehose has proper return type annotation.""" - method = getattr(StreamClient, "posts_firehose") + def test_posts_firehose_en_return_annotation(self): + """Test that posts_firehose_en has proper return type annotation.""" + method = getattr(StreamClient, "posts_firehose_en") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method posts_firehose should have return type annotation" + ), f"Method posts_firehose_en should have return type annotation" - def test_get_rules_exists(self): - """Test that get_rules method exists with correct signature.""" + def test_posts_exists(self): + """Test that posts method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "get_rules", None) - assert method is not None, f"Method get_rules does not exist on StreamClient" + method = getattr(StreamClient, "posts", None) + assert method is not None, f"Method posts does not exist on StreamClient" # Check method is callable - assert callable(method), f"get_rules is not callable" + assert callable(method), f"posts is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_rules should have at least 'self' parameter" + assert len(params) >= 1, f"posts should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -258,12 +262,18 @@ def test_get_rules_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_rules" + ), f"Required parameter '{required_param}' missing from posts" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "ids", - "max_results", - "pagination_token", + "backfill_minutes", + "start_time", + "end_time", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -273,60 +283,52 @@ def test_get_rules_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_rules_return_annotation(self): - """Test that get_rules has proper return type annotation.""" - method = getattr(StreamClient, "get_rules") + def test_posts_return_annotation(self): + """Test that posts has proper return type annotation.""" + method = getattr(StreamClient, "posts") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_rules should have return type annotation" - - - def test_get_rules_pagination_params(self): - """Test that get_rules has pagination parameters.""" - method = getattr(StreamClient, "get_rules") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_rules should have pagination parameters" + ), f"Method posts should have return type annotation" - def test_update_rules_exists(self): - """Test that update_rules method exists with correct signature.""" + def test_posts_firehose_exists(self): + """Test that posts_firehose method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "update_rules", None) - assert method is not None, f"Method update_rules does not exist on StreamClient" + method = getattr(StreamClient, "posts_firehose", None) + assert ( + method is not None + ), f"Method posts_firehose does not exist on StreamClient" # Check method is callable - assert callable(method), f"update_rules is not callable" + assert callable(method), f"posts_firehose is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"update_rules should have at least 'self' parameter" + assert len(params) >= 1, f"posts_firehose should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "partition", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from update_rules" + ), f"Required parameter '{required_param}' missing from posts_firehose" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "dry_run", - "delete_all", + "backfill_minutes", + "start_time", + "end_time", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -336,40 +338,48 @@ def test_update_rules_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_update_rules_return_annotation(self): - """Test that update_rules has proper return type annotation.""" - method = getattr(StreamClient, "update_rules") + def test_posts_firehose_return_annotation(self): + """Test that posts_firehose has proper return type annotation.""" + method = getattr(StreamClient, "posts_firehose") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method update_rules should have return type annotation" + ), f"Method posts_firehose should have return type annotation" - def test_posts_sample_exists(self): - """Test that posts_sample method exists with correct signature.""" + def test_posts_firehose_ja_exists(self): + """Test that posts_firehose_ja method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "posts_sample", None) - assert method is not None, f"Method posts_sample does not exist on StreamClient" + method = getattr(StreamClient, "posts_firehose_ja", None) + assert ( + method is not None + ), f"Method posts_firehose_ja does not exist on StreamClient" # Check method is callable - assert callable(method), f"posts_sample is not callable" + assert callable(method), f"posts_firehose_ja is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"posts_sample should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"posts_firehose_ja should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "partition", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from posts_sample" + ), f"Required parameter '{required_param}' missing from posts_firehose_ja" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", + "start_time", + "end_time", "tweet.fields", "expansions", "media.fields", @@ -385,32 +395,30 @@ def test_posts_sample_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_posts_sample_return_annotation(self): - """Test that posts_sample has proper return type annotation.""" - method = getattr(StreamClient, "posts_sample") + def test_posts_firehose_ja_return_annotation(self): + """Test that posts_firehose_ja has proper return type annotation.""" + method = getattr(StreamClient, "posts_firehose_ja") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method posts_sample should have return type annotation" + ), f"Method posts_firehose_ja should have return type annotation" - def test_posts_firehose_en_exists(self): - """Test that posts_firehose_en method exists with correct signature.""" + def test_likes_sample10_exists(self): + """Test that likes_sample10 method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "posts_firehose_en", None) + method = getattr(StreamClient, "likes_sample10", None) assert ( method is not None - ), f"Method posts_firehose_en does not exist on StreamClient" + ), f"Method likes_sample10 does not exist on StreamClient" # Check method is callable - assert callable(method), f"posts_firehose_en is not callable" + assert callable(method), f"likes_sample10 is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"posts_firehose_en should have at least 'self' parameter" + assert len(params) >= 1, f"likes_sample10 should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -421,18 +429,16 @@ def test_posts_firehose_en_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from posts_firehose_en" + ), f"Required parameter '{required_param}' missing from likes_sample10" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", "start_time", "end_time", - "tweet.fields", + "like_with_tweet_author.fields", "expansions", - "media.fields", - "poll.fields", "user.fields", - "place.fields", + "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -442,30 +448,32 @@ def test_posts_firehose_en_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_posts_firehose_en_return_annotation(self): - """Test that posts_firehose_en has proper return type annotation.""" - method = getattr(StreamClient, "posts_firehose_en") + def test_likes_sample10_return_annotation(self): + """Test that likes_sample10 has proper return type annotation.""" + method = getattr(StreamClient, "likes_sample10") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method posts_firehose_en should have return type annotation" + ), f"Method likes_sample10 should have return type annotation" - def test_posts_sample10_exists(self): - """Test that posts_sample10 method exists with correct signature.""" + def test_posts_compliance_exists(self): + """Test that posts_compliance method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "posts_sample10", None) + method = getattr(StreamClient, "posts_compliance", None) assert ( method is not None - ), f"Method posts_sample10 does not exist on StreamClient" + ), f"Method posts_compliance does not exist on StreamClient" # Check method is callable - assert callable(method), f"posts_sample10 is not callable" + assert callable(method), f"posts_compliance is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"posts_sample10 should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"posts_compliance should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -476,18 +484,12 @@ def test_posts_sample10_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from posts_sample10" + ), f"Required parameter '{required_param}' missing from posts_compliance" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "backfill_minutes", - "start_time", - "end_time", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", - "user.fields", - "place.fields", + optional_params = [ + "backfill_minutes", + "start_time", + "end_time", ] for optional_param in optional_params: if optional_param in params: @@ -497,44 +499,48 @@ def test_posts_sample10_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_posts_sample10_return_annotation(self): - """Test that posts_sample10 has proper return type annotation.""" - method = getattr(StreamClient, "posts_sample10") + def test_posts_compliance_return_annotation(self): + """Test that posts_compliance has proper return type annotation.""" + method = getattr(StreamClient, "posts_compliance") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method posts_sample10 should have return type annotation" + ), f"Method posts_compliance should have return type annotation" - def test_get_rule_counts_exists(self): - """Test that get_rule_counts method exists with correct signature.""" + def test_users_compliance_exists(self): + """Test that users_compliance method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "get_rule_counts", None) + method = getattr(StreamClient, "users_compliance", None) assert ( method is not None - ), f"Method get_rule_counts does not exist on StreamClient" + ), f"Method users_compliance does not exist on StreamClient" # Check method is callable - assert callable(method), f"get_rule_counts is not callable" + assert callable(method), f"users_compliance is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"get_rule_counts should have at least 'self' parameter" + ), f"users_compliance should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "partition", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_rule_counts" + ), f"Required parameter '{required_param}' missing from users_compliance" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "rules_count.fields", + "backfill_minutes", + "start_time", + "end_time", ] for optional_param in optional_params: if optional_param in params: @@ -544,32 +550,30 @@ def test_get_rule_counts_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_rule_counts_return_annotation(self): - """Test that get_rule_counts has proper return type annotation.""" - method = getattr(StreamClient, "get_rule_counts") + def test_users_compliance_return_annotation(self): + """Test that users_compliance has proper return type annotation.""" + method = getattr(StreamClient, "users_compliance") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_rule_counts should have return type annotation" + ), f"Method users_compliance should have return type annotation" - def test_posts_firehose_pt_exists(self): - """Test that posts_firehose_pt method exists with correct signature.""" + def test_likes_firehose_exists(self): + """Test that likes_firehose method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "posts_firehose_pt", None) + method = getattr(StreamClient, "likes_firehose", None) assert ( method is not None - ), f"Method posts_firehose_pt does not exist on StreamClient" + ), f"Method likes_firehose does not exist on StreamClient" # Check method is callable - assert callable(method), f"posts_firehose_pt is not callable" + assert callable(method), f"likes_firehose is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"posts_firehose_pt should have at least 'self' parameter" + assert len(params) >= 1, f"likes_firehose should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -580,18 +584,16 @@ def test_posts_firehose_pt_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from posts_firehose_pt" + ), f"Required parameter '{required_param}' missing from likes_firehose" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", "start_time", "end_time", - "tweet.fields", + "like_with_tweet_author.fields", "expansions", - "media.fields", - "poll.fields", "user.fields", - "place.fields", + "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -601,14 +603,14 @@ def test_posts_firehose_pt_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_posts_firehose_pt_return_annotation(self): - """Test that posts_firehose_pt has proper return type annotation.""" - method = getattr(StreamClient, "posts_firehose_pt") + def test_likes_firehose_return_annotation(self): + """Test that likes_firehose has proper return type annotation.""" + method = getattr(StreamClient, "likes_firehose") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method posts_firehose_pt should have return type annotation" + ), f"Method likes_firehose should have return type annotation" def test_posts_firehose_ko_exists(self): @@ -668,40 +670,36 @@ def test_posts_firehose_ko_return_annotation(self): ), f"Method posts_firehose_ko should have return type annotation" - def test_likes_firehose_exists(self): - """Test that likes_firehose method exists with correct signature.""" + def test_labels_compliance_exists(self): + """Test that labels_compliance method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "likes_firehose", None) + method = getattr(StreamClient, "labels_compliance", None) assert ( method is not None - ), f"Method likes_firehose does not exist on StreamClient" + ), f"Method labels_compliance does not exist on StreamClient" # Check method is callable - assert callable(method), f"likes_firehose is not callable" + assert callable(method), f"labels_compliance is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"likes_firehose should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"labels_compliance should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "partition", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from likes_firehose" + ), f"Required parameter '{required_param}' missing from labels_compliance" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", "start_time", "end_time", - "like_with_tweet_author.fields", - "expansions", - "user.fields", - "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -711,30 +709,30 @@ def test_likes_firehose_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_likes_firehose_return_annotation(self): - """Test that likes_firehose has proper return type annotation.""" - method = getattr(StreamClient, "likes_firehose") + def test_labels_compliance_return_annotation(self): + """Test that labels_compliance has proper return type annotation.""" + method = getattr(StreamClient, "labels_compliance") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method likes_firehose should have return type annotation" + ), f"Method labels_compliance should have return type annotation" - def test_likes_sample10_exists(self): - """Test that likes_sample10 method exists with correct signature.""" + def test_posts_sample10_exists(self): + """Test that posts_sample10 method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "likes_sample10", None) + method = getattr(StreamClient, "posts_sample10", None) assert ( method is not None - ), f"Method likes_sample10 does not exist on StreamClient" + ), f"Method posts_sample10 does not exist on StreamClient" # Check method is callable - assert callable(method), f"likes_sample10 is not callable" + assert callable(method), f"posts_sample10 is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"likes_sample10 should have at least 'self' parameter" + assert len(params) >= 1, f"posts_sample10 should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -745,16 +743,18 @@ def test_likes_sample10_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from likes_sample10" + ), f"Required parameter '{required_param}' missing from posts_sample10" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", "start_time", "end_time", - "like_with_tweet_author.fields", + "tweet.fields", "expansions", + "media.fields", + "poll.fields", "user.fields", - "tweet.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -764,32 +764,32 @@ def test_likes_sample10_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_likes_sample10_return_annotation(self): - """Test that likes_sample10 has proper return type annotation.""" - method = getattr(StreamClient, "likes_sample10") + def test_posts_sample10_return_annotation(self): + """Test that posts_sample10 has proper return type annotation.""" + method = getattr(StreamClient, "posts_sample10") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method likes_sample10 should have return type annotation" + ), f"Method posts_sample10 should have return type annotation" - def test_users_compliance_exists(self): - """Test that users_compliance method exists with correct signature.""" + def test_posts_firehose_pt_exists(self): + """Test that posts_firehose_pt method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "users_compliance", None) + method = getattr(StreamClient, "posts_firehose_pt", None) assert ( method is not None - ), f"Method users_compliance does not exist on StreamClient" + ), f"Method posts_firehose_pt does not exist on StreamClient" # Check method is callable - assert callable(method), f"users_compliance is not callable" + assert callable(method), f"posts_firehose_pt is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"users_compliance should have at least 'self' parameter" + ), f"posts_firehose_pt should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -800,12 +800,18 @@ def test_users_compliance_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from users_compliance" + ), f"Required parameter '{required_param}' missing from posts_firehose_pt" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", "start_time", "end_time", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -815,28 +821,32 @@ def test_users_compliance_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_users_compliance_return_annotation(self): - """Test that users_compliance has proper return type annotation.""" - method = getattr(StreamClient, "users_compliance") + def test_posts_firehose_pt_return_annotation(self): + """Test that posts_firehose_pt has proper return type annotation.""" + method = getattr(StreamClient, "posts_firehose_pt") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method users_compliance should have return type annotation" + ), f"Method posts_firehose_pt should have return type annotation" - def test_posts_exists(self): - """Test that posts method exists with correct signature.""" + def test_get_rule_counts_exists(self): + """Test that get_rule_counts method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "posts", None) - assert method is not None, f"Method posts does not exist on StreamClient" + method = getattr(StreamClient, "get_rule_counts", None) + assert ( + method is not None + ), f"Method get_rule_counts does not exist on StreamClient" # Check method is callable - assert callable(method), f"posts is not callable" + assert callable(method), f"get_rule_counts is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"posts should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_rule_counts should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -845,18 +855,10 @@ def test_posts_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from posts" + ), f"Required parameter '{required_param}' missing from get_rule_counts" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "backfill_minutes", - "start_time", - "end_time", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", - "user.fields", - "place.fields", + "rules_count.fields", ] for optional_param in optional_params: if optional_param in params: @@ -866,48 +868,46 @@ def test_posts_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_posts_return_annotation(self): - """Test that posts has proper return type annotation.""" - method = getattr(StreamClient, "posts") + def test_get_rule_counts_return_annotation(self): + """Test that get_rule_counts has proper return type annotation.""" + method = getattr(StreamClient, "get_rule_counts") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method posts should have return type annotation" + ), f"Method get_rule_counts should have return type annotation" - def test_posts_compliance_exists(self): - """Test that posts_compliance method exists with correct signature.""" + def test_posts_sample_exists(self): + """Test that posts_sample method exists with correct signature.""" # Check method exists - method = getattr(StreamClient, "posts_compliance", None) - assert ( - method is not None - ), f"Method posts_compliance does not exist on StreamClient" + method = getattr(StreamClient, "posts_sample", None) + assert method is not None, f"Method posts_sample does not exist on StreamClient" # Check method is callable - assert callable(method), f"posts_compliance is not callable" + assert callable(method), f"posts_sample is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"posts_compliance should have at least 'self' parameter" + assert len(params) >= 1, f"posts_sample should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "partition", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from posts_compliance" + ), f"Required parameter '{required_param}' missing from posts_sample" # Check optional parameters have defaults (excluding 'self') optional_params = [ "backfill_minutes", - "start_time", - "end_time", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -917,36 +917,36 @@ def test_posts_compliance_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_posts_compliance_return_annotation(self): - """Test that posts_compliance has proper return type annotation.""" - method = getattr(StreamClient, "posts_compliance") + def test_posts_sample_return_annotation(self): + """Test that posts_sample has proper return type annotation.""" + method = getattr(StreamClient, "posts_sample") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method posts_compliance should have return type annotation" + ), f"Method posts_sample should have return type annotation" def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "labels_compliance", - "posts_firehose_ja", "likes_compliance", - "posts_firehose", "get_rules", "update_rules", - "posts_sample", "posts_firehose_en", - "posts_sample10", - "get_rule_counts", - "posts_firehose_pt", - "posts_firehose_ko", - "likes_firehose", - "likes_sample10", - "users_compliance", "posts", + "posts_firehose", + "posts_firehose_ja", + "likes_sample10", "posts_compliance", + "users_compliance", + "likes_firehose", + "posts_firehose_ko", + "labels_compliance", + "posts_sample10", + "posts_firehose_pt", + "get_rule_counts", + "posts_sample", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/trends/test_contracts.py b/tests/trends/test_contracts.py index 87ec0c6..1200c21 100644 --- a/tests/trends/test_contracts.py +++ b/tests/trends/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.trends_client = getattr(self.client, "trends") - def test_get_personalized_request_structure(self): - """Test get_personalized request structure.""" + def test_get_ai_request_structure(self): + """Test get_ai request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -41,10 +41,11 @@ def test_get_personalized_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.trends_client, "get_personalized") + method = getattr(self.trends_client, "get_ai") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -79,7 +80,7 @@ def test_get_personalized_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/personalized_trends" + expected_path = "/2/ai_trends/{id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -95,27 +96,27 @@ def test_get_personalized_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_personalized: {e}") + pytest.fail(f"Contract test failed for get_ai: {e}") - def test_get_personalized_required_parameters(self): - """Test that get_personalized handles parameters correctly.""" - method = getattr(self.trends_client, "get_personalized") - # No required parameters, method should be callable without args + def test_get_ai_required_parameters(self): + """Test that get_ai handles parameters correctly.""" + method = getattr(self.trends_client, "get_ai") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") mock_session.get.return_value = mock_response - try: + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_personalized_response_structure(self): - """Test get_personalized response structure validation.""" + def test_get_ai_response_structure(self): + """Test get_ai response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -128,9 +129,10 @@ def test_get_personalized_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.trends_client, "get_personalized") + method = getattr(self.trends_client, "get_ai") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -142,8 +144,8 @@ def test_get_personalized_response_structure(self): ) - def test_get_by_woeid_request_structure(self): - """Test get_by_woeid request structure.""" + def test_get_personalized_request_structure(self): + """Test get_personalized request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -156,11 +158,10 @@ def test_get_by_woeid_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["woeid"] = 42 # Add request body if required # Call the method try: - method = getattr(self.trends_client, "get_by_woeid") + method = getattr(self.trends_client, "get_personalized") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -195,7 +196,7 @@ def test_get_by_woeid_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/trends/by/woeid/{woeid}" + expected_path = "/2/users/personalized_trends" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -211,27 +212,27 @@ def test_get_by_woeid_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_woeid: {e}") + pytest.fail(f"Contract test failed for get_personalized: {e}") - def test_get_by_woeid_required_parameters(self): - """Test that get_by_woeid handles parameters correctly.""" - method = getattr(self.trends_client, "get_by_woeid") - # Test with missing required parameters - mock the request to avoid network calls + def test_get_personalized_required_parameters(self): + """Test that get_personalized handles parameters correctly.""" + method = getattr(self.trends_client, "get_personalized") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_by_woeid_response_structure(self): - """Test get_by_woeid response structure validation.""" + def test_get_personalized_response_structure(self): + """Test get_personalized response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -244,10 +245,9 @@ def test_get_by_woeid_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["woeid"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.trends_client, "get_by_woeid") + method = getattr(self.trends_client, "get_personalized") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -259,8 +259,8 @@ def test_get_by_woeid_response_structure(self): ) - def test_get_ai_request_structure(self): - """Test get_ai request structure.""" + def test_get_by_woeid_request_structure(self): + """Test get_by_woeid request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -273,11 +273,11 @@ def test_get_ai_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["woeid"] = 42 # Add request body if required # Call the method try: - method = getattr(self.trends_client, "get_ai") + method = getattr(self.trends_client, "get_by_woeid") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -312,7 +312,7 @@ def test_get_ai_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/ai_trends/{id}" + expected_path = "/2/trends/by/woeid/{woeid}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -328,12 +328,12 @@ def test_get_ai_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_ai: {e}") + pytest.fail(f"Contract test failed for get_by_woeid: {e}") - def test_get_ai_required_parameters(self): - """Test that get_ai handles parameters correctly.""" - method = getattr(self.trends_client, "get_ai") + def test_get_by_woeid_required_parameters(self): + """Test that get_by_woeid handles parameters correctly.""" + method = getattr(self.trends_client, "get_by_woeid") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -347,8 +347,8 @@ def test_get_ai_required_parameters(self): method() - def test_get_ai_response_structure(self): - """Test get_ai response structure validation.""" + def test_get_by_woeid_response_structure(self): + """Test get_by_woeid response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -361,10 +361,10 @@ def test_get_ai_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["woeid"] = 1 # Add request body if required # Call method and verify response structure - method = getattr(self.trends_client, "get_ai") + method = getattr(self.trends_client, "get_by_woeid") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/trends/test_structure.py b/tests/trends/test_structure.py index 1ac7026..4dc22b0 100644 --- a/tests/trends/test_structure.py +++ b/tests/trends/test_structure.py @@ -28,6 +28,51 @@ def setup_class(self): self.trends_client = getattr(self.client, "trends") + def test_get_ai_exists(self): + """Test that get_ai method exists with correct signature.""" + # Check method exists + method = getattr(TrendsClient, "get_ai", None) + assert method is not None, f"Method get_ai does not exist on TrendsClient" + # Check method is callable + assert callable(method), f"get_ai is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert len(params) >= 1, f"get_ai should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [ + "id", + ] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from get_ai" + # Check optional parameters have defaults (excluding 'self') + optional_params = [ + "news.fields", + ] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_get_ai_return_annotation(self): + """Test that get_ai has proper return type annotation.""" + method = getattr(TrendsClient, "get_ai") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method get_ai should have return type annotation" + + def test_get_personalized_exists(self): """Test that get_personalized method exists with correct signature.""" # Check method exists @@ -121,57 +166,12 @@ def test_get_by_woeid_return_annotation(self): ), f"Method get_by_woeid should have return type annotation" - def test_get_ai_exists(self): - """Test that get_ai method exists with correct signature.""" - # Check method exists - method = getattr(TrendsClient, "get_ai", None) - assert method is not None, f"Method get_ai does not exist on TrendsClient" - # Check method is callable - assert callable(method), f"get_ai is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"get_ai should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [ - "id", - ] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from get_ai" - # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "news.fields", - ] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_get_ai_return_annotation(self): - """Test that get_ai has proper return type annotation.""" - method = getattr(TrendsClient, "get_ai") - sig = inspect.signature(method) - # Check return annotation exists - assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method get_ai should have return type annotation" - - def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ + "get_ai", "get_personalized", "get_by_woeid", - "get_ai", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/users/test_contracts.py b/tests/users/test_contracts.py index aea1f2b..d256de4 100644 --- a/tests/users/test_contracts.py +++ b/tests/users/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.users_client = getattr(self.client, "users") - def test_get_by_ids_request_structure(self): - """Test get_by_ids request structure.""" + def test_get_bookmarks_request_structure(self): + """Test get_bookmarks request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -41,11 +41,11 @@ def test_get_by_ids_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["ids"] = ["test_item"] + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_by_ids") + method = getattr(self.users_client, "get_bookmarks") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -80,7 +80,7 @@ def test_get_by_ids_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users" + expected_path = "/2/users/{id}/bookmarks" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -96,12 +96,12 @@ def test_get_by_ids_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_ids: {e}") + pytest.fail(f"Contract test failed for get_bookmarks: {e}") - def test_get_by_ids_required_parameters(self): - """Test that get_by_ids handles parameters correctly.""" - method = getattr(self.users_client, "get_by_ids") + def test_get_bookmarks_required_parameters(self): + """Test that get_bookmarks handles parameters correctly.""" + method = getattr(self.users_client, "get_bookmarks") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -115,8 +115,8 @@ def test_get_by_ids_required_parameters(self): method() - def test_get_by_ids_response_structure(self): - """Test get_by_ids response structure validation.""" + def test_get_bookmarks_response_structure(self): + """Test get_bookmarks response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -129,10 +129,10 @@ def test_get_by_ids_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["ids"] = ["test"] + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_by_ids") + method = getattr(self.users_client, "get_bookmarks") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -144,8 +144,8 @@ def test_get_by_ids_response_structure(self): ) - def test_unfollow_user_request_structure(self): - """Test unfollow_user request structure.""" + def test_create_bookmark_request_structure(self): + """Test create_bookmark request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -154,16 +154,19 @@ def test_unfollow_user_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["source_user_id"] = "test_value" - kwargs["target_user_id"] = "test_value" + kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import CreateBookmarkRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateBookmarkRequest() # Call the method try: - method = getattr(self.users_client, "unfollow_user") + method = getattr(self.users_client, "create_bookmark") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -182,7 +185,7 @@ def test_unfollow_user_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -191,14 +194,14 @@ def test_unfollow_user_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{source_user_id}/following/{target_user_id}" + expected_path = "/2/users/{id}/bookmarks" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -214,12 +217,12 @@ def test_unfollow_user_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for unfollow_user: {e}") + pytest.fail(f"Contract test failed for create_bookmark: {e}") - def test_unfollow_user_required_parameters(self): - """Test that unfollow_user handles parameters correctly.""" - method = getattr(self.users_client, "unfollow_user") + def test_create_bookmark_required_parameters(self): + """Test that create_bookmark handles parameters correctly.""" + method = getattr(self.users_client, "create_bookmark") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -227,14 +230,14 @@ def test_unfollow_user_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_unfollow_user_response_structure(self): - """Test unfollow_user response structure validation.""" + def test_create_bookmark_response_structure(self): + """Test create_bookmark response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -244,14 +247,17 @@ def test_unfollow_user_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["source_user_id"] = "test" - kwargs["target_user_id"] = "test" + kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import CreateBookmarkRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateBookmarkRequest() # Call method and verify response structure - method = getattr(self.users_client, "unfollow_user") + method = getattr(self.users_client, "create_bookmark") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -263,8 +269,8 @@ def test_unfollow_user_response_structure(self): ) - def test_get_liked_posts_request_structure(self): - """Test get_liked_posts request structure.""" + def test_get_mentions_request_structure(self): + """Test get_mentions request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -281,7 +287,7 @@ def test_get_liked_posts_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_liked_posts") + method = getattr(self.users_client, "get_mentions") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -316,7 +322,7 @@ def test_get_liked_posts_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/liked_tweets" + expected_path = "/2/users/{id}/mentions" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -332,12 +338,12 @@ def test_get_liked_posts_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_liked_posts: {e}") + pytest.fail(f"Contract test failed for get_mentions: {e}") - def test_get_liked_posts_required_parameters(self): - """Test that get_liked_posts handles parameters correctly.""" - method = getattr(self.users_client, "get_liked_posts") + def test_get_mentions_required_parameters(self): + """Test that get_mentions handles parameters correctly.""" + method = getattr(self.users_client, "get_mentions") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -351,8 +357,8 @@ def test_get_liked_posts_required_parameters(self): method() - def test_get_liked_posts_response_structure(self): - """Test get_liked_posts response structure validation.""" + def test_get_mentions_response_structure(self): + """Test get_mentions response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -368,7 +374,7 @@ def test_get_liked_posts_response_structure(self): kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_liked_posts") + method = getattr(self.users_client, "get_mentions") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -380,8 +386,8 @@ def test_get_liked_posts_response_structure(self): ) - def test_unfollow_list_request_structure(self): - """Test unfollow_list request structure.""" + def test_get_owned_lists_request_structure(self): + """Test get_owned_lists request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -390,16 +396,15 @@ def test_unfollow_list_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" - kwargs["list_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "unfollow_list") + method = getattr(self.users_client, "get_owned_lists") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -418,7 +423,7 @@ def test_unfollow_list_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -427,14 +432,14 @@ def test_unfollow_list_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/followed_lists/{list_id}" + expected_path = "/2/users/{id}/owned_lists" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -450,12 +455,12 @@ def test_unfollow_list_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for unfollow_list: {e}") + pytest.fail(f"Contract test failed for get_owned_lists: {e}") - def test_unfollow_list_required_parameters(self): - """Test that unfollow_list handles parameters correctly.""" - method = getattr(self.users_client, "unfollow_list") + def test_get_owned_lists_required_parameters(self): + """Test that get_owned_lists handles parameters correctly.""" + method = getattr(self.users_client, "get_owned_lists") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -463,14 +468,14 @@ def test_unfollow_list_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_unfollow_list_response_structure(self): - """Test unfollow_list response structure validation.""" + def test_get_owned_lists_response_structure(self): + """Test get_owned_lists response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -480,14 +485,13 @@ def test_unfollow_list_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" - kwargs["list_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "unfollow_list") + method = getattr(self.users_client, "get_owned_lists") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -499,8 +503,8 @@ def test_unfollow_list_response_structure(self): ) - def test_get_following_request_structure(self): - """Test get_following request structure.""" + def test_unpin_list_request_structure(self): + """Test unpin_list request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -509,15 +513,16 @@ def test_get_following_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" + kwargs["list_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_following") + method = getattr(self.users_client, "unpin_list") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -536,7 +541,7 @@ def test_get_following_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -545,14 +550,14 @@ def test_get_following_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/following" + expected_path = "/2/users/{id}/pinned_lists/{list_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -568,12 +573,12 @@ def test_get_following_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_following: {e}") + pytest.fail(f"Contract test failed for unpin_list: {e}") - def test_get_following_required_parameters(self): - """Test that get_following handles parameters correctly.""" - method = getattr(self.users_client, "get_following") + def test_unpin_list_required_parameters(self): + """Test that unpin_list handles parameters correctly.""" + method = getattr(self.users_client, "unpin_list") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -581,14 +586,14 @@ def test_get_following_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_following_response_structure(self): - """Test get_following response structure validation.""" + def test_unpin_list_response_structure(self): + """Test unpin_list response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -598,13 +603,14 @@ def test_get_following_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" + kwargs["list_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_following") + method = getattr(self.users_client, "unpin_list") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -616,8 +622,8 @@ def test_get_following_response_structure(self): ) - def test_follow_user_request_structure(self): - """Test follow_user request structure.""" + def test_block_dms_request_structure(self): + """Test block_dms request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -632,13 +638,9 @@ def test_follow_user_request_structure(self): # Add required parameters kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import FollowUserRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = FollowUserRequest() # Call the method try: - method = getattr(self.users_client, "follow_user") + method = getattr(self.users_client, "block_dms") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -673,7 +675,7 @@ def test_follow_user_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/following" + expected_path = "/2/users/{id}/dm/block" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -689,12 +691,12 @@ def test_follow_user_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for follow_user: {e}") + pytest.fail(f"Contract test failed for block_dms: {e}") - def test_follow_user_required_parameters(self): - """Test that follow_user handles parameters correctly.""" - method = getattr(self.users_client, "follow_user") + def test_block_dms_required_parameters(self): + """Test that block_dms handles parameters correctly.""" + method = getattr(self.users_client, "block_dms") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -708,8 +710,8 @@ def test_follow_user_required_parameters(self): method() - def test_follow_user_response_structure(self): - """Test follow_user response structure validation.""" + def test_block_dms_response_structure(self): + """Test block_dms response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -724,12 +726,8 @@ def test_follow_user_response_structure(self): kwargs = {} kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import FollowUserRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = FollowUserRequest() # Call method and verify response structure - method = getattr(self.users_client, "follow_user") + method = getattr(self.users_client, "block_dms") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -741,8 +739,8 @@ def test_follow_user_response_structure(self): ) - def test_unmute_user_request_structure(self): - """Test unmute_user request structure.""" + def test_repost_post_request_structure(self): + """Test repost_post request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -751,16 +749,19 @@ def test_unmute_user_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["source_user_id"] = "test_value" - kwargs["target_user_id"] = "test_value" + kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import RepostPostRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = RepostPostRequest() # Call the method try: - method = getattr(self.users_client, "unmute_user") + method = getattr(self.users_client, "repost_post") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -779,7 +780,7 @@ def test_unmute_user_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -788,14 +789,14 @@ def test_unmute_user_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{source_user_id}/muting/{target_user_id}" + expected_path = "/2/users/{id}/retweets" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -811,12 +812,12 @@ def test_unmute_user_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for unmute_user: {e}") + pytest.fail(f"Contract test failed for repost_post: {e}") - def test_unmute_user_required_parameters(self): - """Test that unmute_user handles parameters correctly.""" - method = getattr(self.users_client, "unmute_user") + def test_repost_post_required_parameters(self): + """Test that repost_post handles parameters correctly.""" + method = getattr(self.users_client, "repost_post") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -824,14 +825,14 @@ def test_unmute_user_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_unmute_user_response_structure(self): - """Test unmute_user response structure validation.""" + def test_repost_post_response_structure(self): + """Test repost_post response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -841,14 +842,17 @@ def test_unmute_user_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["source_user_id"] = "test" - kwargs["target_user_id"] = "test" + kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import RepostPostRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = RepostPostRequest() # Call method and verify response structure - method = getattr(self.users_client, "unmute_user") + method = getattr(self.users_client, "repost_post") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -860,8 +864,8 @@ def test_unmute_user_response_structure(self): ) - def test_repost_post_request_structure(self): - """Test repost_post request structure.""" + def test_get_following_request_structure(self): + """Test get_following request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -870,19 +874,15 @@ def test_repost_post_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import RepostPostRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = RepostPostRequest() # Call the method try: - method = getattr(self.users_client, "repost_post") + method = getattr(self.users_client, "get_following") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -901,7 +901,7 @@ def test_repost_post_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -910,14 +910,14 @@ def test_repost_post_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/retweets" + expected_path = "/2/users/{id}/following" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -933,12 +933,12 @@ def test_repost_post_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for repost_post: {e}") + pytest.fail(f"Contract test failed for get_following: {e}") - def test_repost_post_required_parameters(self): - """Test that repost_post handles parameters correctly.""" - method = getattr(self.users_client, "repost_post") + def test_get_following_required_parameters(self): + """Test that get_following handles parameters correctly.""" + method = getattr(self.users_client, "get_following") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -946,14 +946,14 @@ def test_repost_post_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_repost_post_response_structure(self): - """Test repost_post response structure validation.""" + def test_get_following_response_structure(self): + """Test get_following response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -963,17 +963,13 @@ def test_repost_post_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import RepostPostRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = RepostPostRequest() # Call method and verify response structure - method = getattr(self.users_client, "repost_post") + method = getattr(self.users_client, "get_following") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -985,8 +981,8 @@ def test_repost_post_response_structure(self): ) - def test_get_blocking_request_structure(self): - """Test get_blocking request structure.""" + def test_follow_user_request_structure(self): + """Test follow_user request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -995,15 +991,19 @@ def test_get_blocking_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import FollowUserRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = FollowUserRequest() # Call the method try: - method = getattr(self.users_client, "get_blocking") + method = getattr(self.users_client, "follow_user") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1022,7 +1022,7 @@ def test_get_blocking_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1031,14 +1031,14 @@ def test_get_blocking_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/blocking" + expected_path = "/2/users/{id}/following" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1054,12 +1054,12 @@ def test_get_blocking_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_blocking: {e}") + pytest.fail(f"Contract test failed for follow_user: {e}") - def test_get_blocking_required_parameters(self): - """Test that get_blocking handles parameters correctly.""" - method = getattr(self.users_client, "get_blocking") + def test_follow_user_required_parameters(self): + """Test that follow_user handles parameters correctly.""" + method = getattr(self.users_client, "follow_user") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1067,14 +1067,14 @@ def test_get_blocking_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_blocking_response_structure(self): - """Test get_blocking response structure validation.""" + def test_follow_user_response_structure(self): + """Test follow_user response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1084,13 +1084,17 @@ def test_get_blocking_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import FollowUserRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = FollowUserRequest() # Call method and verify response structure - method = getattr(self.users_client, "get_blocking") + method = getattr(self.users_client, "follow_user") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1102,8 +1106,8 @@ def test_get_blocking_response_structure(self): ) - def test_get_followers_request_structure(self): - """Test get_followers request structure.""" + def test_unrepost_post_request_structure(self): + """Test unrepost_post request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1112,15 +1116,16 @@ def test_get_followers_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" + kwargs["source_tweet_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_followers") + method = getattr(self.users_client, "unrepost_post") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1139,7 +1144,7 @@ def test_get_followers_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1148,14 +1153,14 @@ def test_get_followers_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/followers" + expected_path = "/2/users/{id}/retweets/{source_tweet_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1171,12 +1176,12 @@ def test_get_followers_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_followers: {e}") + pytest.fail(f"Contract test failed for unrepost_post: {e}") - def test_get_followers_required_parameters(self): - """Test that get_followers handles parameters correctly.""" - method = getattr(self.users_client, "get_followers") + def test_unrepost_post_required_parameters(self): + """Test that unrepost_post handles parameters correctly.""" + method = getattr(self.users_client, "unrepost_post") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1184,14 +1189,14 @@ def test_get_followers_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_followers_response_structure(self): - """Test get_followers response structure validation.""" + def test_unrepost_post_response_structure(self): + """Test unrepost_post response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1201,13 +1206,14 @@ def test_get_followers_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" + kwargs["source_tweet_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_followers") + method = getattr(self.users_client, "unrepost_post") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1219,8 +1225,8 @@ def test_get_followers_response_structure(self): ) - def test_get_mentions_request_structure(self): - """Test get_mentions request structure.""" + def test_get_by_usernames_request_structure(self): + """Test get_by_usernames request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1233,11 +1239,11 @@ def test_get_mentions_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["usernames"] = ["test_item"] # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_mentions") + method = getattr(self.users_client, "get_by_usernames") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1272,7 +1278,7 @@ def test_get_mentions_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/mentions" + expected_path = "/2/users/by" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1288,12 +1294,12 @@ def test_get_mentions_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_mentions: {e}") + pytest.fail(f"Contract test failed for get_by_usernames: {e}") - def test_get_mentions_required_parameters(self): - """Test that get_mentions handles parameters correctly.""" - method = getattr(self.users_client, "get_mentions") + def test_get_by_usernames_required_parameters(self): + """Test that get_by_usernames handles parameters correctly.""" + method = getattr(self.users_client, "get_by_usernames") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1307,8 +1313,8 @@ def test_get_mentions_required_parameters(self): method() - def test_get_mentions_response_structure(self): - """Test get_mentions response structure validation.""" + def test_get_by_usernames_response_structure(self): + """Test get_by_usernames response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1321,10 +1327,10 @@ def test_get_mentions_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["usernames"] = ["test"] # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_mentions") + method = getattr(self.users_client, "get_by_usernames") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1336,8 +1342,8 @@ def test_get_mentions_response_structure(self): ) - def test_get_pinned_lists_request_structure(self): - """Test get_pinned_lists request structure.""" + def test_get_liked_posts_request_structure(self): + """Test get_liked_posts request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1354,7 +1360,7 @@ def test_get_pinned_lists_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_pinned_lists") + method = getattr(self.users_client, "get_liked_posts") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1389,7 +1395,7 @@ def test_get_pinned_lists_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/pinned_lists" + expected_path = "/2/users/{id}/liked_tweets" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1405,12 +1411,12 @@ def test_get_pinned_lists_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_pinned_lists: {e}") + pytest.fail(f"Contract test failed for get_liked_posts: {e}") - def test_get_pinned_lists_required_parameters(self): - """Test that get_pinned_lists handles parameters correctly.""" - method = getattr(self.users_client, "get_pinned_lists") + def test_get_liked_posts_required_parameters(self): + """Test that get_liked_posts handles parameters correctly.""" + method = getattr(self.users_client, "get_liked_posts") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1424,8 +1430,8 @@ def test_get_pinned_lists_required_parameters(self): method() - def test_get_pinned_lists_response_structure(self): - """Test get_pinned_lists response structure validation.""" + def test_get_liked_posts_response_structure(self): + """Test get_liked_posts response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1441,7 +1447,7 @@ def test_get_pinned_lists_response_structure(self): kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_pinned_lists") + method = getattr(self.users_client, "get_liked_posts") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1453,8 +1459,8 @@ def test_get_pinned_lists_response_structure(self): ) - def test_pin_list_request_structure(self): - """Test pin_list request structure.""" + def test_get_reposts_of_me_request_structure(self): + """Test get_reposts_of_me request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1463,19 +1469,14 @@ def test_pin_list_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import PinListRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = PinListRequest() # Call the method try: - method = getattr(self.users_client, "pin_list") + method = getattr(self.users_client, "get_reposts_of_me") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1494,7 +1495,7 @@ def test_pin_list_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1503,14 +1504,14 @@ def test_pin_list_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/pinned_lists" + expected_path = "/2/users/reposts_of_me" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1526,27 +1527,27 @@ def test_pin_list_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for pin_list: {e}") + pytest.fail(f"Contract test failed for get_reposts_of_me: {e}") - def test_pin_list_required_parameters(self): - """Test that pin_list handles parameters correctly.""" - method = getattr(self.users_client, "pin_list") - # Test with missing required parameters - mock the request to avoid network calls + def test_get_reposts_of_me_required_parameters(self): + """Test that get_reposts_of_me handles parameters correctly.""" + method = getattr(self.users_client, "get_reposts_of_me") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None + mock_session.get.return_value = mock_response + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_pin_list_response_structure(self): - """Test pin_list response structure validation.""" + def test_get_reposts_of_me_response_structure(self): + """Test get_reposts_of_me response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1556,17 +1557,12 @@ def test_pin_list_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import PinListRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = PinListRequest() # Call method and verify response structure - method = getattr(self.users_client, "pin_list") + method = getattr(self.users_client, "get_reposts_of_me") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1578,8 +1574,8 @@ def test_pin_list_response_structure(self): ) - def test_unpin_list_request_structure(self): - """Test unpin_list request structure.""" + def test_get_bookmark_folders_request_structure(self): + """Test get_bookmark_folders request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1588,16 +1584,15 @@ def test_unpin_list_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" - kwargs["list_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "unpin_list") + method = getattr(self.users_client, "get_bookmark_folders") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1616,7 +1611,7 @@ def test_unpin_list_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1625,14 +1620,14 @@ def test_unpin_list_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/pinned_lists/{list_id}" + expected_path = "/2/users/{id}/bookmarks/folders" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1648,12 +1643,12 @@ def test_unpin_list_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for unpin_list: {e}") + pytest.fail(f"Contract test failed for get_bookmark_folders: {e}") - def test_unpin_list_required_parameters(self): - """Test that unpin_list handles parameters correctly.""" - method = getattr(self.users_client, "unpin_list") + def test_get_bookmark_folders_required_parameters(self): + """Test that get_bookmark_folders handles parameters correctly.""" + method = getattr(self.users_client, "get_bookmark_folders") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1661,14 +1656,14 @@ def test_unpin_list_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_unpin_list_response_structure(self): - """Test unpin_list response structure validation.""" + def test_get_bookmark_folders_response_structure(self): + """Test get_bookmark_folders response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1678,14 +1673,13 @@ def test_unpin_list_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" - kwargs["list_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "unpin_list") + method = getattr(self.users_client, "get_bookmark_folders") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1816,8 +1810,8 @@ def test_delete_bookmark_response_structure(self): ) - def test_block_dms_request_structure(self): - """Test block_dms request structure.""" + def test_like_post_request_structure(self): + """Test like_post request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1832,9 +1826,13 @@ def test_block_dms_request_structure(self): # Add required parameters kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import LikePostRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = LikePostRequest() # Call the method try: - method = getattr(self.users_client, "block_dms") + method = getattr(self.users_client, "like_post") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1869,7 +1867,7 @@ def test_block_dms_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/dm/block" + expected_path = "/2/users/{id}/likes" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -1885,12 +1883,12 @@ def test_block_dms_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for block_dms: {e}") + pytest.fail(f"Contract test failed for like_post: {e}") - def test_block_dms_required_parameters(self): - """Test that block_dms handles parameters correctly.""" - method = getattr(self.users_client, "block_dms") + def test_like_post_required_parameters(self): + """Test that like_post handles parameters correctly.""" + method = getattr(self.users_client, "like_post") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -1904,8 +1902,8 @@ def test_block_dms_required_parameters(self): method() - def test_block_dms_response_structure(self): - """Test block_dms response structure validation.""" + def test_like_post_response_structure(self): + """Test like_post response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -1920,8 +1918,12 @@ def test_block_dms_response_structure(self): kwargs = {} kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import LikePostRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = LikePostRequest() # Call method and verify response structure - method = getattr(self.users_client, "block_dms") + method = getattr(self.users_client, "like_post") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -1933,8 +1935,8 @@ def test_block_dms_response_structure(self): ) - def test_unlike_post_request_structure(self): - """Test unlike_post request structure.""" + def test_unblock_dms_request_structure(self): + """Test unblock_dms request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -1943,16 +1945,15 @@ def test_unlike_post_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" - kwargs["tweet_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "unlike_post") + method = getattr(self.users_client, "unblock_dms") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -1971,7 +1972,7 @@ def test_unlike_post_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -1980,14 +1981,14 @@ def test_unlike_post_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/likes/{tweet_id}" + expected_path = "/2/users/{id}/dm/unblock" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2003,12 +2004,12 @@ def test_unlike_post_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for unlike_post: {e}") + pytest.fail(f"Contract test failed for unblock_dms: {e}") - def test_unlike_post_required_parameters(self): - """Test that unlike_post handles parameters correctly.""" - method = getattr(self.users_client, "unlike_post") + def test_unblock_dms_required_parameters(self): + """Test that unblock_dms handles parameters correctly.""" + method = getattr(self.users_client, "unblock_dms") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -2016,14 +2017,14 @@ def test_unlike_post_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_unlike_post_response_structure(self): - """Test unlike_post response structure validation.""" + def test_unblock_dms_response_structure(self): + """Test unblock_dms response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2033,14 +2034,13 @@ def test_unlike_post_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" - kwargs["tweet_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "unlike_post") + method = getattr(self.users_client, "unblock_dms") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2052,8 +2052,8 @@ def test_unlike_post_response_structure(self): ) - def test_get_bookmarks_by_folder_id_request_structure(self): - """Test get_bookmarks_by_folder_id request structure.""" + def test_unfollow_user_request_structure(self): + """Test unfollow_user request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -2062,16 +2062,16 @@ def test_get_bookmarks_by_folder_id_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" - kwargs["folder_id"] = "test_value" + kwargs["source_user_id"] = "test_value" + kwargs["target_user_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_bookmarks_by_folder_id") + method = getattr(self.users_client, "unfollow_user") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -2090,7 +2090,7 @@ def test_get_bookmarks_by_folder_id_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -2099,14 +2099,14 @@ def test_get_bookmarks_by_folder_id_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/bookmarks/folders/{folder_id}" + expected_path = "/2/users/{source_user_id}/following/{target_user_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2122,12 +2122,12 @@ def test_get_bookmarks_by_folder_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_bookmarks_by_folder_id: {e}") + pytest.fail(f"Contract test failed for unfollow_user: {e}") - def test_get_bookmarks_by_folder_id_required_parameters(self): - """Test that get_bookmarks_by_folder_id handles parameters correctly.""" - method = getattr(self.users_client, "get_bookmarks_by_folder_id") + def test_unfollow_user_required_parameters(self): + """Test that unfollow_user handles parameters correctly.""" + method = getattr(self.users_client, "unfollow_user") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -2135,14 +2135,14 @@ def test_get_bookmarks_by_folder_id_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_bookmarks_by_folder_id_response_structure(self): - """Test get_bookmarks_by_folder_id response structure validation.""" + def test_unfollow_user_response_structure(self): + """Test unfollow_user response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2152,14 +2152,14 @@ def test_get_bookmarks_by_folder_id_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" - kwargs["folder_id"] = "test" + kwargs["source_user_id"] = "test" + kwargs["target_user_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_bookmarks_by_folder_id") + method = getattr(self.users_client, "unfollow_user") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2171,8 +2171,8 @@ def test_get_bookmarks_by_folder_id_response_structure(self): ) - def test_get_bookmark_folders_request_structure(self): - """Test get_bookmark_folders request structure.""" + def test_get_list_memberships_request_structure(self): + """Test get_list_memberships request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -2189,7 +2189,7 @@ def test_get_bookmark_folders_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_bookmark_folders") + method = getattr(self.users_client, "get_list_memberships") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -2224,7 +2224,7 @@ def test_get_bookmark_folders_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/bookmarks/folders" + expected_path = "/2/users/{id}/list_memberships" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2240,12 +2240,12 @@ def test_get_bookmark_folders_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_bookmark_folders: {e}") + pytest.fail(f"Contract test failed for get_list_memberships: {e}") - def test_get_bookmark_folders_required_parameters(self): - """Test that get_bookmark_folders handles parameters correctly.""" - method = getattr(self.users_client, "get_bookmark_folders") + def test_get_list_memberships_required_parameters(self): + """Test that get_list_memberships handles parameters correctly.""" + method = getattr(self.users_client, "get_list_memberships") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -2259,8 +2259,8 @@ def test_get_bookmark_folders_required_parameters(self): method() - def test_get_bookmark_folders_response_structure(self): - """Test get_bookmark_folders response structure validation.""" + def test_get_list_memberships_response_structure(self): + """Test get_list_memberships response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2276,7 +2276,7 @@ def test_get_bookmark_folders_response_structure(self): kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_bookmark_folders") + method = getattr(self.users_client, "get_list_memberships") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2288,8 +2288,8 @@ def test_get_bookmark_folders_response_structure(self): ) - def test_unrepost_post_request_structure(self): - """Test unrepost_post request structure.""" + def test_get_muting_request_structure(self): + """Test get_muting request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -2298,16 +2298,15 @@ def test_unrepost_post_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" - kwargs["source_tweet_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "unrepost_post") + method = getattr(self.users_client, "get_muting") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -2326,7 +2325,7 @@ def test_unrepost_post_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -2335,14 +2334,14 @@ def test_unrepost_post_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/retweets/{source_tweet_id}" + expected_path = "/2/users/{id}/muting" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2358,12 +2357,12 @@ def test_unrepost_post_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for unrepost_post: {e}") + pytest.fail(f"Contract test failed for get_muting: {e}") - def test_unrepost_post_required_parameters(self): - """Test that unrepost_post handles parameters correctly.""" - method = getattr(self.users_client, "unrepost_post") + def test_get_muting_required_parameters(self): + """Test that get_muting handles parameters correctly.""" + method = getattr(self.users_client, "get_muting") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -2371,14 +2370,14 @@ def test_unrepost_post_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_unrepost_post_response_structure(self): - """Test unrepost_post response structure validation.""" + def test_get_muting_response_structure(self): + """Test get_muting response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2388,14 +2387,13 @@ def test_unrepost_post_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" - kwargs["source_tweet_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "unrepost_post") + method = getattr(self.users_client, "get_muting") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2407,8 +2405,8 @@ def test_unrepost_post_response_structure(self): ) - def test_get_reposts_of_me_request_structure(self): - """Test get_reposts_of_me request structure.""" + def test_mute_user_request_structure(self): + """Test mute_user request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -2417,14 +2415,19 @@ def test_get_reposts_of_me_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import MuteUserRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = MuteUserRequest() # Call the method try: - method = getattr(self.users_client, "get_reposts_of_me") + method = getattr(self.users_client, "mute_user") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -2443,7 +2446,7 @@ def test_get_reposts_of_me_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -2452,14 +2455,14 @@ def test_get_reposts_of_me_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/reposts_of_me" + expected_path = "/2/users/{id}/muting" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2475,27 +2478,27 @@ def test_get_reposts_of_me_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_reposts_of_me: {e}") + pytest.fail(f"Contract test failed for mute_user: {e}") - def test_get_reposts_of_me_required_parameters(self): - """Test that get_reposts_of_me handles parameters correctly.""" - method = getattr(self.users_client, "get_reposts_of_me") - # No required parameters, method should be callable without args + def test_mute_user_required_parameters(self): + """Test that mute_user handles parameters correctly.""" + method = getattr(self.users_client, "mute_user") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response - try: + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_session.post.return_value = mock_response + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_reposts_of_me_response_structure(self): - """Test get_reposts_of_me response structure validation.""" + def test_mute_user_response_structure(self): + """Test mute_user response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2505,12 +2508,17 @@ def test_get_reposts_of_me_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import MuteUserRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = MuteUserRequest() # Call method and verify response structure - method = getattr(self.users_client, "get_reposts_of_me") + method = getattr(self.users_client, "mute_user") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2522,8 +2530,8 @@ def test_get_reposts_of_me_response_structure(self): ) - def test_get_list_memberships_request_structure(self): - """Test get_list_memberships request structure.""" + def test_get_by_username_request_structure(self): + """Test get_by_username request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -2536,11 +2544,11 @@ def test_get_list_memberships_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["username"] = "test_username" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_list_memberships") + method = getattr(self.users_client, "get_by_username") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -2575,7 +2583,7 @@ def test_get_list_memberships_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/list_memberships" + expected_path = "/2/users/by/username/{username}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2591,12 +2599,12 @@ def test_get_list_memberships_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_list_memberships: {e}") + pytest.fail(f"Contract test failed for get_by_username: {e}") - def test_get_list_memberships_required_parameters(self): - """Test that get_list_memberships handles parameters correctly.""" - method = getattr(self.users_client, "get_list_memberships") + def test_get_by_username_required_parameters(self): + """Test that get_by_username handles parameters correctly.""" + method = getattr(self.users_client, "get_by_username") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -2610,8 +2618,8 @@ def test_get_list_memberships_required_parameters(self): method() - def test_get_list_memberships_response_structure(self): - """Test get_list_memberships response structure validation.""" + def test_get_by_username_response_structure(self): + """Test get_by_username response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2624,10 +2632,10 @@ def test_get_list_memberships_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["username"] = "test_value" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_list_memberships") + method = getattr(self.users_client, "get_by_username") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2639,8 +2647,8 @@ def test_get_list_memberships_response_structure(self): ) - def test_get_timeline_request_structure(self): - """Test get_timeline request structure.""" + def test_get_by_id_request_structure(self): + """Test get_by_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -2657,7 +2665,7 @@ def test_get_timeline_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_timeline") + method = getattr(self.users_client, "get_by_id") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -2692,7 +2700,7 @@ def test_get_timeline_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/timelines/reverse_chronological" + expected_path = "/2/users/{id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2708,12 +2716,12 @@ def test_get_timeline_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_timeline: {e}") + pytest.fail(f"Contract test failed for get_by_id: {e}") - def test_get_timeline_required_parameters(self): - """Test that get_timeline handles parameters correctly.""" - method = getattr(self.users_client, "get_timeline") + def test_get_by_id_required_parameters(self): + """Test that get_by_id handles parameters correctly.""" + method = getattr(self.users_client, "get_by_id") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -2727,8 +2735,8 @@ def test_get_timeline_required_parameters(self): method() - def test_get_timeline_response_structure(self): - """Test get_timeline response structure validation.""" + def test_get_by_id_response_structure(self): + """Test get_by_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2744,7 +2752,7 @@ def test_get_timeline_response_structure(self): kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_timeline") + method = getattr(self.users_client, "get_by_id") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2756,8 +2764,8 @@ def test_get_timeline_response_structure(self): ) - def test_like_post_request_structure(self): - """Test like_post request structure.""" + def test_get_by_ids_request_structure(self): + """Test get_by_ids request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -2766,19 +2774,15 @@ def test_like_post_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["ids"] = ["test_item"] # Add request body if required - # Import and create proper request model instance - from xdk.users.models import LikePostRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = LikePostRequest() # Call the method try: - method = getattr(self.users_client, "like_post") + method = getattr(self.users_client, "get_by_ids") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -2797,7 +2801,7 @@ def test_like_post_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -2806,14 +2810,14 @@ def test_like_post_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/likes" + expected_path = "/2/users" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2829,12 +2833,12 @@ def test_like_post_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for like_post: {e}") + pytest.fail(f"Contract test failed for get_by_ids: {e}") - def test_like_post_required_parameters(self): - """Test that like_post handles parameters correctly.""" - method = getattr(self.users_client, "like_post") + def test_get_by_ids_required_parameters(self): + """Test that get_by_ids handles parameters correctly.""" + method = getattr(self.users_client, "get_by_ids") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -2842,14 +2846,14 @@ def test_like_post_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_like_post_response_structure(self): - """Test like_post response structure validation.""" + def test_get_by_ids_response_structure(self): + """Test get_by_ids response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2859,17 +2863,13 @@ def test_like_post_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["ids"] = ["test"] # Add request body if required - # Import and create proper request model instance - from xdk.users.models import LikePostRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = LikePostRequest() # Call method and verify response structure - method = getattr(self.users_client, "like_post") + method = getattr(self.users_client, "get_by_ids") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2881,8 +2881,8 @@ def test_like_post_response_structure(self): ) - def test_get_me_request_structure(self): - """Test get_me request structure.""" + def test_get_bookmarks_by_folder_id_request_structure(self): + """Test get_bookmarks_by_folder_id request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -2895,10 +2895,12 @@ def test_get_me_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters + kwargs["id"] = "test_value" + kwargs["folder_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_me") + method = getattr(self.users_client, "get_bookmarks_by_folder_id") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -2933,7 +2935,7 @@ def test_get_me_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/me" + expected_path = "/2/users/{id}/bookmarks/folders/{folder_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -2949,27 +2951,27 @@ def test_get_me_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_me: {e}") + pytest.fail(f"Contract test failed for get_bookmarks_by_folder_id: {e}") - def test_get_me_required_parameters(self): - """Test that get_me handles parameters correctly.""" - method = getattr(self.users_client, "get_me") - # No required parameters, method should be callable without args + def test_get_bookmarks_by_folder_id_required_parameters(self): + """Test that get_bookmarks_by_folder_id handles parameters correctly.""" + method = getattr(self.users_client, "get_bookmarks_by_folder_id") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") mock_session.get.return_value = mock_response - try: + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_me_response_structure(self): - """Test get_me response structure validation.""" + def test_get_bookmarks_by_folder_id_response_structure(self): + """Test get_bookmarks_by_folder_id response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -2982,9 +2984,11 @@ def test_get_me_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["id"] = "test" + kwargs["folder_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_me") + method = getattr(self.users_client, "get_bookmarks_by_folder_id") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -2996,8 +3000,8 @@ def test_get_me_response_structure(self): ) - def test_get_by_usernames_request_structure(self): - """Test get_by_usernames request structure.""" + def test_get_followed_lists_request_structure(self): + """Test get_followed_lists request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3010,11 +3014,11 @@ def test_get_by_usernames_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["usernames"] = ["test_item"] + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_by_usernames") + method = getattr(self.users_client, "get_followed_lists") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3049,7 +3053,7 @@ def test_get_by_usernames_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/by" + expected_path = "/2/users/{id}/followed_lists" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -3065,12 +3069,12 @@ def test_get_by_usernames_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_usernames: {e}") + pytest.fail(f"Contract test failed for get_followed_lists: {e}") - def test_get_by_usernames_required_parameters(self): - """Test that get_by_usernames handles parameters correctly.""" - method = getattr(self.users_client, "get_by_usernames") + def test_get_followed_lists_required_parameters(self): + """Test that get_followed_lists handles parameters correctly.""" + method = getattr(self.users_client, "get_followed_lists") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -3084,8 +3088,8 @@ def test_get_by_usernames_required_parameters(self): method() - def test_get_by_usernames_response_structure(self): - """Test get_by_usernames response structure validation.""" + def test_get_followed_lists_response_structure(self): + """Test get_followed_lists response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -3098,10 +3102,10 @@ def test_get_by_usernames_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["usernames"] = ["test"] + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_by_usernames") + method = getattr(self.users_client, "get_followed_lists") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -3113,8 +3117,8 @@ def test_get_by_usernames_response_structure(self): ) - def test_get_posts_request_structure(self): - """Test get_posts request structure.""" + def test_follow_list_request_structure(self): + """Test follow_list request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3123,15 +3127,19 @@ def test_get_posts_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import FollowListRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = FollowListRequest() # Call the method try: - method = getattr(self.users_client, "get_posts") + method = getattr(self.users_client, "follow_list") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3150,7 +3158,7 @@ def test_get_posts_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -3159,14 +3167,14 @@ def test_get_posts_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/tweets" + expected_path = "/2/users/{id}/followed_lists" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -3182,12 +3190,12 @@ def test_get_posts_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_posts: {e}") + pytest.fail(f"Contract test failed for follow_list: {e}") - def test_get_posts_required_parameters(self): - """Test that get_posts handles parameters correctly.""" - method = getattr(self.users_client, "get_posts") + def test_follow_list_required_parameters(self): + """Test that follow_list handles parameters correctly.""" + method = getattr(self.users_client, "follow_list") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -3195,14 +3203,14 @@ def test_get_posts_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_posts_response_structure(self): - """Test get_posts response structure validation.""" + def test_follow_list_response_structure(self): + """Test follow_list response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -3212,13 +3220,17 @@ def test_get_posts_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import FollowListRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = FollowListRequest() # Call method and verify response structure - method = getattr(self.users_client, "get_posts") + method = getattr(self.users_client, "follow_list") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -3230,8 +3242,8 @@ def test_get_posts_response_structure(self): ) - def test_get_followed_lists_request_structure(self): - """Test get_followed_lists request structure.""" + def test_get_me_request_structure(self): + """Test get_me request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3244,11 +3256,10 @@ def test_get_followed_lists_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_followed_lists") + method = getattr(self.users_client, "get_me") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3283,7 +3294,7 @@ def test_get_followed_lists_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/followed_lists" + expected_path = "/2/users/me" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -3299,27 +3310,27 @@ def test_get_followed_lists_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_followed_lists: {e}") + pytest.fail(f"Contract test failed for get_me: {e}") - def test_get_followed_lists_required_parameters(self): - """Test that get_followed_lists handles parameters correctly.""" - method = getattr(self.users_client, "get_followed_lists") - # Test with missing required parameters - mock the request to avoid network calls + def test_get_me_required_parameters(self): + """Test that get_me handles parameters correctly.""" + method = getattr(self.users_client, "get_me") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_followed_lists_response_structure(self): - """Test get_followed_lists response structure validation.""" + def test_get_me_response_structure(self): + """Test get_me response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -3332,10 +3343,9 @@ def test_get_followed_lists_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_followed_lists") + method = getattr(self.users_client, "get_me") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -3347,8 +3357,8 @@ def test_get_followed_lists_response_structure(self): ) - def test_follow_list_request_structure(self): - """Test follow_list request structure.""" + def test_unlike_post_request_structure(self): + """Test unlike_post request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3357,19 +3367,16 @@ def test_follow_list_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" + kwargs["tweet_id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import FollowListRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = FollowListRequest() # Call the method try: - method = getattr(self.users_client, "follow_list") + method = getattr(self.users_client, "unlike_post") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3388,7 +3395,7 @@ def test_follow_list_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -3397,14 +3404,14 @@ def test_follow_list_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/followed_lists" + expected_path = "/2/users/{id}/likes/{tweet_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -3420,12 +3427,12 @@ def test_follow_list_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for follow_list: {e}") + pytest.fail(f"Contract test failed for unlike_post: {e}") - def test_follow_list_required_parameters(self): - """Test that follow_list handles parameters correctly.""" - method = getattr(self.users_client, "follow_list") + def test_unlike_post_required_parameters(self): + """Test that unlike_post handles parameters correctly.""" + method = getattr(self.users_client, "unlike_post") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -3433,14 +3440,14 @@ def test_follow_list_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_follow_list_response_structure(self): - """Test follow_list response structure validation.""" + def test_unlike_post_response_structure(self): + """Test unlike_post response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -3450,17 +3457,14 @@ def test_follow_list_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" + kwargs["tweet_id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import FollowListRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = FollowListRequest() # Call method and verify response structure - method = getattr(self.users_client, "follow_list") + method = getattr(self.users_client, "unlike_post") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -3472,8 +3476,8 @@ def test_follow_list_response_structure(self): ) - def test_get_by_username_request_structure(self): - """Test get_by_username request structure.""" + def test_get_posts_request_structure(self): + """Test get_posts request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3486,11 +3490,11 @@ def test_get_by_username_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["username"] = "test_username" + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_by_username") + method = getattr(self.users_client, "get_posts") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3525,7 +3529,7 @@ def test_get_by_username_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/by/username/{username}" + expected_path = "/2/users/{id}/tweets" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -3541,12 +3545,12 @@ def test_get_by_username_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_username: {e}") + pytest.fail(f"Contract test failed for get_posts: {e}") - def test_get_by_username_required_parameters(self): - """Test that get_by_username handles parameters correctly.""" - method = getattr(self.users_client, "get_by_username") + def test_get_posts_required_parameters(self): + """Test that get_posts handles parameters correctly.""" + method = getattr(self.users_client, "get_posts") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -3560,8 +3564,8 @@ def test_get_by_username_required_parameters(self): method() - def test_get_by_username_response_structure(self): - """Test get_by_username response structure validation.""" + def test_get_posts_response_structure(self): + """Test get_posts response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -3574,10 +3578,10 @@ def test_get_by_username_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["username"] = "test_value" + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_by_username") + method = getattr(self.users_client, "get_posts") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -3589,8 +3593,8 @@ def test_get_by_username_response_structure(self): ) - def test_get_owned_lists_request_structure(self): - """Test get_owned_lists request structure.""" + def test_unfollow_list_request_structure(self): + """Test unfollow_list request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3599,15 +3603,16 @@ def test_get_owned_lists_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" + kwargs["list_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_owned_lists") + method = getattr(self.users_client, "unfollow_list") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3626,7 +3631,7 @@ def test_get_owned_lists_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -3635,14 +3640,14 @@ def test_get_owned_lists_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/owned_lists" + expected_path = "/2/users/{id}/followed_lists/{list_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -3658,12 +3663,12 @@ def test_get_owned_lists_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_owned_lists: {e}") + pytest.fail(f"Contract test failed for unfollow_list: {e}") - def test_get_owned_lists_required_parameters(self): - """Test that get_owned_lists handles parameters correctly.""" - method = getattr(self.users_client, "get_owned_lists") + def test_unfollow_list_required_parameters(self): + """Test that unfollow_list handles parameters correctly.""" + method = getattr(self.users_client, "unfollow_list") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -3671,14 +3676,14 @@ def test_get_owned_lists_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_owned_lists_response_structure(self): - """Test get_owned_lists response structure validation.""" + def test_unfollow_list_response_structure(self): + """Test unfollow_list response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -3688,13 +3693,14 @@ def test_get_owned_lists_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" + kwargs["list_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_owned_lists") + method = getattr(self.users_client, "unfollow_list") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -3706,8 +3712,8 @@ def test_get_owned_lists_response_structure(self): ) - def test_get_by_id_request_structure(self): - """Test get_by_id request structure.""" + def test_search_request_structure(self): + """Test search request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3720,11 +3726,11 @@ def test_get_by_id_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["query"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_by_id") + method = getattr(self.users_client, "search") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3759,7 +3765,7 @@ def test_get_by_id_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}" + expected_path = "/2/users/search" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -3775,12 +3781,12 @@ def test_get_by_id_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_by_id: {e}") + pytest.fail(f"Contract test failed for search: {e}") - def test_get_by_id_required_parameters(self): - """Test that get_by_id handles parameters correctly.""" - method = getattr(self.users_client, "get_by_id") + def test_search_required_parameters(self): + """Test that search handles parameters correctly.""" + method = getattr(self.users_client, "search") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -3794,8 +3800,8 @@ def test_get_by_id_required_parameters(self): method() - def test_get_by_id_response_structure(self): - """Test get_by_id response structure validation.""" + def test_search_response_structure(self): + """Test search response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -3808,10 +3814,10 @@ def test_get_by_id_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["query"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_by_id") + method = getattr(self.users_client, "search") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -3823,8 +3829,8 @@ def test_get_by_id_response_structure(self): ) - def test_unblock_dms_request_structure(self): - """Test unblock_dms request structure.""" + def test_get_blocking_request_structure(self): + """Test get_blocking request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3833,7 +3839,7 @@ def test_unblock_dms_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters @@ -3841,7 +3847,7 @@ def test_unblock_dms_request_structure(self): # Add request body if required # Call the method try: - method = getattr(self.users_client, "unblock_dms") + method = getattr(self.users_client, "get_blocking") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3860,7 +3866,7 @@ def test_unblock_dms_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -3869,14 +3875,14 @@ def test_unblock_dms_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/dm/unblock" + expected_path = "/2/users/{id}/blocking" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -3892,12 +3898,12 @@ def test_unblock_dms_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for unblock_dms: {e}") + pytest.fail(f"Contract test failed for get_blocking: {e}") - def test_unblock_dms_required_parameters(self): - """Test that unblock_dms handles parameters correctly.""" - method = getattr(self.users_client, "unblock_dms") + def test_get_blocking_required_parameters(self): + """Test that get_blocking handles parameters correctly.""" + method = getattr(self.users_client, "get_blocking") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -3905,14 +3911,14 @@ def test_unblock_dms_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_unblock_dms_response_structure(self): - """Test unblock_dms response structure validation.""" + def test_get_blocking_response_structure(self): + """Test get_blocking response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -3922,13 +3928,13 @@ def test_unblock_dms_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "unblock_dms") + method = getattr(self.users_client, "get_blocking") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -3940,8 +3946,8 @@ def test_unblock_dms_response_structure(self): ) - def test_search_request_structure(self): - """Test search request structure.""" + def test_get_timeline_request_structure(self): + """Test get_timeline request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -3954,11 +3960,11 @@ def test_search_request_structure(self): # Prepare test parameters kwargs = {} # Add required parameters - kwargs["query"] = "test_value" + kwargs["id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "search") + method = getattr(self.users_client, "get_timeline") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -3993,7 +3999,7 @@ def test_search_request_structure(self): called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/search" + expected_path = "/2/users/{id}/timelines/reverse_chronological" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -4009,12 +4015,12 @@ def test_search_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for search: {e}") + pytest.fail(f"Contract test failed for get_timeline: {e}") - def test_search_required_parameters(self): - """Test that search handles parameters correctly.""" - method = getattr(self.users_client, "search") + def test_get_timeline_required_parameters(self): + """Test that get_timeline handles parameters correctly.""" + method = getattr(self.users_client, "get_timeline") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -4028,8 +4034,8 @@ def test_search_required_parameters(self): method() - def test_search_response_structure(self): - """Test search response structure validation.""" + def test_get_timeline_response_structure(self): + """Test get_timeline response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -4042,10 +4048,10 @@ def test_search_response_structure(self): mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["query"] = "test" + kwargs["id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "search") + method = getattr(self.users_client, "get_timeline") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -4057,8 +4063,8 @@ def test_search_response_structure(self): ) - def test_get_muting_request_structure(self): - """Test get_muting request structure.""" + def test_unmute_user_request_structure(self): + """Test unmute_user request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -4067,15 +4073,16 @@ def test_get_muting_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["id"] = "test_value" + kwargs["source_user_id"] = "test_value" + kwargs["target_user_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.users_client, "get_muting") + method = getattr(self.users_client, "unmute_user") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -4094,7 +4101,7 @@ def test_get_muting_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -4103,14 +4110,14 @@ def test_get_muting_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/muting" + expected_path = "/2/users/{source_user_id}/muting/{target_user_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -4126,12 +4133,12 @@ def test_get_muting_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_muting: {e}") + pytest.fail(f"Contract test failed for unmute_user: {e}") - def test_get_muting_required_parameters(self): - """Test that get_muting handles parameters correctly.""" - method = getattr(self.users_client, "get_muting") + def test_unmute_user_required_parameters(self): + """Test that unmute_user handles parameters correctly.""" + method = getattr(self.users_client, "unmute_user") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -4139,14 +4146,14 @@ def test_get_muting_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_muting_response_structure(self): - """Test get_muting response structure validation.""" + def test_unmute_user_response_structure(self): + """Test unmute_user response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -4156,13 +4163,14 @@ def test_get_muting_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["id"] = "test" + kwargs["source_user_id"] = "test" + kwargs["target_user_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.users_client, "get_muting") + method = getattr(self.users_client, "unmute_user") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -4174,8 +4182,8 @@ def test_get_muting_response_structure(self): ) - def test_mute_user_request_structure(self): - """Test mute_user request structure.""" + def test_get_pinned_lists_request_structure(self): + """Test get_pinned_lists request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -4184,19 +4192,15 @@ def test_mute_user_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import MuteUserRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = MuteUserRequest() # Call the method try: - method = getattr(self.users_client, "mute_user") + method = getattr(self.users_client, "get_pinned_lists") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -4215,7 +4219,7 @@ def test_mute_user_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -4224,14 +4228,14 @@ def test_mute_user_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/muting" + expected_path = "/2/users/{id}/pinned_lists" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -4247,12 +4251,12 @@ def test_mute_user_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for mute_user: {e}") + pytest.fail(f"Contract test failed for get_pinned_lists: {e}") - def test_mute_user_required_parameters(self): - """Test that mute_user handles parameters correctly.""" - method = getattr(self.users_client, "mute_user") + def test_get_pinned_lists_required_parameters(self): + """Test that get_pinned_lists handles parameters correctly.""" + method = getattr(self.users_client, "get_pinned_lists") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -4260,14 +4264,14 @@ def test_mute_user_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_mute_user_response_structure(self): - """Test mute_user response structure validation.""" + def test_get_pinned_lists_response_structure(self): + """Test get_pinned_lists response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -4277,17 +4281,13 @@ def test_mute_user_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import MuteUserRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = MuteUserRequest() # Call method and verify response structure - method = getattr(self.users_client, "mute_user") + method = getattr(self.users_client, "get_pinned_lists") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -4299,8 +4299,8 @@ def test_mute_user_response_structure(self): ) - def test_get_bookmarks_request_structure(self): - """Test get_bookmarks request structure.""" + def test_pin_list_request_structure(self): + """Test pin_list request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -4309,15 +4309,19 @@ def test_get_bookmarks_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import PinListRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = PinListRequest() # Call the method try: - method = getattr(self.users_client, "get_bookmarks") + method = getattr(self.users_client, "pin_list") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -4336,7 +4340,7 @@ def test_get_bookmarks_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -4345,14 +4349,14 @@ def test_get_bookmarks_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/bookmarks" + expected_path = "/2/users/{id}/pinned_lists" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -4368,12 +4372,12 @@ def test_get_bookmarks_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_bookmarks: {e}") + pytest.fail(f"Contract test failed for pin_list: {e}") - def test_get_bookmarks_required_parameters(self): - """Test that get_bookmarks handles parameters correctly.""" - method = getattr(self.users_client, "get_bookmarks") + def test_pin_list_required_parameters(self): + """Test that pin_list handles parameters correctly.""" + method = getattr(self.users_client, "pin_list") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -4381,14 +4385,14 @@ def test_get_bookmarks_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_get_bookmarks_response_structure(self): - """Test get_bookmarks response structure validation.""" + def test_pin_list_response_structure(self): + """Test pin_list response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -4398,13 +4402,17 @@ def test_get_bookmarks_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.users.models import PinListRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = PinListRequest() # Call method and verify response structure - method = getattr(self.users_client, "get_bookmarks") + method = getattr(self.users_client, "pin_list") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -4416,8 +4424,8 @@ def test_get_bookmarks_response_structure(self): ) - def test_create_bookmark_request_structure(self): - """Test create_bookmark request structure.""" + def test_get_followers_request_structure(self): + """Test get_followers request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -4426,19 +4434,15 @@ def test_create_bookmark_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters kwargs["id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import CreateBookmarkRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateBookmarkRequest() # Call the method try: - method = getattr(self.users_client, "create_bookmark") + method = getattr(self.users_client, "get_followers") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -4457,7 +4461,7 @@ def test_create_bookmark_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -4466,14 +4470,14 @@ def test_create_bookmark_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/users/{id}/bookmarks" + expected_path = "/2/users/{id}/followers" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -4489,12 +4493,12 @@ def test_create_bookmark_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create_bookmark: {e}") + pytest.fail(f"Contract test failed for get_followers: {e}") - def test_create_bookmark_required_parameters(self): - """Test that create_bookmark handles parameters correctly.""" - method = getattr(self.users_client, "create_bookmark") + def test_get_followers_required_parameters(self): + """Test that get_followers handles parameters correctly.""" + method = getattr(self.users_client, "get_followers") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -4502,14 +4506,14 @@ def test_create_bookmark_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_bookmark_response_structure(self): - """Test create_bookmark response structure validation.""" + def test_get_followers_response_structure(self): + """Test get_followers response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -4519,17 +4523,13 @@ def test_create_bookmark_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} kwargs["id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.users.models import CreateBookmarkRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateBookmarkRequest() # Call method and verify response structure - method = getattr(self.users_client, "create_bookmark") + method = getattr(self.users_client, "get_followers") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/users/test_pagination.py b/tests/users/test_pagination.py index a417346..b99a717 100644 --- a/tests/users/test_pagination.py +++ b/tests/users/test_pagination.py @@ -27,20 +27,20 @@ def setup_class(self): self.users_client = getattr(self.client, "users") - def test_get_liked_posts_cursor_creation(self): - """Test that get_liked_posts can be used with Cursor.""" - method = getattr(self.users_client, "get_liked_posts") + def test_get_bookmarks_cursor_creation(self): + """Test that get_bookmarks can be used with Cursor.""" + method = getattr(self.users_client, "get_bookmarks") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_liked_posts should support pagination") + pytest.fail(f"Method get_bookmarks should support pagination") - def test_get_liked_posts_cursor_pages(self): - """Test pagination with pages() for get_liked_posts.""" + def test_get_bookmarks_cursor_pages(self): + """Test pagination with pages() for get_bookmarks.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -61,7 +61,7 @@ def test_get_liked_posts_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_liked_posts") + method = getattr(self.users_client, "get_bookmarks") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -77,8 +77,8 @@ def test_get_liked_posts_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_liked_posts_cursor_items(self): - """Test pagination with items() for get_liked_posts.""" + def test_get_bookmarks_cursor_items(self): + """Test pagination with items() for get_bookmarks.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -97,7 +97,7 @@ def test_get_liked_posts_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_liked_posts") + method = getattr(self.users_client, "get_bookmarks") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -108,15 +108,15 @@ def test_get_liked_posts_cursor_items(self): ), "Items should have 'id' field" - def test_get_liked_posts_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_liked_posts.""" + def test_get_bookmarks_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_bookmarks.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_liked_posts") + method = getattr(self.users_client, "get_bookmarks") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -169,20 +169,20 @@ def test_get_liked_posts_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_following_cursor_creation(self): - """Test that get_following can be used with Cursor.""" - method = getattr(self.users_client, "get_following") + def test_get_mentions_cursor_creation(self): + """Test that get_mentions can be used with Cursor.""" + method = getattr(self.users_client, "get_mentions") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_following should support pagination") + pytest.fail(f"Method get_mentions should support pagination") - def test_get_following_cursor_pages(self): - """Test pagination with pages() for get_following.""" + def test_get_mentions_cursor_pages(self): + """Test pagination with pages() for get_mentions.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -203,7 +203,7 @@ def test_get_following_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_following") + method = getattr(self.users_client, "get_mentions") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -219,8 +219,8 @@ def test_get_following_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_following_cursor_items(self): - """Test pagination with items() for get_following.""" + def test_get_mentions_cursor_items(self): + """Test pagination with items() for get_mentions.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -239,7 +239,7 @@ def test_get_following_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_following") + method = getattr(self.users_client, "get_mentions") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -250,15 +250,15 @@ def test_get_following_cursor_items(self): ), "Items should have 'id' field" - def test_get_following_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_following.""" + def test_get_mentions_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_mentions.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_following") + method = getattr(self.users_client, "get_mentions") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -311,20 +311,20 @@ def test_get_following_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_blocking_cursor_creation(self): - """Test that get_blocking can be used with Cursor.""" - method = getattr(self.users_client, "get_blocking") + def test_get_owned_lists_cursor_creation(self): + """Test that get_owned_lists can be used with Cursor.""" + method = getattr(self.users_client, "get_owned_lists") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_blocking should support pagination") + pytest.fail(f"Method get_owned_lists should support pagination") - def test_get_blocking_cursor_pages(self): - """Test pagination with pages() for get_blocking.""" + def test_get_owned_lists_cursor_pages(self): + """Test pagination with pages() for get_owned_lists.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -345,7 +345,7 @@ def test_get_blocking_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_blocking") + method = getattr(self.users_client, "get_owned_lists") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -361,8 +361,8 @@ def test_get_blocking_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_blocking_cursor_items(self): - """Test pagination with items() for get_blocking.""" + def test_get_owned_lists_cursor_items(self): + """Test pagination with items() for get_owned_lists.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -381,7 +381,7 @@ def test_get_blocking_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_blocking") + method = getattr(self.users_client, "get_owned_lists") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -392,15 +392,15 @@ def test_get_blocking_cursor_items(self): ), "Items should have 'id' field" - def test_get_blocking_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_blocking.""" + def test_get_owned_lists_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_owned_lists.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_blocking") + method = getattr(self.users_client, "get_owned_lists") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -453,20 +453,20 @@ def test_get_blocking_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_followers_cursor_creation(self): - """Test that get_followers can be used with Cursor.""" - method = getattr(self.users_client, "get_followers") + def test_get_following_cursor_creation(self): + """Test that get_following can be used with Cursor.""" + method = getattr(self.users_client, "get_following") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_followers should support pagination") + pytest.fail(f"Method get_following should support pagination") - def test_get_followers_cursor_pages(self): - """Test pagination with pages() for get_followers.""" + def test_get_following_cursor_pages(self): + """Test pagination with pages() for get_following.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -487,7 +487,7 @@ def test_get_followers_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_followers") + method = getattr(self.users_client, "get_following") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -503,8 +503,8 @@ def test_get_followers_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_followers_cursor_items(self): - """Test pagination with items() for get_followers.""" + def test_get_following_cursor_items(self): + """Test pagination with items() for get_following.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -523,7 +523,7 @@ def test_get_followers_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_followers") + method = getattr(self.users_client, "get_following") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -534,15 +534,15 @@ def test_get_followers_cursor_items(self): ), "Items should have 'id' field" - def test_get_followers_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_followers.""" + def test_get_following_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_following.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_followers") + method = getattr(self.users_client, "get_following") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -595,20 +595,20 @@ def test_get_followers_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_mentions_cursor_creation(self): - """Test that get_mentions can be used with Cursor.""" - method = getattr(self.users_client, "get_mentions") + def test_get_liked_posts_cursor_creation(self): + """Test that get_liked_posts can be used with Cursor.""" + method = getattr(self.users_client, "get_liked_posts") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_mentions should support pagination") + pytest.fail(f"Method get_liked_posts should support pagination") - def test_get_mentions_cursor_pages(self): - """Test pagination with pages() for get_mentions.""" + def test_get_liked_posts_cursor_pages(self): + """Test pagination with pages() for get_liked_posts.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -629,7 +629,7 @@ def test_get_mentions_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_mentions") + method = getattr(self.users_client, "get_liked_posts") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -645,8 +645,8 @@ def test_get_mentions_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_mentions_cursor_items(self): - """Test pagination with items() for get_mentions.""" + def test_get_liked_posts_cursor_items(self): + """Test pagination with items() for get_liked_posts.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -665,7 +665,7 @@ def test_get_mentions_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_mentions") + method = getattr(self.users_client, "get_liked_posts") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -676,15 +676,15 @@ def test_get_mentions_cursor_items(self): ), "Items should have 'id' field" - def test_get_mentions_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_mentions.""" + def test_get_liked_posts_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_liked_posts.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_mentions") + method = getattr(self.users_client, "get_liked_posts") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -737,20 +737,20 @@ def test_get_mentions_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_bookmark_folders_cursor_creation(self): - """Test that get_bookmark_folders can be used with Cursor.""" - method = getattr(self.users_client, "get_bookmark_folders") + def test_get_reposts_of_me_cursor_creation(self): + """Test that get_reposts_of_me can be used with Cursor.""" + method = getattr(self.users_client, "get_reposts_of_me") # Should be able to create cursor without error try: - test_cursor = cursor(method, "test_value", max_results=10) + test_cursor = cursor(method, max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_bookmark_folders should support pagination") + pytest.fail(f"Method get_reposts_of_me should support pagination") - def test_get_bookmark_folders_cursor_pages(self): - """Test pagination with pages() for get_bookmark_folders.""" + def test_get_reposts_of_me_cursor_pages(self): + """Test pagination with pages() for get_reposts_of_me.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -771,8 +771,8 @@ def test_get_bookmark_folders_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_bookmark_folders") - test_cursor = cursor(method, "test_value", max_results=2) + method = getattr(self.users_client, "get_reposts_of_me") + test_cursor = cursor(method, max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" # Verify first page @@ -787,8 +787,8 @@ def test_get_bookmark_folders_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_bookmark_folders_cursor_items(self): - """Test pagination with items() for get_bookmark_folders.""" + def test_get_reposts_of_me_cursor_items(self): + """Test pagination with items() for get_reposts_of_me.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -807,8 +807,8 @@ def test_get_bookmark_folders_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_bookmark_folders") - test_cursor = cursor(method, "test_value", max_results=10) + method = getattr(self.users_client, "get_reposts_of_me") + test_cursor = cursor(method, max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" # Verify items have expected structure @@ -818,17 +818,17 @@ def test_get_bookmark_folders_cursor_items(self): ), "Items should have 'id' field" - def test_get_bookmark_folders_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_bookmark_folders.""" + def test_get_reposts_of_me_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_reposts_of_me.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_bookmark_folders") + method = getattr(self.users_client, "get_reposts_of_me") # Test with max_results parameter - test_cursor = cursor(method, "test_value", max_results=5) + test_cursor = cursor(method, max_results=5) list(test_cursor.pages(1)) # Trigger one request # Verify max_results was passed in request call_args = mock_session.get.call_args @@ -857,7 +857,7 @@ def test_get_bookmark_folders_pagination_parameters(self): mock_response_with_token, second_page_response, ] - test_cursor = cursor(method, "test_value", max_results=1) + test_cursor = cursor(method, max_results=1) pages = list(test_cursor.pages(2)) # Should have made 2 requests assert ( @@ -879,20 +879,20 @@ def test_get_bookmark_folders_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_reposts_of_me_cursor_creation(self): - """Test that get_reposts_of_me can be used with Cursor.""" - method = getattr(self.users_client, "get_reposts_of_me") + def test_get_bookmark_folders_cursor_creation(self): + """Test that get_bookmark_folders can be used with Cursor.""" + method = getattr(self.users_client, "get_bookmark_folders") # Should be able to create cursor without error try: - test_cursor = cursor(method, max_results=10) + test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_reposts_of_me should support pagination") + pytest.fail(f"Method get_bookmark_folders should support pagination") - def test_get_reposts_of_me_cursor_pages(self): - """Test pagination with pages() for get_reposts_of_me.""" + def test_get_bookmark_folders_cursor_pages(self): + """Test pagination with pages() for get_bookmark_folders.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -913,8 +913,8 @@ def test_get_reposts_of_me_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_reposts_of_me") - test_cursor = cursor(method, max_results=2) + method = getattr(self.users_client, "get_bookmark_folders") + test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" # Verify first page @@ -929,8 +929,8 @@ def test_get_reposts_of_me_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_reposts_of_me_cursor_items(self): - """Test pagination with items() for get_reposts_of_me.""" + def test_get_bookmark_folders_cursor_items(self): + """Test pagination with items() for get_bookmark_folders.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -949,8 +949,8 @@ def test_get_reposts_of_me_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_reposts_of_me") - test_cursor = cursor(method, max_results=10) + method = getattr(self.users_client, "get_bookmark_folders") + test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" # Verify items have expected structure @@ -960,17 +960,17 @@ def test_get_reposts_of_me_cursor_items(self): ), "Items should have 'id' field" - def test_get_reposts_of_me_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_reposts_of_me.""" + def test_get_bookmark_folders_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_bookmark_folders.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_reposts_of_me") + method = getattr(self.users_client, "get_bookmark_folders") # Test with max_results parameter - test_cursor = cursor(method, max_results=5) + test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request # Verify max_results was passed in request call_args = mock_session.get.call_args @@ -999,7 +999,7 @@ def test_get_reposts_of_me_pagination_parameters(self): mock_response_with_token, second_page_response, ] - test_cursor = cursor(method, max_results=1) + test_cursor = cursor(method, "test_value", max_results=1) pages = list(test_cursor.pages(2)) # Should have made 2 requests assert ( @@ -1163,20 +1163,20 @@ def test_get_list_memberships_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_timeline_cursor_creation(self): - """Test that get_timeline can be used with Cursor.""" - method = getattr(self.users_client, "get_timeline") + def test_get_muting_cursor_creation(self): + """Test that get_muting can be used with Cursor.""" + method = getattr(self.users_client, "get_muting") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_timeline should support pagination") + pytest.fail(f"Method get_muting should support pagination") - def test_get_timeline_cursor_pages(self): - """Test pagination with pages() for get_timeline.""" + def test_get_muting_cursor_pages(self): + """Test pagination with pages() for get_muting.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -1197,7 +1197,7 @@ def test_get_timeline_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_timeline") + method = getattr(self.users_client, "get_muting") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -1213,8 +1213,8 @@ def test_get_timeline_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_timeline_cursor_items(self): - """Test pagination with items() for get_timeline.""" + def test_get_muting_cursor_items(self): + """Test pagination with items() for get_muting.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -1233,7 +1233,7 @@ def test_get_timeline_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_timeline") + method = getattr(self.users_client, "get_muting") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -1244,15 +1244,15 @@ def test_get_timeline_cursor_items(self): ), "Items should have 'id' field" - def test_get_timeline_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_timeline.""" + def test_get_muting_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_muting.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_timeline") + method = getattr(self.users_client, "get_muting") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -1305,20 +1305,20 @@ def test_get_timeline_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_posts_cursor_creation(self): - """Test that get_posts can be used with Cursor.""" - method = getattr(self.users_client, "get_posts") + def test_get_followed_lists_cursor_creation(self): + """Test that get_followed_lists can be used with Cursor.""" + method = getattr(self.users_client, "get_followed_lists") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_posts should support pagination") + pytest.fail(f"Method get_followed_lists should support pagination") - def test_get_posts_cursor_pages(self): - """Test pagination with pages() for get_posts.""" + def test_get_followed_lists_cursor_pages(self): + """Test pagination with pages() for get_followed_lists.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -1339,7 +1339,7 @@ def test_get_posts_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_posts") + method = getattr(self.users_client, "get_followed_lists") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -1355,8 +1355,8 @@ def test_get_posts_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_posts_cursor_items(self): - """Test pagination with items() for get_posts.""" + def test_get_followed_lists_cursor_items(self): + """Test pagination with items() for get_followed_lists.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -1375,7 +1375,7 @@ def test_get_posts_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_posts") + method = getattr(self.users_client, "get_followed_lists") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -1386,15 +1386,15 @@ def test_get_posts_cursor_items(self): ), "Items should have 'id' field" - def test_get_posts_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_posts.""" + def test_get_followed_lists_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_followed_lists.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_posts") + method = getattr(self.users_client, "get_followed_lists") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -1447,20 +1447,20 @@ def test_get_posts_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_followed_lists_cursor_creation(self): - """Test that get_followed_lists can be used with Cursor.""" - method = getattr(self.users_client, "get_followed_lists") + def test_get_posts_cursor_creation(self): + """Test that get_posts can be used with Cursor.""" + method = getattr(self.users_client, "get_posts") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_followed_lists should support pagination") + pytest.fail(f"Method get_posts should support pagination") - def test_get_followed_lists_cursor_pages(self): - """Test pagination with pages() for get_followed_lists.""" + def test_get_posts_cursor_pages(self): + """Test pagination with pages() for get_posts.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -1481,7 +1481,7 @@ def test_get_followed_lists_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_followed_lists") + method = getattr(self.users_client, "get_posts") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -1497,8 +1497,8 @@ def test_get_followed_lists_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_followed_lists_cursor_items(self): - """Test pagination with items() for get_followed_lists.""" + def test_get_posts_cursor_items(self): + """Test pagination with items() for get_posts.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -1517,7 +1517,7 @@ def test_get_followed_lists_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_followed_lists") + method = getattr(self.users_client, "get_posts") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -1528,15 +1528,15 @@ def test_get_followed_lists_cursor_items(self): ), "Items should have 'id' field" - def test_get_followed_lists_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_followed_lists.""" + def test_get_posts_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_posts.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_followed_lists") + method = getattr(self.users_client, "get_posts") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -1589,20 +1589,20 @@ def test_get_followed_lists_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_owned_lists_cursor_creation(self): - """Test that get_owned_lists can be used with Cursor.""" - method = getattr(self.users_client, "get_owned_lists") + def test_search_cursor_creation(self): + """Test that search can be used with Cursor.""" + method = getattr(self.users_client, "search") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_owned_lists should support pagination") + pytest.fail(f"Method search should support pagination") - def test_get_owned_lists_cursor_pages(self): - """Test pagination with pages() for get_owned_lists.""" + def test_search_cursor_pages(self): + """Test pagination with pages() for search.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -1623,7 +1623,7 @@ def test_get_owned_lists_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_owned_lists") + method = getattr(self.users_client, "search") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -1639,8 +1639,8 @@ def test_get_owned_lists_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_owned_lists_cursor_items(self): - """Test pagination with items() for get_owned_lists.""" + def test_search_cursor_items(self): + """Test pagination with items() for search.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -1659,7 +1659,7 @@ def test_get_owned_lists_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_owned_lists") + method = getattr(self.users_client, "search") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -1670,15 +1670,15 @@ def test_get_owned_lists_cursor_items(self): ), "Items should have 'id' field" - def test_get_owned_lists_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_owned_lists.""" + def test_search_pagination_parameters(self): + """Test that pagination parameters are handled correctly for search.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_owned_lists") + method = getattr(self.users_client, "search") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -1724,27 +1724,27 @@ def test_get_owned_lists_pagination_parameters(self): ): second_params = second_call_args[1]["params"] assert ( - "pagination_token" in second_params - ), "Second request should include pagination_token" + "next_token" in second_params + ), "Second request should include next_token" assert ( - second_params["pagination_token"] == "next_token_value" + second_params["next_token"] == "next_token_value" ), "Pagination token should be passed correctly" - def test_search_cursor_creation(self): - """Test that search can be used with Cursor.""" - method = getattr(self.users_client, "search") + def test_get_blocking_cursor_creation(self): + """Test that get_blocking can be used with Cursor.""" + method = getattr(self.users_client, "get_blocking") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method search should support pagination") + pytest.fail(f"Method get_blocking should support pagination") - def test_search_cursor_pages(self): - """Test pagination with pages() for search.""" + def test_get_blocking_cursor_pages(self): + """Test pagination with pages() for get_blocking.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -1765,7 +1765,7 @@ def test_search_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "search") + method = getattr(self.users_client, "get_blocking") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -1781,8 +1781,8 @@ def test_search_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_search_cursor_items(self): - """Test pagination with items() for search.""" + def test_get_blocking_cursor_items(self): + """Test pagination with items() for get_blocking.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -1801,7 +1801,7 @@ def test_search_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "search") + method = getattr(self.users_client, "get_blocking") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -1812,15 +1812,15 @@ def test_search_cursor_items(self): ), "Items should have 'id' field" - def test_search_pagination_parameters(self): - """Test that pagination parameters are handled correctly for search.""" + def test_get_blocking_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_blocking.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "search") + method = getattr(self.users_client, "get_blocking") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -1866,27 +1866,27 @@ def test_search_pagination_parameters(self): ): second_params = second_call_args[1]["params"] assert ( - "next_token" in second_params - ), "Second request should include next_token" + "pagination_token" in second_params + ), "Second request should include pagination_token" assert ( - second_params["next_token"] == "next_token_value" + second_params["pagination_token"] == "next_token_value" ), "Pagination token should be passed correctly" - def test_get_muting_cursor_creation(self): - """Test that get_muting can be used with Cursor.""" - method = getattr(self.users_client, "get_muting") + def test_get_timeline_cursor_creation(self): + """Test that get_timeline can be used with Cursor.""" + method = getattr(self.users_client, "get_timeline") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_muting should support pagination") + pytest.fail(f"Method get_timeline should support pagination") - def test_get_muting_cursor_pages(self): - """Test pagination with pages() for get_muting.""" + def test_get_timeline_cursor_pages(self): + """Test pagination with pages() for get_timeline.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -1907,7 +1907,7 @@ def test_get_muting_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_muting") + method = getattr(self.users_client, "get_timeline") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -1923,8 +1923,8 @@ def test_get_muting_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_muting_cursor_items(self): - """Test pagination with items() for get_muting.""" + def test_get_timeline_cursor_items(self): + """Test pagination with items() for get_timeline.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -1943,7 +1943,7 @@ def test_get_muting_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_muting") + method = getattr(self.users_client, "get_timeline") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -1954,15 +1954,15 @@ def test_get_muting_cursor_items(self): ), "Items should have 'id' field" - def test_get_muting_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_muting.""" + def test_get_timeline_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_timeline.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_muting") + method = getattr(self.users_client, "get_timeline") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -2015,20 +2015,20 @@ def test_get_muting_pagination_parameters(self): ), "Pagination token should be passed correctly" - def test_get_bookmarks_cursor_creation(self): - """Test that get_bookmarks can be used with Cursor.""" - method = getattr(self.users_client, "get_bookmarks") + def test_get_followers_cursor_creation(self): + """Test that get_followers can be used with Cursor.""" + method = getattr(self.users_client, "get_followers") # Should be able to create cursor without error try: test_cursor = cursor(method, "test_value", max_results=10) assert test_cursor is not None assert isinstance(test_cursor, Cursor) except PaginationError: - pytest.fail(f"Method get_bookmarks should support pagination") + pytest.fail(f"Method get_followers should support pagination") - def test_get_bookmarks_cursor_pages(self): - """Test pagination with pages() for get_bookmarks.""" + def test_get_followers_cursor_pages(self): + """Test pagination with pages() for get_followers.""" with patch.object(self.client, "session") as mock_session: # Mock first page response first_page_response = Mock() @@ -2049,7 +2049,7 @@ def test_get_bookmarks_cursor_pages(self): # Return different responses for consecutive calls mock_session.get.side_effect = [first_page_response, second_page_response] # Test pagination - method = getattr(self.users_client, "get_bookmarks") + method = getattr(self.users_client, "get_followers") test_cursor = cursor(method, "test_value", max_results=2) pages = list(test_cursor.pages(2)) # Limit to 2 pages assert len(pages) == 2, f"Should get 2 pages, got {len(pages)}" @@ -2065,8 +2065,8 @@ def test_get_bookmarks_cursor_pages(self): assert len(second_data) == 1, "Second page should have 1 item" - def test_get_bookmarks_cursor_items(self): - """Test pagination with items() for get_bookmarks.""" + def test_get_followers_cursor_items(self): + """Test pagination with items() for get_followers.""" with patch.object(self.client, "session") as mock_session: # Mock response with paginated data mock_response = Mock() @@ -2085,7 +2085,7 @@ def test_get_bookmarks_cursor_items(self): mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response # Test item iteration - method = getattr(self.users_client, "get_bookmarks") + method = getattr(self.users_client, "get_followers") test_cursor = cursor(method, "test_value", max_results=10) items = list(test_cursor.items(5)) # Limit to 5 items assert len(items) == 3, f"Should get 3 items, got {len(items)}" @@ -2096,15 +2096,15 @@ def test_get_bookmarks_cursor_items(self): ), "Items should have 'id' field" - def test_get_bookmarks_pagination_parameters(self): - """Test that pagination parameters are handled correctly for get_bookmarks.""" + def test_get_followers_pagination_parameters(self): + """Test that pagination parameters are handled correctly for get_followers.""" with patch.object(self.client, "session") as mock_session: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": [], "meta": {"result_count": 0}} mock_response.raise_for_status.return_value = None mock_session.get.return_value = mock_response - method = getattr(self.users_client, "get_bookmarks") + method = getattr(self.users_client, "get_followers") # Test with max_results parameter test_cursor = cursor(method, "test_value", max_results=5) list(test_cursor.pages(1)) # Trigger one request @@ -2167,7 +2167,7 @@ def test_pagination_edge_cases(self): empty_response.raise_for_status.return_value = None mock_session.get.return_value = empty_response # Pick first paginatable method for testing - method = getattr(self.users_client, "get_liked_posts") + method = getattr(self.users_client, "get_bookmarks") test_cursor = cursor(method, "test_value", max_results=10) # Should handle empty responses gracefully pages = list(test_cursor.pages(1)) diff --git a/tests/users/test_structure.py b/tests/users/test_structure.py index 5630dfb..fd61573 100644 --- a/tests/users/test_structure.py +++ b/tests/users/test_structure.py @@ -28,34 +28,39 @@ def setup_class(self): self.users_client = getattr(self.client, "users") - def test_get_by_ids_exists(self): - """Test that get_by_ids method exists with correct signature.""" + def test_get_bookmarks_exists(self): + """Test that get_bookmarks method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_by_ids", None) - assert method is not None, f"Method get_by_ids does not exist on UsersClient" + method = getattr(UsersClient, "get_bookmarks", None) + assert method is not None, f"Method get_bookmarks does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_by_ids is not callable" + assert callable(method), f"get_bookmarks is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_by_ids should have at least 'self' parameter" + assert len(params) >= 1, f"get_bookmarks should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "ids", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_ids" + ), f"Required parameter '{required_param}' missing from get_bookmarks" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "user.fields", - "expansions", + "max_results", + "pagination_token", "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -65,40 +70,62 @@ def test_get_by_ids_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_ids_return_annotation(self): - """Test that get_by_ids has proper return type annotation.""" - method = getattr(UsersClient, "get_by_ids") + def test_get_bookmarks_return_annotation(self): + """Test that get_bookmarks has proper return type annotation.""" + method = getattr(UsersClient, "get_bookmarks") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_ids should have return type annotation" + ), f"Method get_bookmarks should have return type annotation" - def test_unfollow_user_exists(self): - """Test that unfollow_user method exists with correct signature.""" + def test_get_bookmarks_pagination_params(self): + """Test that get_bookmarks has pagination parameters.""" + method = getattr(UsersClient, "get_bookmarks") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_bookmarks should have pagination parameters" + + + def test_create_bookmark_exists(self): + """Test that create_bookmark method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "unfollow_user", None) - assert method is not None, f"Method unfollow_user does not exist on UsersClient" + method = getattr(UsersClient, "create_bookmark", None) + assert ( + method is not None + ), f"Method create_bookmark does not exist on UsersClient" # Check method is callable - assert callable(method), f"unfollow_user is not callable" + assert callable(method), f"create_bookmark is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"unfollow_user should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"create_bookmark should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "source_user_id", - "target_user_id", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from unfollow_user" + ), f"Required parameter '{required_param}' missing from create_bookmark" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -109,32 +136,28 @@ def test_unfollow_user_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_unfollow_user_return_annotation(self): - """Test that unfollow_user has proper return type annotation.""" - method = getattr(UsersClient, "unfollow_user") + def test_create_bookmark_return_annotation(self): + """Test that create_bookmark has proper return type annotation.""" + method = getattr(UsersClient, "create_bookmark") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method unfollow_user should have return type annotation" + ), f"Method create_bookmark should have return type annotation" - def test_get_liked_posts_exists(self): - """Test that get_liked_posts method exists with correct signature.""" + def test_get_mentions_exists(self): + """Test that get_mentions method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_liked_posts", None) - assert ( - method is not None - ), f"Method get_liked_posts does not exist on UsersClient" + method = getattr(UsersClient, "get_mentions", None) + assert method is not None, f"Method get_mentions does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_liked_posts is not callable" + assert callable(method), f"get_mentions is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_liked_posts should have at least 'self' parameter" + assert len(params) >= 1, f"get_mentions should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -145,11 +168,15 @@ def test_get_liked_posts_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_liked_posts" + ), f"Required parameter '{required_param}' missing from get_mentions" # Check optional parameters have defaults (excluding 'self') optional_params = [ + "since_id", + "until_id", "max_results", "pagination_token", + "start_time", + "end_time", "tweet.fields", "expansions", "media.fields", @@ -165,19 +192,19 @@ def test_get_liked_posts_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_liked_posts_return_annotation(self): - """Test that get_liked_posts has proper return type annotation.""" - method = getattr(UsersClient, "get_liked_posts") + def test_get_mentions_return_annotation(self): + """Test that get_mentions has proper return type annotation.""" + method = getattr(UsersClient, "get_mentions") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_liked_posts should have return type annotation" + ), f"Method get_mentions should have return type annotation" - def test_get_liked_posts_pagination_params(self): - """Test that get_liked_posts has pagination parameters.""" - method = getattr(UsersClient, "get_liked_posts") + def test_get_mentions_pagination_params(self): + """Test that get_mentions has pagination parameters.""" + method = getattr(UsersClient, "get_mentions") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -191,65 +218,25 @@ def test_get_liked_posts_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_liked_posts should have pagination parameters" + ), f"Paginated method get_mentions should have pagination parameters" - def test_unfollow_list_exists(self): - """Test that unfollow_list method exists with correct signature.""" + def test_get_owned_lists_exists(self): + """Test that get_owned_lists method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "unfollow_list", None) - assert method is not None, f"Method unfollow_list does not exist on UsersClient" - # Check method is callable - assert callable(method), f"unfollow_list is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"unfollow_list should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [ - "id", - "list_id", - ] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from unfollow_list" - # Check optional parameters have defaults (excluding 'self') - optional_params = [] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_unfollow_list_return_annotation(self): - """Test that unfollow_list has proper return type annotation.""" - method = getattr(UsersClient, "unfollow_list") - sig = inspect.signature(method) - # Check return annotation exists + method = getattr(UsersClient, "get_owned_lists", None) assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method unfollow_list should have return type annotation" - - - def test_get_following_exists(self): - """Test that get_following method exists with correct signature.""" - # Check method exists - method = getattr(UsersClient, "get_following", None) - assert method is not None, f"Method get_following does not exist on UsersClient" + method is not None + ), f"Method get_owned_lists does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_following is not callable" + assert callable(method), f"get_owned_lists is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_following should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_owned_lists should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -260,14 +247,14 @@ def test_get_following_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_following" + ), f"Required parameter '{required_param}' missing from get_owned_lists" # Check optional parameters have defaults (excluding 'self') optional_params = [ "max_results", "pagination_token", - "user.fields", + "list.fields", "expansions", - "tweet.fields", + "user.fields", ] for optional_param in optional_params: if optional_param in params: @@ -277,19 +264,19 @@ def test_get_following_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_following_return_annotation(self): - """Test that get_following has proper return type annotation.""" - method = getattr(UsersClient, "get_following") + def test_get_owned_lists_return_annotation(self): + """Test that get_owned_lists has proper return type annotation.""" + method = getattr(UsersClient, "get_owned_lists") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_following should have return type annotation" + ), f"Method get_owned_lists should have return type annotation" - def test_get_following_pagination_params(self): - """Test that get_following has pagination parameters.""" - method = getattr(UsersClient, "get_following") + def test_get_owned_lists_pagination_params(self): + """Test that get_owned_lists has pagination parameters.""" + method = getattr(UsersClient, "get_owned_lists") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -303,32 +290,33 @@ def test_get_following_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_following should have pagination parameters" + ), f"Paginated method get_owned_lists should have pagination parameters" - def test_follow_user_exists(self): - """Test that follow_user method exists with correct signature.""" + def test_unpin_list_exists(self): + """Test that unpin_list method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "follow_user", None) - assert method is not None, f"Method follow_user does not exist on UsersClient" + method = getattr(UsersClient, "unpin_list", None) + assert method is not None, f"Method unpin_list does not exist on UsersClient" # Check method is callable - assert callable(method), f"follow_user is not callable" + assert callable(method), f"unpin_list is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"follow_user should have at least 'self' parameter" + assert len(params) >= 1, f"unpin_list should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ "id", + "list_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from follow_user" + ), f"Required parameter '{required_param}' missing from unpin_list" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -339,40 +327,39 @@ def test_follow_user_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_follow_user_return_annotation(self): - """Test that follow_user has proper return type annotation.""" - method = getattr(UsersClient, "follow_user") + def test_unpin_list_return_annotation(self): + """Test that unpin_list has proper return type annotation.""" + method = getattr(UsersClient, "unpin_list") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method follow_user should have return type annotation" + ), f"Method unpin_list should have return type annotation" - def test_unmute_user_exists(self): - """Test that unmute_user method exists with correct signature.""" + def test_block_dms_exists(self): + """Test that block_dms method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "unmute_user", None) - assert method is not None, f"Method unmute_user does not exist on UsersClient" + method = getattr(UsersClient, "block_dms", None) + assert method is not None, f"Method block_dms does not exist on UsersClient" # Check method is callable - assert callable(method), f"unmute_user is not callable" + assert callable(method), f"block_dms is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"unmute_user should have at least 'self' parameter" + assert len(params) >= 1, f"block_dms should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "source_user_id", - "target_user_id", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from unmute_user" + ), f"Required parameter '{required_param}' missing from block_dms" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -383,14 +370,14 @@ def test_unmute_user_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_unmute_user_return_annotation(self): - """Test that unmute_user has proper return type annotation.""" - method = getattr(UsersClient, "unmute_user") + def test_block_dms_return_annotation(self): + """Test that block_dms has proper return type annotation.""" + method = getattr(UsersClient, "block_dms") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method unmute_user should have return type annotation" + ), f"Method block_dms should have return type annotation" def test_repost_post_exists(self): @@ -436,18 +423,18 @@ def test_repost_post_return_annotation(self): ), f"Method repost_post should have return type annotation" - def test_get_blocking_exists(self): - """Test that get_blocking method exists with correct signature.""" + def test_get_following_exists(self): + """Test that get_following method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_blocking", None) - assert method is not None, f"Method get_blocking does not exist on UsersClient" + method = getattr(UsersClient, "get_following", None) + assert method is not None, f"Method get_following does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_blocking is not callable" + assert callable(method), f"get_following is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_blocking should have at least 'self' parameter" + assert len(params) >= 1, f"get_following should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -458,7 +445,7 @@ def test_get_blocking_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_blocking" + ), f"Required parameter '{required_param}' missing from get_following" # Check optional parameters have defaults (excluding 'self') optional_params = [ "max_results", @@ -475,19 +462,19 @@ def test_get_blocking_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_blocking_return_annotation(self): - """Test that get_blocking has proper return type annotation.""" - method = getattr(UsersClient, "get_blocking") + def test_get_following_return_annotation(self): + """Test that get_following has proper return type annotation.""" + method = getattr(UsersClient, "get_following") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_blocking should have return type annotation" + ), f"Method get_following should have return type annotation" - def test_get_blocking_pagination_params(self): - """Test that get_blocking has pagination parameters.""" - method = getattr(UsersClient, "get_blocking") + def test_get_following_pagination_params(self): + """Test that get_following has pagination parameters.""" + method = getattr(UsersClient, "get_following") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -501,21 +488,21 @@ def test_get_blocking_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_blocking should have pagination parameters" + ), f"Paginated method get_following should have pagination parameters" - def test_get_followers_exists(self): - """Test that get_followers method exists with correct signature.""" + def test_follow_user_exists(self): + """Test that follow_user method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_followers", None) - assert method is not None, f"Method get_followers does not exist on UsersClient" + method = getattr(UsersClient, "follow_user", None) + assert method is not None, f"Method follow_user does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_followers is not callable" + assert callable(method), f"follow_user is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_followers should have at least 'self' parameter" + assert len(params) >= 1, f"follow_user should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -526,15 +513,9 @@ def test_get_followers_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_followers" + ), f"Required parameter '{required_param}' missing from follow_user" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "max_results", - "pagination_token", - "user.fields", - "expansions", - "tweet.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -543,73 +524,42 @@ def test_get_followers_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_followers_return_annotation(self): - """Test that get_followers has proper return type annotation.""" - method = getattr(UsersClient, "get_followers") + def test_follow_user_return_annotation(self): + """Test that follow_user has proper return type annotation.""" + method = getattr(UsersClient, "follow_user") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_followers should have return type annotation" - - - def test_get_followers_pagination_params(self): - """Test that get_followers has pagination parameters.""" - method = getattr(UsersClient, "get_followers") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_followers should have pagination parameters" + ), f"Method follow_user should have return type annotation" - def test_get_mentions_exists(self): - """Test that get_mentions method exists with correct signature.""" + def test_unrepost_post_exists(self): + """Test that unrepost_post method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_mentions", None) - assert method is not None, f"Method get_mentions does not exist on UsersClient" + method = getattr(UsersClient, "unrepost_post", None) + assert method is not None, f"Method unrepost_post does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_mentions is not callable" + assert callable(method), f"unrepost_post is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_mentions should have at least 'self' parameter" + assert len(params) >= 1, f"unrepost_post should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ "id", + "source_tweet_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_mentions" + ), f"Required parameter '{required_param}' missing from unrepost_post" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "since_id", - "until_id", - "max_results", - "pagination_token", - "start_time", - "end_time", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", - "user.fields", - "place.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -618,67 +568,48 @@ def test_get_mentions_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_mentions_return_annotation(self): - """Test that get_mentions has proper return type annotation.""" - method = getattr(UsersClient, "get_mentions") + def test_unrepost_post_return_annotation(self): + """Test that unrepost_post has proper return type annotation.""" + method = getattr(UsersClient, "unrepost_post") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_mentions should have return type annotation" - - - def test_get_mentions_pagination_params(self): - """Test that get_mentions has pagination parameters.""" - method = getattr(UsersClient, "get_mentions") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_mentions should have pagination parameters" + ), f"Method unrepost_post should have return type annotation" - def test_get_pinned_lists_exists(self): - """Test that get_pinned_lists method exists with correct signature.""" + def test_get_by_usernames_exists(self): + """Test that get_by_usernames method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_pinned_lists", None) + method = getattr(UsersClient, "get_by_usernames", None) assert ( method is not None - ), f"Method get_pinned_lists does not exist on UsersClient" + ), f"Method get_by_usernames does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_pinned_lists is not callable" + assert callable(method), f"get_by_usernames is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"get_pinned_lists should have at least 'self' parameter" + ), f"get_by_usernames should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "usernames", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_pinned_lists" + ), f"Required parameter '{required_param}' missing from get_by_usernames" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "list.fields", - "expansions", "user.fields", + "expansions", + "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -688,28 +619,32 @@ def test_get_pinned_lists_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_pinned_lists_return_annotation(self): - """Test that get_pinned_lists has proper return type annotation.""" - method = getattr(UsersClient, "get_pinned_lists") + def test_get_by_usernames_return_annotation(self): + """Test that get_by_usernames has proper return type annotation.""" + method = getattr(UsersClient, "get_by_usernames") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_pinned_lists should have return type annotation" + ), f"Method get_by_usernames should have return type annotation" - def test_pin_list_exists(self): - """Test that pin_list method exists with correct signature.""" + def test_get_liked_posts_exists(self): + """Test that get_liked_posts method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "pin_list", None) - assert method is not None, f"Method pin_list does not exist on UsersClient" + method = getattr(UsersClient, "get_liked_posts", None) + assert ( + method is not None + ), f"Method get_liked_posts does not exist on UsersClient" # Check method is callable - assert callable(method), f"pin_list is not callable" + assert callable(method), f"get_liked_posts is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"pin_list should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_liked_posts should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -720,9 +655,18 @@ def test_pin_list_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from pin_list" + ), f"Required parameter '{required_param}' missing from get_liked_posts" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "max_results", + "pagination_token", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -731,42 +675,71 @@ def test_pin_list_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_pin_list_return_annotation(self): - """Test that pin_list has proper return type annotation.""" - method = getattr(UsersClient, "pin_list") + def test_get_liked_posts_return_annotation(self): + """Test that get_liked_posts has proper return type annotation.""" + method = getattr(UsersClient, "get_liked_posts") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method pin_list should have return type annotation" + ), f"Method get_liked_posts should have return type annotation" - def test_unpin_list_exists(self): - """Test that unpin_list method exists with correct signature.""" + def test_get_liked_posts_pagination_params(self): + """Test that get_liked_posts has pagination parameters.""" + method = getattr(UsersClient, "get_liked_posts") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_liked_posts should have pagination parameters" + + + def test_get_reposts_of_me_exists(self): + """Test that get_reposts_of_me method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "unpin_list", None) - assert method is not None, f"Method unpin_list does not exist on UsersClient" + method = getattr(UsersClient, "get_reposts_of_me", None) + assert ( + method is not None + ), f"Method get_reposts_of_me does not exist on UsersClient" # Check method is callable - assert callable(method), f"unpin_list is not callable" + assert callable(method), f"get_reposts_of_me is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"unpin_list should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_reposts_of_me should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "id", - "list_id", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from unpin_list" + ), f"Required parameter '{required_param}' missing from get_reposts_of_me" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "max_results", + "pagination_token", + "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -775,19 +748,107 @@ def test_unpin_list_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_unpin_list_return_annotation(self): - """Test that unpin_list has proper return type annotation.""" - method = getattr(UsersClient, "unpin_list") + def test_get_reposts_of_me_return_annotation(self): + """Test that get_reposts_of_me has proper return type annotation.""" + method = getattr(UsersClient, "get_reposts_of_me") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method unpin_list should have return type annotation" + ), f"Method get_reposts_of_me should have return type annotation" - def test_delete_bookmark_exists(self): - """Test that delete_bookmark method exists with correct signature.""" - # Check method exists + def test_get_reposts_of_me_pagination_params(self): + """Test that get_reposts_of_me has pagination parameters.""" + method = getattr(UsersClient, "get_reposts_of_me") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_reposts_of_me should have pagination parameters" + + + def test_get_bookmark_folders_exists(self): + """Test that get_bookmark_folders method exists with correct signature.""" + # Check method exists + method = getattr(UsersClient, "get_bookmark_folders", None) + assert ( + method is not None + ), f"Method get_bookmark_folders does not exist on UsersClient" + # Check method is callable + assert callable(method), f"get_bookmark_folders is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert ( + len(params) >= 1 + ), f"get_bookmark_folders should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [ + "id", + ] + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from get_bookmark_folders" + # Check optional parameters have defaults (excluding 'self') + optional_params = [ + "max_results", + "pagination_token", + ] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_get_bookmark_folders_return_annotation(self): + """Test that get_bookmark_folders has proper return type annotation.""" + method = getattr(UsersClient, "get_bookmark_folders") + sig = inspect.signature(method) + # Check return annotation exists + assert ( + sig.return_annotation is not inspect.Signature.empty + ), f"Method get_bookmark_folders should have return type annotation" + + + def test_get_bookmark_folders_pagination_params(self): + """Test that get_bookmark_folders has pagination parameters.""" + method = getattr(UsersClient, "get_bookmark_folders") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_bookmark_folders should have pagination parameters" + + + def test_delete_bookmark_exists(self): + """Test that delete_bookmark method exists with correct signature.""" + # Check method exists method = getattr(UsersClient, "delete_bookmark", None) assert ( method is not None @@ -833,18 +894,18 @@ def test_delete_bookmark_return_annotation(self): ), f"Method delete_bookmark should have return type annotation" - def test_block_dms_exists(self): - """Test that block_dms method exists with correct signature.""" + def test_like_post_exists(self): + """Test that like_post method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "block_dms", None) - assert method is not None, f"Method block_dms does not exist on UsersClient" + method = getattr(UsersClient, "like_post", None) + assert method is not None, f"Method like_post does not exist on UsersClient" # Check method is callable - assert callable(method), f"block_dms is not callable" + assert callable(method), f"like_post is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"block_dms should have at least 'self' parameter" + assert len(params) >= 1, f"like_post should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -855,7 +916,7 @@ def test_block_dms_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from block_dms" + ), f"Required parameter '{required_param}' missing from like_post" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -866,40 +927,39 @@ def test_block_dms_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_block_dms_return_annotation(self): - """Test that block_dms has proper return type annotation.""" - method = getattr(UsersClient, "block_dms") + def test_like_post_return_annotation(self): + """Test that like_post has proper return type annotation.""" + method = getattr(UsersClient, "like_post") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method block_dms should have return type annotation" + ), f"Method like_post should have return type annotation" - def test_unlike_post_exists(self): - """Test that unlike_post method exists with correct signature.""" + def test_unblock_dms_exists(self): + """Test that unblock_dms method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "unlike_post", None) - assert method is not None, f"Method unlike_post does not exist on UsersClient" + method = getattr(UsersClient, "unblock_dms", None) + assert method is not None, f"Method unblock_dms does not exist on UsersClient" # Check method is callable - assert callable(method), f"unlike_post is not callable" + assert callable(method), f"unblock_dms is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"unlike_post should have at least 'self' parameter" + assert len(params) >= 1, f"unblock_dms should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ "id", - "tweet_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from unlike_post" + ), f"Required parameter '{required_param}' missing from unblock_dms" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -910,44 +970,40 @@ def test_unlike_post_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_unlike_post_return_annotation(self): - """Test that unlike_post has proper return type annotation.""" - method = getattr(UsersClient, "unlike_post") + def test_unblock_dms_return_annotation(self): + """Test that unblock_dms has proper return type annotation.""" + method = getattr(UsersClient, "unblock_dms") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method unlike_post should have return type annotation" + ), f"Method unblock_dms should have return type annotation" - def test_get_bookmarks_by_folder_id_exists(self): - """Test that get_bookmarks_by_folder_id method exists with correct signature.""" + def test_unfollow_user_exists(self): + """Test that unfollow_user method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_bookmarks_by_folder_id", None) - assert ( - method is not None - ), f"Method get_bookmarks_by_folder_id does not exist on UsersClient" + method = getattr(UsersClient, "unfollow_user", None) + assert method is not None, f"Method unfollow_user does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_bookmarks_by_folder_id is not callable" + assert callable(method), f"unfollow_user is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_bookmarks_by_folder_id should have at least 'self' parameter" + assert len(params) >= 1, f"unfollow_user should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", - "folder_id", + "source_user_id", + "target_user_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_bookmarks_by_folder_id" + ), f"Required parameter '{required_param}' missing from unfollow_user" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -958,32 +1014,32 @@ def test_get_bookmarks_by_folder_id_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_bookmarks_by_folder_id_return_annotation(self): - """Test that get_bookmarks_by_folder_id has proper return type annotation.""" - method = getattr(UsersClient, "get_bookmarks_by_folder_id") + def test_unfollow_user_return_annotation(self): + """Test that unfollow_user has proper return type annotation.""" + method = getattr(UsersClient, "unfollow_user") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_bookmarks_by_folder_id should have return type annotation" + ), f"Method unfollow_user should have return type annotation" - def test_get_bookmark_folders_exists(self): - """Test that get_bookmark_folders method exists with correct signature.""" + def test_get_list_memberships_exists(self): + """Test that get_list_memberships method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_bookmark_folders", None) + method = getattr(UsersClient, "get_list_memberships", None) assert ( method is not None - ), f"Method get_bookmark_folders does not exist on UsersClient" + ), f"Method get_list_memberships does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_bookmark_folders is not callable" + assert callable(method), f"get_list_memberships is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"get_bookmark_folders should have at least 'self' parameter" + ), f"get_list_memberships should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -994,11 +1050,14 @@ def test_get_bookmark_folders_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_bookmark_folders" + ), f"Required parameter '{required_param}' missing from get_list_memberships" # Check optional parameters have defaults (excluding 'self') optional_params = [ "max_results", "pagination_token", + "list.fields", + "expansions", + "user.fields", ] for optional_param in optional_params: if optional_param in params: @@ -1008,19 +1067,19 @@ def test_get_bookmark_folders_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_bookmark_folders_return_annotation(self): - """Test that get_bookmark_folders has proper return type annotation.""" - method = getattr(UsersClient, "get_bookmark_folders") + def test_get_list_memberships_return_annotation(self): + """Test that get_list_memberships has proper return type annotation.""" + method = getattr(UsersClient, "get_list_memberships") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_bookmark_folders should have return type annotation" + ), f"Method get_list_memberships should have return type annotation" - def test_get_bookmark_folders_pagination_params(self): - """Test that get_bookmark_folders has pagination parameters.""" - method = getattr(UsersClient, "get_bookmark_folders") + def test_get_list_memberships_pagination_params(self): + """Test that get_list_memberships has pagination parameters.""" + method = getattr(UsersClient, "get_list_memberships") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -1034,35 +1093,40 @@ def test_get_bookmark_folders_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_bookmark_folders should have pagination parameters" + ), f"Paginated method get_list_memberships should have pagination parameters" - def test_unrepost_post_exists(self): - """Test that unrepost_post method exists with correct signature.""" + def test_get_muting_exists(self): + """Test that get_muting method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "unrepost_post", None) - assert method is not None, f"Method unrepost_post does not exist on UsersClient" + method = getattr(UsersClient, "get_muting", None) + assert method is not None, f"Method get_muting does not exist on UsersClient" # Check method is callable - assert callable(method), f"unrepost_post is not callable" + assert callable(method), f"get_muting is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"unrepost_post should have at least 'self' parameter" + assert len(params) >= 1, f"get_muting should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ "id", - "source_tweet_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from unrepost_post" + ), f"Required parameter '{required_param}' missing from get_muting" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "max_results", + "pagination_token", + "user.fields", + "expansions", + "tweet.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1071,52 +1135,60 @@ def test_unrepost_post_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_unrepost_post_return_annotation(self): - """Test that unrepost_post has proper return type annotation.""" - method = getattr(UsersClient, "unrepost_post") + def test_get_muting_return_annotation(self): + """Test that get_muting has proper return type annotation.""" + method = getattr(UsersClient, "get_muting") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method unrepost_post should have return type annotation" + ), f"Method get_muting should have return type annotation" - def test_get_reposts_of_me_exists(self): - """Test that get_reposts_of_me method exists with correct signature.""" - # Check method exists - method = getattr(UsersClient, "get_reposts_of_me", None) - assert ( - method is not None - ), f"Method get_reposts_of_me does not exist on UsersClient" - # Check method is callable - assert callable(method), f"get_reposts_of_me is not callable" - # Check method signature + def test_get_muting_pagination_params(self): + """Test that get_muting has pagination parameters.""" + method = getattr(UsersClient, "get_muting") sig = inspect.signature(method) params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_reposts_of_me should have at least 'self' parameter" + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_muting should have pagination parameters" + + + def test_mute_user_exists(self): + """Test that mute_user method exists with correct signature.""" + # Check method exists + method = getattr(UsersClient, "mute_user", None) + assert method is not None, f"Method mute_user does not exist on UsersClient" + # Check method is callable + assert callable(method), f"mute_user is not callable" + # Check method signature + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have 'self' as first parameter + assert len(params) >= 1, f"mute_user should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "id", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_reposts_of_me" + ), f"Required parameter '{required_param}' missing from mute_user" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "max_results", - "pagination_token", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", - "user.fields", - "place.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1125,69 +1197,48 @@ def test_get_reposts_of_me_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_reposts_of_me_return_annotation(self): - """Test that get_reposts_of_me has proper return type annotation.""" - method = getattr(UsersClient, "get_reposts_of_me") + def test_mute_user_return_annotation(self): + """Test that mute_user has proper return type annotation.""" + method = getattr(UsersClient, "mute_user") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_reposts_of_me should have return type annotation" - - - def test_get_reposts_of_me_pagination_params(self): - """Test that get_reposts_of_me has pagination parameters.""" - method = getattr(UsersClient, "get_reposts_of_me") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_reposts_of_me should have pagination parameters" + ), f"Method mute_user should have return type annotation" - def test_get_list_memberships_exists(self): - """Test that get_list_memberships method exists with correct signature.""" + def test_get_by_username_exists(self): + """Test that get_by_username method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_list_memberships", None) + method = getattr(UsersClient, "get_by_username", None) assert ( method is not None - ), f"Method get_list_memberships does not exist on UsersClient" + ), f"Method get_by_username does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_list_memberships is not callable" + assert callable(method), f"get_by_username is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"get_list_memberships should have at least 'self' parameter" + ), f"get_by_username should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "username", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_list_memberships" + ), f"Required parameter '{required_param}' missing from get_by_username" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "max_results", - "pagination_token", - "list.fields", - "expansions", "user.fields", + "expansions", + "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -1197,47 +1248,28 @@ def test_get_list_memberships_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_list_memberships_return_annotation(self): - """Test that get_list_memberships has proper return type annotation.""" - method = getattr(UsersClient, "get_list_memberships") + def test_get_by_username_return_annotation(self): + """Test that get_by_username has proper return type annotation.""" + method = getattr(UsersClient, "get_by_username") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_list_memberships should have return type annotation" - - - def test_get_list_memberships_pagination_params(self): - """Test that get_list_memberships has pagination parameters.""" - method = getattr(UsersClient, "get_list_memberships") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_list_memberships should have pagination parameters" + ), f"Method get_by_username should have return type annotation" - def test_get_timeline_exists(self): - """Test that get_timeline method exists with correct signature.""" + def test_get_by_id_exists(self): + """Test that get_by_id method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_timeline", None) - assert method is not None, f"Method get_timeline does not exist on UsersClient" + method = getattr(UsersClient, "get_by_id", None) + assert method is not None, f"Method get_by_id does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_timeline is not callable" + assert callable(method), f"get_by_id is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_timeline should have at least 'self' parameter" + assert len(params) >= 1, f"get_by_id should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -1248,22 +1280,12 @@ def test_get_timeline_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_timeline" + ), f"Required parameter '{required_param}' missing from get_by_id" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "since_id", - "until_id", - "max_results", - "pagination_token", - "exclude", - "start_time", - "end_time", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", "user.fields", - "place.fields", + "expansions", + "tweet.fields", ] for optional_param in optional_params: if optional_param in params: @@ -1273,60 +1295,45 @@ def test_get_timeline_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_timeline_return_annotation(self): - """Test that get_timeline has proper return type annotation.""" - method = getattr(UsersClient, "get_timeline") + def test_get_by_id_return_annotation(self): + """Test that get_by_id has proper return type annotation.""" + method = getattr(UsersClient, "get_by_id") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_timeline should have return type annotation" - - - def test_get_timeline_pagination_params(self): - """Test that get_timeline has pagination parameters.""" - method = getattr(UsersClient, "get_timeline") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_timeline should have pagination parameters" + ), f"Method get_by_id should have return type annotation" - def test_like_post_exists(self): - """Test that like_post method exists with correct signature.""" + def test_get_by_ids_exists(self): + """Test that get_by_ids method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "like_post", None) - assert method is not None, f"Method like_post does not exist on UsersClient" + method = getattr(UsersClient, "get_by_ids", None) + assert method is not None, f"Method get_by_ids does not exist on UsersClient" # Check method is callable - assert callable(method), f"like_post is not callable" + assert callable(method), f"get_by_ids is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"like_post should have at least 'self' parameter" + assert len(params) >= 1, f"get_by_ids should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "ids", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from like_post" + ), f"Required parameter '{required_param}' missing from get_by_ids" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "user.fields", + "expansions", + "tweet.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1335,43 +1342,46 @@ def test_like_post_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_like_post_return_annotation(self): - """Test that like_post has proper return type annotation.""" - method = getattr(UsersClient, "like_post") + def test_get_by_ids_return_annotation(self): + """Test that get_by_ids has proper return type annotation.""" + method = getattr(UsersClient, "get_by_ids") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method like_post should have return type annotation" + ), f"Method get_by_ids should have return type annotation" - def test_get_me_exists(self): - """Test that get_me method exists with correct signature.""" + def test_get_bookmarks_by_folder_id_exists(self): + """Test that get_bookmarks_by_folder_id method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_me", None) - assert method is not None, f"Method get_me does not exist on UsersClient" + method = getattr(UsersClient, "get_bookmarks_by_folder_id", None) + assert ( + method is not None + ), f"Method get_bookmarks_by_folder_id does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_me is not callable" + assert callable(method), f"get_bookmarks_by_folder_id is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_me should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_bookmarks_by_folder_id should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "id", + "folder_id", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_me" + ), f"Required parameter '{required_param}' missing from get_bookmarks_by_folder_id" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "user.fields", - "expansions", - "tweet.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1380,48 +1390,50 @@ def test_get_me_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_me_return_annotation(self): - """Test that get_me has proper return type annotation.""" - method = getattr(UsersClient, "get_me") + def test_get_bookmarks_by_folder_id_return_annotation(self): + """Test that get_bookmarks_by_folder_id has proper return type annotation.""" + method = getattr(UsersClient, "get_bookmarks_by_folder_id") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_me should have return type annotation" + ), f"Method get_bookmarks_by_folder_id should have return type annotation" - def test_get_by_usernames_exists(self): - """Test that get_by_usernames method exists with correct signature.""" + def test_get_followed_lists_exists(self): + """Test that get_followed_lists method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_by_usernames", None) + method = getattr(UsersClient, "get_followed_lists", None) assert ( method is not None - ), f"Method get_by_usernames does not exist on UsersClient" + ), f"Method get_followed_lists does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_by_usernames is not callable" + assert callable(method), f"get_followed_lists is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"get_by_usernames should have at least 'self' parameter" + ), f"get_followed_lists should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "usernames", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_usernames" + ), f"Required parameter '{required_param}' missing from get_followed_lists" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "user.fields", + "max_results", + "pagination_token", + "list.fields", "expansions", - "tweet.fields", + "user.fields", ] for optional_param in optional_params: if optional_param in params: @@ -1431,76 +1443,19 @@ def test_get_by_usernames_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_usernames_return_annotation(self): - """Test that get_by_usernames has proper return type annotation.""" - method = getattr(UsersClient, "get_by_usernames") - sig = inspect.signature(method) - # Check return annotation exists - assert ( - sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_usernames should have return type annotation" - - - def test_get_posts_exists(self): - """Test that get_posts method exists with correct signature.""" - # Check method exists - method = getattr(UsersClient, "get_posts", None) - assert method is not None, f"Method get_posts does not exist on UsersClient" - # Check method is callable - assert callable(method), f"get_posts is not callable" - # Check method signature - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have 'self' as first parameter - assert len(params) >= 1, f"get_posts should have at least 'self' parameter" - assert ( - params[0] == "self" - ), f"First parameter should be 'self', got '{params[0]}'" - # Check required parameters exist (excluding 'self') - required_params = [ - "id", - ] - for required_param in required_params: - assert ( - required_param in params - ), f"Required parameter '{required_param}' missing from get_posts" - # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "since_id", - "until_id", - "max_results", - "pagination_token", - "exclude", - "start_time", - "end_time", - "tweet.fields", - "expansions", - "media.fields", - "poll.fields", - "user.fields", - "place.fields", - ] - for optional_param in optional_params: - if optional_param in params: - param_obj = sig.parameters[optional_param] - assert ( - param_obj.default is not inspect.Parameter.empty - ), f"Optional parameter '{optional_param}' should have a default value" - - - def test_get_posts_return_annotation(self): - """Test that get_posts has proper return type annotation.""" - method = getattr(UsersClient, "get_posts") + def test_get_followed_lists_return_annotation(self): + """Test that get_followed_lists has proper return type annotation.""" + method = getattr(UsersClient, "get_followed_lists") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_posts should have return type annotation" + ), f"Method get_followed_lists should have return type annotation" - def test_get_posts_pagination_params(self): - """Test that get_posts has pagination parameters.""" - method = getattr(UsersClient, "get_posts") + def test_get_followed_lists_pagination_params(self): + """Test that get_followed_lists has pagination parameters.""" + method = getattr(UsersClient, "get_followed_lists") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -1514,25 +1469,21 @@ def test_get_posts_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_posts should have pagination parameters" + ), f"Paginated method get_followed_lists should have pagination parameters" - def test_get_followed_lists_exists(self): - """Test that get_followed_lists method exists with correct signature.""" + def test_follow_list_exists(self): + """Test that follow_list method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_followed_lists", None) - assert ( - method is not None - ), f"Method get_followed_lists does not exist on UsersClient" + method = getattr(UsersClient, "follow_list", None) + assert method is not None, f"Method follow_list does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_followed_lists is not callable" + assert callable(method), f"follow_list is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_followed_lists should have at least 'self' parameter" + assert len(params) >= 1, f"follow_list should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -1543,15 +1494,9 @@ def test_get_followed_lists_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_followed_lists" + ), f"Required parameter '{required_param}' missing from follow_list" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "max_results", - "pagination_token", - "list.fields", - "expansions", - "user.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1560,60 +1505,43 @@ def test_get_followed_lists_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_followed_lists_return_annotation(self): - """Test that get_followed_lists has proper return type annotation.""" - method = getattr(UsersClient, "get_followed_lists") + def test_follow_list_return_annotation(self): + """Test that follow_list has proper return type annotation.""" + method = getattr(UsersClient, "follow_list") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_followed_lists should have return type annotation" - - - def test_get_followed_lists_pagination_params(self): - """Test that get_followed_lists has pagination parameters.""" - method = getattr(UsersClient, "get_followed_lists") - sig = inspect.signature(method) - params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", - ] - has_pagination_param = any(param in params for param in pagination_params) - assert ( - has_pagination_param - ), f"Paginated method get_followed_lists should have pagination parameters" + ), f"Method follow_list should have return type annotation" - def test_follow_list_exists(self): - """Test that follow_list method exists with correct signature.""" + def test_get_me_exists(self): + """Test that get_me method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "follow_list", None) - assert method is not None, f"Method follow_list does not exist on UsersClient" + method = getattr(UsersClient, "get_me", None) + assert method is not None, f"Method get_me does not exist on UsersClient" # Check method is callable - assert callable(method), f"follow_list is not callable" + assert callable(method), f"get_me is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"follow_list should have at least 'self' parameter" + assert len(params) >= 1, f"get_me should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "id", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from follow_list" + ), f"Required parameter '{required_param}' missing from get_me" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "user.fields", + "expansions", + "tweet.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1622,49 +1550,42 @@ def test_follow_list_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_follow_list_return_annotation(self): - """Test that follow_list has proper return type annotation.""" - method = getattr(UsersClient, "follow_list") + def test_get_me_return_annotation(self): + """Test that get_me has proper return type annotation.""" + method = getattr(UsersClient, "get_me") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method follow_list should have return type annotation" + ), f"Method get_me should have return type annotation" - def test_get_by_username_exists(self): - """Test that get_by_username method exists with correct signature.""" + def test_unlike_post_exists(self): + """Test that unlike_post method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_by_username", None) - assert ( - method is not None - ), f"Method get_by_username does not exist on UsersClient" + method = getattr(UsersClient, "unlike_post", None) + assert method is not None, f"Method unlike_post does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_by_username is not callable" + assert callable(method), f"unlike_post is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_by_username should have at least 'self' parameter" + assert len(params) >= 1, f"unlike_post should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "username", + "id", + "tweet_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_username" + ), f"Required parameter '{required_param}' missing from unlike_post" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "user.fields", - "expansions", - "tweet.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1673,32 +1594,28 @@ def test_get_by_username_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_username_return_annotation(self): - """Test that get_by_username has proper return type annotation.""" - method = getattr(UsersClient, "get_by_username") + def test_unlike_post_return_annotation(self): + """Test that unlike_post has proper return type annotation.""" + method = getattr(UsersClient, "unlike_post") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_username should have return type annotation" + ), f"Method unlike_post should have return type annotation" - def test_get_owned_lists_exists(self): - """Test that get_owned_lists method exists with correct signature.""" + def test_get_posts_exists(self): + """Test that get_posts method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_owned_lists", None) - assert ( - method is not None - ), f"Method get_owned_lists does not exist on UsersClient" + method = getattr(UsersClient, "get_posts", None) + assert method is not None, f"Method get_posts does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_owned_lists is not callable" + assert callable(method), f"get_posts is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"get_owned_lists should have at least 'self' parameter" + assert len(params) >= 1, f"get_posts should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -1709,14 +1626,22 @@ def test_get_owned_lists_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_owned_lists" + ), f"Required parameter '{required_param}' missing from get_posts" # Check optional parameters have defaults (excluding 'self') optional_params = [ + "since_id", + "until_id", "max_results", "pagination_token", - "list.fields", + "exclude", + "start_time", + "end_time", + "tweet.fields", "expansions", + "media.fields", + "poll.fields", "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -1726,19 +1651,19 @@ def test_get_owned_lists_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_owned_lists_return_annotation(self): - """Test that get_owned_lists has proper return type annotation.""" - method = getattr(UsersClient, "get_owned_lists") + def test_get_posts_return_annotation(self): + """Test that get_posts has proper return type annotation.""" + method = getattr(UsersClient, "get_posts") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_owned_lists should have return type annotation" + ), f"Method get_posts should have return type annotation" - def test_get_owned_lists_pagination_params(self): - """Test that get_owned_lists has pagination parameters.""" - method = getattr(UsersClient, "get_owned_lists") + def test_get_posts_pagination_params(self): + """Test that get_posts has pagination parameters.""" + method = getattr(UsersClient, "get_posts") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -1752,38 +1677,35 @@ def test_get_owned_lists_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_owned_lists should have pagination parameters" + ), f"Paginated method get_posts should have pagination parameters" - def test_get_by_id_exists(self): - """Test that get_by_id method exists with correct signature.""" + def test_unfollow_list_exists(self): + """Test that unfollow_list method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_by_id", None) - assert method is not None, f"Method get_by_id does not exist on UsersClient" + method = getattr(UsersClient, "unfollow_list", None) + assert method is not None, f"Method unfollow_list does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_by_id is not callable" + assert callable(method), f"unfollow_list is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_by_id should have at least 'self' parameter" + assert len(params) >= 1, f"unfollow_list should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ "id", + "list_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_by_id" + ), f"Required parameter '{required_param}' missing from unfollow_list" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "user.fields", - "expansions", - "tweet.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1792,41 +1714,47 @@ def test_get_by_id_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_by_id_return_annotation(self): - """Test that get_by_id has proper return type annotation.""" - method = getattr(UsersClient, "get_by_id") + def test_unfollow_list_return_annotation(self): + """Test that unfollow_list has proper return type annotation.""" + method = getattr(UsersClient, "unfollow_list") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_by_id should have return type annotation" + ), f"Method unfollow_list should have return type annotation" - def test_unblock_dms_exists(self): - """Test that unblock_dms method exists with correct signature.""" + def test_search_exists(self): + """Test that search method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "unblock_dms", None) - assert method is not None, f"Method unblock_dms does not exist on UsersClient" + method = getattr(UsersClient, "search", None) + assert method is not None, f"Method search does not exist on UsersClient" # Check method is callable - assert callable(method), f"unblock_dms is not callable" + assert callable(method), f"search is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"unblock_dms should have at least 'self' parameter" + assert len(params) >= 1, f"search should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "query", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from unblock_dms" + ), f"Required parameter '{required_param}' missing from search" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "max_results", + "next_token", + "user.fields", + "expansions", + "tweet.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -1835,43 +1763,62 @@ def test_unblock_dms_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_unblock_dms_return_annotation(self): - """Test that unblock_dms has proper return type annotation.""" - method = getattr(UsersClient, "unblock_dms") + def test_search_return_annotation(self): + """Test that search has proper return type annotation.""" + method = getattr(UsersClient, "search") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method unblock_dms should have return type annotation" + ), f"Method search should have return type annotation" - def test_search_exists(self): - """Test that search method exists with correct signature.""" + def test_search_pagination_params(self): + """Test that search has pagination parameters.""" + method = getattr(UsersClient, "search") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method search should have pagination parameters" + + + def test_get_blocking_exists(self): + """Test that get_blocking method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "search", None) - assert method is not None, f"Method search does not exist on UsersClient" + method = getattr(UsersClient, "get_blocking", None) + assert method is not None, f"Method get_blocking does not exist on UsersClient" # Check method is callable - assert callable(method), f"search is not callable" + assert callable(method), f"get_blocking is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"search should have at least 'self' parameter" + assert len(params) >= 1, f"get_blocking should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "query", + "id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from search" + ), f"Required parameter '{required_param}' missing from get_blocking" # Check optional parameters have defaults (excluding 'self') optional_params = [ "max_results", - "next_token", + "pagination_token", "user.fields", "expansions", "tweet.fields", @@ -1884,19 +1831,19 @@ def test_search_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_search_return_annotation(self): - """Test that search has proper return type annotation.""" - method = getattr(UsersClient, "search") + def test_get_blocking_return_annotation(self): + """Test that get_blocking has proper return type annotation.""" + method = getattr(UsersClient, "get_blocking") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method search should have return type annotation" + ), f"Method get_blocking should have return type annotation" - def test_search_pagination_params(self): - """Test that search has pagination parameters.""" - method = getattr(UsersClient, "search") + def test_get_blocking_pagination_params(self): + """Test that get_blocking has pagination parameters.""" + method = getattr(UsersClient, "get_blocking") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -1910,21 +1857,21 @@ def test_search_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method search should have pagination parameters" + ), f"Paginated method get_blocking should have pagination parameters" - def test_get_muting_exists(self): - """Test that get_muting method exists with correct signature.""" + def test_get_timeline_exists(self): + """Test that get_timeline method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_muting", None) - assert method is not None, f"Method get_muting does not exist on UsersClient" + method = getattr(UsersClient, "get_timeline", None) + assert method is not None, f"Method get_timeline does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_muting is not callable" + assert callable(method), f"get_timeline is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_muting should have at least 'self' parameter" + assert len(params) >= 1, f"get_timeline should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -1935,14 +1882,22 @@ def test_get_muting_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_muting" + ), f"Required parameter '{required_param}' missing from get_timeline" # Check optional parameters have defaults (excluding 'self') optional_params = [ + "since_id", + "until_id", "max_results", "pagination_token", - "user.fields", - "expansions", + "exclude", + "start_time", + "end_time", "tweet.fields", + "expansions", + "media.fields", + "poll.fields", + "user.fields", + "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -1952,19 +1907,19 @@ def test_get_muting_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_muting_return_annotation(self): - """Test that get_muting has proper return type annotation.""" - method = getattr(UsersClient, "get_muting") + def test_get_timeline_return_annotation(self): + """Test that get_timeline has proper return type annotation.""" + method = getattr(UsersClient, "get_timeline") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_muting should have return type annotation" + ), f"Method get_timeline should have return type annotation" - def test_get_muting_pagination_params(self): - """Test that get_muting has pagination parameters.""" - method = getattr(UsersClient, "get_muting") + def test_get_timeline_pagination_params(self): + """Test that get_timeline has pagination parameters.""" + method = getattr(UsersClient, "get_timeline") sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have pagination-related parameters @@ -1978,32 +1933,33 @@ def test_get_muting_pagination_params(self): has_pagination_param = any(param in params for param in pagination_params) assert ( has_pagination_param - ), f"Paginated method get_muting should have pagination parameters" + ), f"Paginated method get_timeline should have pagination parameters" - def test_mute_user_exists(self): - """Test that mute_user method exists with correct signature.""" + def test_unmute_user_exists(self): + """Test that unmute_user method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "mute_user", None) - assert method is not None, f"Method mute_user does not exist on UsersClient" + method = getattr(UsersClient, "unmute_user", None) + assert method is not None, f"Method unmute_user does not exist on UsersClient" # Check method is callable - assert callable(method), f"mute_user is not callable" + assert callable(method), f"unmute_user is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"mute_user should have at least 'self' parameter" + assert len(params) >= 1, f"unmute_user should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') required_params = [ - "id", + "source_user_id", + "target_user_id", ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from mute_user" + ), f"Required parameter '{required_param}' missing from unmute_user" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -2014,28 +1970,32 @@ def test_mute_user_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_mute_user_return_annotation(self): - """Test that mute_user has proper return type annotation.""" - method = getattr(UsersClient, "mute_user") + def test_unmute_user_return_annotation(self): + """Test that unmute_user has proper return type annotation.""" + method = getattr(UsersClient, "unmute_user") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method mute_user should have return type annotation" + ), f"Method unmute_user should have return type annotation" - def test_get_bookmarks_exists(self): - """Test that get_bookmarks method exists with correct signature.""" + def test_get_pinned_lists_exists(self): + """Test that get_pinned_lists method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "get_bookmarks", None) - assert method is not None, f"Method get_bookmarks does not exist on UsersClient" + method = getattr(UsersClient, "get_pinned_lists", None) + assert ( + method is not None + ), f"Method get_pinned_lists does not exist on UsersClient" # Check method is callable - assert callable(method), f"get_bookmarks is not callable" + assert callable(method), f"get_pinned_lists is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get_bookmarks should have at least 'self' parameter" + assert ( + len(params) >= 1 + ), f"get_pinned_lists should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -2046,17 +2006,12 @@ def test_get_bookmarks_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_bookmarks" + ), f"Required parameter '{required_param}' missing from get_pinned_lists" # Check optional parameters have defaults (excluding 'self') optional_params = [ - "max_results", - "pagination_token", - "tweet.fields", + "list.fields", "expansions", - "media.fields", - "poll.fields", "user.fields", - "place.fields", ] for optional_param in optional_params: if optional_param in params: @@ -2066,51 +2021,71 @@ def test_get_bookmarks_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_bookmarks_return_annotation(self): - """Test that get_bookmarks has proper return type annotation.""" - method = getattr(UsersClient, "get_bookmarks") + def test_get_pinned_lists_return_annotation(self): + """Test that get_pinned_lists has proper return type annotation.""" + method = getattr(UsersClient, "get_pinned_lists") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_bookmarks should have return type annotation" + ), f"Method get_pinned_lists should have return type annotation" - def test_get_bookmarks_pagination_params(self): - """Test that get_bookmarks has pagination parameters.""" - method = getattr(UsersClient, "get_bookmarks") + def test_pin_list_exists(self): + """Test that pin_list method exists with correct signature.""" + # Check method exists + method = getattr(UsersClient, "pin_list", None) + assert method is not None, f"Method pin_list does not exist on UsersClient" + # Check method is callable + assert callable(method), f"pin_list is not callable" + # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) - # Should have pagination-related parameters - pagination_params = [ - "pagination_token", - "max_results", - "next_token", - "cursor", - "limit", + # Should have 'self' as first parameter + assert len(params) >= 1, f"pin_list should have at least 'self' parameter" + assert ( + params[0] == "self" + ), f"First parameter should be 'self', got '{params[0]}'" + # Check required parameters exist (excluding 'self') + required_params = [ + "id", ] - has_pagination_param = any(param in params for param in pagination_params) + for required_param in required_params: + assert ( + required_param in params + ), f"Required parameter '{required_param}' missing from pin_list" + # Check optional parameters have defaults (excluding 'self') + optional_params = [] + for optional_param in optional_params: + if optional_param in params: + param_obj = sig.parameters[optional_param] + assert ( + param_obj.default is not inspect.Parameter.empty + ), f"Optional parameter '{optional_param}' should have a default value" + + + def test_pin_list_return_annotation(self): + """Test that pin_list has proper return type annotation.""" + method = getattr(UsersClient, "pin_list") + sig = inspect.signature(method) + # Check return annotation exists assert ( - has_pagination_param - ), f"Paginated method get_bookmarks should have pagination parameters" + sig.return_annotation is not inspect.Signature.empty + ), f"Method pin_list should have return type annotation" - def test_create_bookmark_exists(self): - """Test that create_bookmark method exists with correct signature.""" + def test_get_followers_exists(self): + """Test that get_followers method exists with correct signature.""" # Check method exists - method = getattr(UsersClient, "create_bookmark", None) - assert ( - method is not None - ), f"Method create_bookmark does not exist on UsersClient" + method = getattr(UsersClient, "get_followers", None) + assert method is not None, f"Method get_followers does not exist on UsersClient" # Check method is callable - assert callable(method), f"create_bookmark is not callable" + assert callable(method), f"get_followers is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert ( - len(params) >= 1 - ), f"create_bookmark should have at least 'self' parameter" + assert len(params) >= 1, f"get_followers should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -2121,9 +2096,15 @@ def test_create_bookmark_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from create_bookmark" + ), f"Required parameter '{required_param}' missing from get_followers" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "max_results", + "pagination_token", + "user.fields", + "expansions", + "tweet.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -2132,57 +2113,76 @@ def test_create_bookmark_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_create_bookmark_return_annotation(self): - """Test that create_bookmark has proper return type annotation.""" - method = getattr(UsersClient, "create_bookmark") + def test_get_followers_return_annotation(self): + """Test that get_followers has proper return type annotation.""" + method = getattr(UsersClient, "get_followers") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method create_bookmark should have return type annotation" + ), f"Method get_followers should have return type annotation" + + + def test_get_followers_pagination_params(self): + """Test that get_followers has pagination parameters.""" + method = getattr(UsersClient, "get_followers") + sig = inspect.signature(method) + params = list(sig.parameters.keys()) + # Should have pagination-related parameters + pagination_params = [ + "pagination_token", + "max_results", + "next_token", + "cursor", + "limit", + ] + has_pagination_param = any(param in params for param in pagination_params) + assert ( + has_pagination_param + ), f"Paginated method get_followers should have pagination parameters" def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "get_by_ids", - "unfollow_user", - "get_liked_posts", - "unfollow_list", - "get_following", - "follow_user", - "unmute_user", - "repost_post", - "get_blocking", - "get_followers", + "get_bookmarks", + "create_bookmark", "get_mentions", - "get_pinned_lists", - "pin_list", + "get_owned_lists", "unpin_list", - "delete_bookmark", "block_dms", - "unlike_post", - "get_bookmarks_by_folder_id", - "get_bookmark_folders", + "repost_post", + "get_following", + "follow_user", "unrepost_post", + "get_by_usernames", + "get_liked_posts", "get_reposts_of_me", - "get_list_memberships", - "get_timeline", + "get_bookmark_folders", + "delete_bookmark", "like_post", - "get_me", - "get_by_usernames", - "get_posts", - "get_followed_lists", - "follow_list", - "get_by_username", - "get_owned_lists", - "get_by_id", "unblock_dms", - "search", + "unfollow_user", + "get_list_memberships", "get_muting", "mute_user", - "get_bookmarks", - "create_bookmark", + "get_by_username", + "get_by_id", + "get_by_ids", + "get_bookmarks_by_folder_id", + "get_followed_lists", + "follow_list", + "get_me", + "unlike_post", + "get_posts", + "unfollow_list", + "search", + "get_blocking", + "get_timeline", + "unmute_user", + "get_pinned_lists", + "pin_list", + "get_followers", ] for expected_method in expected_methods: assert hasattr( diff --git a/tests/webhooks/test_contracts.py b/tests/webhooks/test_contracts.py index fbd04d2..c107d65 100644 --- a/tests/webhooks/test_contracts.py +++ b/tests/webhooks/test_contracts.py @@ -27,8 +27,8 @@ def setup_class(self): self.webhooks_client = getattr(self.client, "webhooks") - def test_validate_request_structure(self): - """Test validate request structure.""" + def test_get_request_structure(self): + """Test get request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -37,15 +37,14 @@ def test_validate_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.put.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["webhook_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.webhooks_client, "validate") + method = getattr(self.webhooks_client, "get") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -64,7 +63,7 @@ def test_validate_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.put.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -73,14 +72,14 @@ def test_validate_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.put.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.put.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/webhooks/{webhook_id}" + expected_path = "/2/webhooks" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -96,27 +95,27 @@ def test_validate_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for validate: {e}") + pytest.fail(f"Contract test failed for get: {e}") - def test_validate_required_parameters(self): - """Test that validate handles parameters correctly.""" - method = getattr(self.webhooks_client, "validate") - # Test with missing required parameters - mock the request to avoid network calls + def test_get_required_parameters(self): + """Test that get handles parameters correctly.""" + method = getattr(self.webhooks_client, "get") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.put.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None + mock_session.get.return_value = mock_response + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_validate_response_structure(self): - """Test validate response structure validation.""" + def test_get_response_structure(self): + """Test get response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -126,13 +125,12 @@ def test_validate_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.put.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["webhook_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.webhooks_client, "validate") + method = getattr(self.webhooks_client, "get") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -144,8 +142,8 @@ def test_validate_response_structure(self): ) - def test_delete_request_structure(self): - """Test delete request structure.""" + def test_create_request_structure(self): + """Test create request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -154,15 +152,18 @@ def test_delete_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters - kwargs["webhook_id"] = "test_value" # Add request body if required + # Import and create proper request model instance + from xdk.webhooks.models import CreateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateRequest() # Call the method try: - method = getattr(self.webhooks_client, "delete") + method = getattr(self.webhooks_client, "create") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -181,7 +182,7 @@ def test_delete_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.delete.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -190,14 +191,14 @@ def test_delete_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.delete.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.delete.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/webhooks/{webhook_id}" + expected_path = "/2/webhooks" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -213,12 +214,12 @@ def test_delete_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for delete: {e}") + pytest.fail(f"Contract test failed for create: {e}") - def test_delete_required_parameters(self): - """Test that delete handles parameters correctly.""" - method = getattr(self.webhooks_client, "delete") + def test_create_required_parameters(self): + """Test that create handles parameters correctly.""" + method = getattr(self.webhooks_client, "create") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -226,14 +227,14 @@ def test_delete_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_delete_response_structure(self): - """Test delete response structure validation.""" + def test_create_response_structure(self): + """Test create response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -243,13 +244,16 @@ def test_delete_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.delete.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} - kwargs["webhook_id"] = "test" # Add request body if required + # Import and create proper request model instance + from xdk.webhooks.models import CreateRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateRequest() # Call method and verify response structure - method = getattr(self.webhooks_client, "delete") + method = getattr(self.webhooks_client, "create") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -261,8 +265,8 @@ def test_delete_response_structure(self): ) - def test_get_stream_links_request_structure(self): - """Test get_stream_links request structure.""" + def test_create_webhook_replay_job_request_structure(self): + """Test create_webhook_replay_job request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -271,14 +275,18 @@ def test_get_stream_links_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters # Add request body if required + # Import and create proper request model instance + from xdk.webhooks.models import CreateWebhookReplayJobRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateWebhookReplayJobRequest() # Call the method try: - method = getattr(self.webhooks_client, "get_stream_links") + method = getattr(self.webhooks_client, "create_webhook_replay_job") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -297,7 +305,7 @@ def test_get_stream_links_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.post.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -306,14 +314,14 @@ def test_get_stream_links_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.post.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.post.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/tweets/search/webhooks" + expected_path = "/2/webhooks/replay" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -329,27 +337,27 @@ def test_get_stream_links_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get_stream_links: {e}") + pytest.fail(f"Contract test failed for create_webhook_replay_job: {e}") - def test_get_stream_links_required_parameters(self): - """Test that get_stream_links handles parameters correctly.""" - method = getattr(self.webhooks_client, "get_stream_links") - # No required parameters, method should be callable without args + def test_create_webhook_replay_job_required_parameters(self): + """Test that create_webhook_replay_job handles parameters correctly.""" + method = getattr(self.webhooks_client, "create_webhook_replay_job") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response - try: + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_session.post.return_value = mock_response + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_stream_links_response_structure(self): - """Test get_stream_links response structure validation.""" + def test_create_webhook_replay_job_response_structure(self): + """Test create_webhook_replay_job response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -359,12 +367,16 @@ def test_get_stream_links_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.post.return_value = mock_response # Prepare minimal valid parameters kwargs = {} # Add request body if required + # Import and create proper request model instance + from xdk.webhooks.models import CreateWebhookReplayJobRequest + # Create instance with minimal valid data (empty instance should work for most cases) + kwargs["body"] = CreateWebhookReplayJobRequest() # Call method and verify response structure - method = getattr(self.webhooks_client, "get_stream_links") + method = getattr(self.webhooks_client, "create_webhook_replay_job") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -610,8 +622,8 @@ def test_delete_stream_link_response_structure(self): ) - def test_create_webhook_replay_job_request_structure(self): - """Test create_webhook_replay_job request structure.""" + def test_get_stream_links_request_structure(self): + """Test get_stream_links request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -620,18 +632,14 @@ def test_create_webhook_replay_job_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters # Add request body if required - # Import and create proper request model instance - from xdk.webhooks.models import CreateWebhookReplayJobRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateWebhookReplayJobRequest() # Call the method try: - method = getattr(self.webhooks_client, "create_webhook_replay_job") + method = getattr(self.webhooks_client, "get_stream_links") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -650,7 +658,7 @@ def test_create_webhook_replay_job_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.get.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -659,14 +667,14 @@ def test_create_webhook_replay_job_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.get.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.get.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/webhooks/replay" + expected_path = "/2/tweets/search/webhooks" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -682,27 +690,27 @@ def test_create_webhook_replay_job_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create_webhook_replay_job: {e}") + pytest.fail(f"Contract test failed for get_stream_links: {e}") - def test_create_webhook_replay_job_required_parameters(self): - """Test that create_webhook_replay_job handles parameters correctly.""" - method = getattr(self.webhooks_client, "create_webhook_replay_job") - # Test with missing required parameters - mock the request to avoid network calls + def test_get_stream_links_required_parameters(self): + """Test that get_stream_links handles parameters correctly.""" + method = getattr(self.webhooks_client, "get_stream_links") + # No required parameters, method should be callable without args with patch.object(self.client, "session") as mock_session: - # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 400 - mock_response.json.return_value = {"error": "Missing required parameters"} - mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response - # Call without required parameters should either raise locally or via server response - with pytest.raises((TypeError, ValueError, Exception)): + mock_response.status_code = 200 + mock_response.json.return_value = {} + mock_response.raise_for_status.return_value = None + mock_session.get.return_value = mock_response + try: method() + except Exception as e: + pytest.fail(f"Method with no required params should be callable: {e}") - def test_create_webhook_replay_job_response_structure(self): - """Test create_webhook_replay_job response structure validation.""" + def test_get_stream_links_response_structure(self): + """Test get_stream_links response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -712,16 +720,12 @@ def test_create_webhook_replay_job_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.get.return_value = mock_response # Prepare minimal valid parameters kwargs = {} # Add request body if required - # Import and create proper request model instance - from xdk.webhooks.models import CreateWebhookReplayJobRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateWebhookReplayJobRequest() # Call method and verify response structure - method = getattr(self.webhooks_client, "create_webhook_replay_job") + method = getattr(self.webhooks_client, "get_stream_links") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -733,8 +737,8 @@ def test_create_webhook_replay_job_response_structure(self): ) - def test_get_request_structure(self): - """Test get request structure.""" + def test_validate_request_structure(self): + """Test validate request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -743,14 +747,15 @@ def test_get_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["webhook_id"] = "test_value" # Add request body if required # Call the method try: - method = getattr(self.webhooks_client, "get") + method = getattr(self.webhooks_client, "validate") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -769,7 +774,7 @@ def test_get_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.get.return_value = mock_streaming_response + mock_session.put.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -778,14 +783,14 @@ def test_get_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.get.assert_called_once() + mock_session.put.assert_called_once() # Verify request structure - call_args = mock_session.get.call_args + call_args = mock_session.put.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/webhooks" + expected_path = "/2/webhooks/{webhook_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -801,27 +806,27 @@ def test_get_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for get: {e}") + pytest.fail(f"Contract test failed for validate: {e}") - def test_get_required_parameters(self): - """Test that get handles parameters correctly.""" - method = getattr(self.webhooks_client, "get") - # No required parameters, method should be callable without args + def test_validate_required_parameters(self): + """Test that validate handles parameters correctly.""" + method = getattr(self.webhooks_client, "validate") + # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: + # Mock a 400 response (typical for missing required parameters) mock_response = Mock() - mock_response.status_code = 200 - mock_response.json.return_value = {} - mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response - try: + mock_response.status_code = 400 + mock_response.json.return_value = {"error": "Missing required parameters"} + mock_response.raise_for_status.side_effect = Exception("Bad Request") + mock_session.put.return_value = mock_response + # Call without required parameters should either raise locally or via server response + with pytest.raises((TypeError, ValueError, Exception)): method() - except Exception as e: - pytest.fail(f"Method with no required params should be callable: {e}") - def test_get_response_structure(self): - """Test get response structure validation.""" + def test_validate_response_structure(self): + """Test validate response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -831,12 +836,13 @@ def test_get_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.get.return_value = mock_response + mock_session.put.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["webhook_id"] = "test" # Add request body if required # Call method and verify response structure - method = getattr(self.webhooks_client, "get") + method = getattr(self.webhooks_client, "validate") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed @@ -848,8 +854,8 @@ def test_get_response_structure(self): ) - def test_create_request_structure(self): - """Test create request structure.""" + def test_delete_request_structure(self): + """Test delete request structure.""" # Mock the session to capture request details with patch.object(self.client, "session") as mock_session: mock_response = Mock() @@ -858,18 +864,15 @@ def test_create_request_structure(self): "data": None, } mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare test parameters kwargs = {} # Add required parameters + kwargs["webhook_id"] = "test_value" # Add request body if required - # Import and create proper request model instance - from xdk.webhooks.models import CreateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateRequest() # Call the method try: - method = getattr(self.webhooks_client, "create") + method = getattr(self.webhooks_client, "delete") result = method(**kwargs) # Check if this is a streaming operation (returns Generator) import types @@ -888,7 +891,7 @@ def test_create_request_structure(self): test_data = '{"data": "test"}\n' mock_streaming_response.iter_content.return_value = [test_data] # Update the session mock to return our streaming response - mock_session.post.return_value = mock_streaming_response + mock_session.delete.return_value = mock_streaming_response # Consume the generator to trigger the HTTP request try: next(result) @@ -897,14 +900,14 @@ def test_create_request_structure(self): except Exception: pass # Ignore other exceptions in test data processing # Verify the request was made - mock_session.post.assert_called_once() + mock_session.delete.assert_called_once() # Verify request structure - call_args = mock_session.post.call_args + call_args = mock_session.delete.call_args # Check URL structure called_url = ( call_args[0][0] if call_args[0] else call_args[1].get("url", "") ) - expected_path = "/2/webhooks" + expected_path = "/2/webhooks/{webhook_id}" assert expected_path.replace("{", "").replace( "}", "" ) in called_url or any( @@ -920,12 +923,12 @@ def test_create_request_structure(self): # For regular operations, verify we got a result assert result is not None, "Method should return a result" except Exception as e: - pytest.fail(f"Contract test failed for create: {e}") + pytest.fail(f"Contract test failed for delete: {e}") - def test_create_required_parameters(self): - """Test that create handles parameters correctly.""" - method = getattr(self.webhooks_client, "create") + def test_delete_required_parameters(self): + """Test that delete handles parameters correctly.""" + method = getattr(self.webhooks_client, "delete") # Test with missing required parameters - mock the request to avoid network calls with patch.object(self.client, "session") as mock_session: # Mock a 400 response (typical for missing required parameters) @@ -933,14 +936,14 @@ def test_create_required_parameters(self): mock_response.status_code = 400 mock_response.json.return_value = {"error": "Missing required parameters"} mock_response.raise_for_status.side_effect = Exception("Bad Request") - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Call without required parameters should either raise locally or via server response with pytest.raises((TypeError, ValueError, Exception)): method() - def test_create_response_structure(self): - """Test create response structure validation.""" + def test_delete_response_structure(self): + """Test delete response structure validation.""" with patch.object(self.client, "session") as mock_session: # Create mock response with expected structure mock_response_data = { @@ -950,16 +953,13 @@ def test_create_response_structure(self): mock_response.status_code = 200 mock_response.json.return_value = mock_response_data mock_response.raise_for_status.return_value = None - mock_session.post.return_value = mock_response + mock_session.delete.return_value = mock_response # Prepare minimal valid parameters kwargs = {} + kwargs["webhook_id"] = "test" # Add request body if required - # Import and create proper request model instance - from xdk.webhooks.models import CreateRequest - # Create instance with minimal valid data (empty instance should work for most cases) - kwargs["body"] = CreateRequest() # Call method and verify response structure - method = getattr(self.webhooks_client, "create") + method = getattr(self.webhooks_client, "delete") result = method(**kwargs) # Verify response object has expected attributes # Optional field - just check it doesn't cause errors if accessed diff --git a/tests/webhooks/test_structure.py b/tests/webhooks/test_structure.py index 3aa45c8..0c9aec0 100644 --- a/tests/webhooks/test_structure.py +++ b/tests/webhooks/test_structure.py @@ -28,31 +28,31 @@ def setup_class(self): self.webhooks_client = getattr(self.client, "webhooks") - def test_validate_exists(self): - """Test that validate method exists with correct signature.""" + def test_get_exists(self): + """Test that get method exists with correct signature.""" # Check method exists - method = getattr(WebhooksClient, "validate", None) - assert method is not None, f"Method validate does not exist on WebhooksClient" + method = getattr(WebhooksClient, "get", None) + assert method is not None, f"Method get does not exist on WebhooksClient" # Check method is callable - assert callable(method), f"validate is not callable" + assert callable(method), f"get is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"validate should have at least 'self' parameter" + assert len(params) >= 1, f"get should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "webhook_id", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from validate" + ), f"Required parameter '{required_param}' missing from get" # Check optional parameters have defaults (excluding 'self') - optional_params = [] + optional_params = [ + "webhook_config.fields", + ] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -61,39 +61,37 @@ def test_validate_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_validate_return_annotation(self): - """Test that validate has proper return type annotation.""" - method = getattr(WebhooksClient, "validate") + def test_get_return_annotation(self): + """Test that get has proper return type annotation.""" + method = getattr(WebhooksClient, "get") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method validate should have return type annotation" + ), f"Method get should have return type annotation" - def test_delete_exists(self): - """Test that delete method exists with correct signature.""" + def test_create_exists(self): + """Test that create method exists with correct signature.""" # Check method exists - method = getattr(WebhooksClient, "delete", None) - assert method is not None, f"Method delete does not exist on WebhooksClient" + method = getattr(WebhooksClient, "create", None) + assert method is not None, f"Method create does not exist on WebhooksClient" # Check method is callable - assert callable(method), f"delete is not callable" + assert callable(method), f"create is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"delete should have at least 'self' parameter" + assert len(params) >= 1, f"create should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [ - "webhook_id", - ] + required_params = [] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from delete" + ), f"Required parameter '{required_param}' missing from create" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -104,32 +102,32 @@ def test_delete_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_delete_return_annotation(self): - """Test that delete has proper return type annotation.""" - method = getattr(WebhooksClient, "delete") + def test_create_return_annotation(self): + """Test that create has proper return type annotation.""" + method = getattr(WebhooksClient, "create") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method delete should have return type annotation" + ), f"Method create should have return type annotation" - def test_get_stream_links_exists(self): - """Test that get_stream_links method exists with correct signature.""" + def test_create_webhook_replay_job_exists(self): + """Test that create_webhook_replay_job method exists with correct signature.""" # Check method exists - method = getattr(WebhooksClient, "get_stream_links", None) + method = getattr(WebhooksClient, "create_webhook_replay_job", None) assert ( method is not None - ), f"Method get_stream_links does not exist on WebhooksClient" + ), f"Method create_webhook_replay_job does not exist on WebhooksClient" # Check method is callable - assert callable(method), f"get_stream_links is not callable" + assert callable(method), f"create_webhook_replay_job is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"get_stream_links should have at least 'self' parameter" + ), f"create_webhook_replay_job should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -138,7 +136,7 @@ def test_get_stream_links_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get_stream_links" + ), f"Required parameter '{required_param}' missing from create_webhook_replay_job" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -149,14 +147,14 @@ def test_get_stream_links_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_stream_links_return_annotation(self): - """Test that get_stream_links has proper return type annotation.""" - method = getattr(WebhooksClient, "get_stream_links") + def test_create_webhook_replay_job_return_annotation(self): + """Test that create_webhook_replay_job has proper return type annotation.""" + method = getattr(WebhooksClient, "create_webhook_replay_job") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get_stream_links should have return type annotation" + ), f"Method create_webhook_replay_job should have return type annotation" def test_create_stream_link_exists(self): @@ -260,22 +258,22 @@ def test_delete_stream_link_return_annotation(self): ), f"Method delete_stream_link should have return type annotation" - def test_create_webhook_replay_job_exists(self): - """Test that create_webhook_replay_job method exists with correct signature.""" + def test_get_stream_links_exists(self): + """Test that get_stream_links method exists with correct signature.""" # Check method exists - method = getattr(WebhooksClient, "create_webhook_replay_job", None) + method = getattr(WebhooksClient, "get_stream_links", None) assert ( method is not None - ), f"Method create_webhook_replay_job does not exist on WebhooksClient" + ), f"Method get_stream_links does not exist on WebhooksClient" # Check method is callable - assert callable(method), f"create_webhook_replay_job is not callable" + assert callable(method), f"get_stream_links is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter assert ( len(params) >= 1 - ), f"create_webhook_replay_job should have at least 'self' parameter" + ), f"get_stream_links should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" @@ -284,7 +282,7 @@ def test_create_webhook_replay_job_exists(self): for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from create_webhook_replay_job" + ), f"Required parameter '{required_param}' missing from get_stream_links" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -295,41 +293,41 @@ def test_create_webhook_replay_job_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_create_webhook_replay_job_return_annotation(self): - """Test that create_webhook_replay_job has proper return type annotation.""" - method = getattr(WebhooksClient, "create_webhook_replay_job") + def test_get_stream_links_return_annotation(self): + """Test that get_stream_links has proper return type annotation.""" + method = getattr(WebhooksClient, "get_stream_links") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method create_webhook_replay_job should have return type annotation" + ), f"Method get_stream_links should have return type annotation" - def test_get_exists(self): - """Test that get method exists with correct signature.""" + def test_validate_exists(self): + """Test that validate method exists with correct signature.""" # Check method exists - method = getattr(WebhooksClient, "get", None) - assert method is not None, f"Method get does not exist on WebhooksClient" + method = getattr(WebhooksClient, "validate", None) + assert method is not None, f"Method validate does not exist on WebhooksClient" # Check method is callable - assert callable(method), f"get is not callable" + assert callable(method), f"validate is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"get should have at least 'self' parameter" + assert len(params) >= 1, f"validate should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "webhook_id", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from get" + ), f"Required parameter '{required_param}' missing from validate" # Check optional parameters have defaults (excluding 'self') - optional_params = [ - "webhook_config.fields", - ] + optional_params = [] for optional_param in optional_params: if optional_param in params: param_obj = sig.parameters[optional_param] @@ -338,37 +336,39 @@ def test_get_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_get_return_annotation(self): - """Test that get has proper return type annotation.""" - method = getattr(WebhooksClient, "get") + def test_validate_return_annotation(self): + """Test that validate has proper return type annotation.""" + method = getattr(WebhooksClient, "validate") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method get should have return type annotation" + ), f"Method validate should have return type annotation" - def test_create_exists(self): - """Test that create method exists with correct signature.""" + def test_delete_exists(self): + """Test that delete method exists with correct signature.""" # Check method exists - method = getattr(WebhooksClient, "create", None) - assert method is not None, f"Method create does not exist on WebhooksClient" + method = getattr(WebhooksClient, "delete", None) + assert method is not None, f"Method delete does not exist on WebhooksClient" # Check method is callable - assert callable(method), f"create is not callable" + assert callable(method), f"delete is not callable" # Check method signature sig = inspect.signature(method) params = list(sig.parameters.keys()) # Should have 'self' as first parameter - assert len(params) >= 1, f"create should have at least 'self' parameter" + assert len(params) >= 1, f"delete should have at least 'self' parameter" assert ( params[0] == "self" ), f"First parameter should be 'self', got '{params[0]}'" # Check required parameters exist (excluding 'self') - required_params = [] + required_params = [ + "webhook_id", + ] for required_param in required_params: assert ( required_param in params - ), f"Required parameter '{required_param}' missing from create" + ), f"Required parameter '{required_param}' missing from delete" # Check optional parameters have defaults (excluding 'self') optional_params = [] for optional_param in optional_params: @@ -379,27 +379,27 @@ def test_create_exists(self): ), f"Optional parameter '{optional_param}' should have a default value" - def test_create_return_annotation(self): - """Test that create has proper return type annotation.""" - method = getattr(WebhooksClient, "create") + def test_delete_return_annotation(self): + """Test that delete has proper return type annotation.""" + method = getattr(WebhooksClient, "delete") sig = inspect.signature(method) # Check return annotation exists assert ( sig.return_annotation is not inspect.Signature.empty - ), f"Method create should have return type annotation" + ), f"Method delete should have return type annotation" def test_all_expected_methods_exist(self): """Test that all expected methods exist on the client.""" expected_methods = [ - "validate", - "delete", - "get_stream_links", - "create_stream_link", - "delete_stream_link", - "create_webhook_replay_job", "get", "create", + "create_webhook_replay_job", + "create_stream_link", + "delete_stream_link", + "get_stream_links", + "validate", + "delete", ] for expected_method in expected_methods: assert hasattr( diff --git a/uv.lock b/uv.lock index 6a3141f..1252f31 100644 --- a/uv.lock +++ b/uv.lock @@ -2211,7 +2211,7 @@ wheels = [ [[package]] name = "xdk" -version = "0.4.1" +version = "0.4.2" source = { editable = "." } dependencies = [ { name = "pydantic", version = "2.10.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, diff --git a/xdk/account_activity/client.py b/xdk/account_activity/client.py index f94dedb..a90083a 100644 --- a/xdk/account_activity/client.py +++ b/xdk/account_activity/client.py @@ -21,13 +21,13 @@ if TYPE_CHECKING: from ..client import Client from .models import ( + CreateReplayJobResponse, ValidateSubscriptionResponse, CreateSubscriptionRequest, CreateSubscriptionResponse, GetSubscriptionsResponse, - CreateReplayJobResponse, - DeleteSubscriptionResponse, GetSubscriptionCountResponse, + DeleteSubscriptionResponse, ) @@ -39,6 +39,54 @@ def __init__(self, client: Client): self.client = client + def create_replay_job( + self, webhook_id: Any, from_date: str, to_date: str + ) -> CreateReplayJobResponse: + """ + Create replay job + Creates a replay job to retrieve activities from up to the past 5 days for all subscriptions associated with a given webhook. + Args: + webhook_id: The unique identifier for the webhook configuration. + from_date: The oldest (starting) UTC timestamp (inclusive) from which events will be provided, in `yyyymmddhhmm` format. + to_date: The latest (ending) UTC timestamp (exclusive) up to which events will be provided, in `yyyymmddhhmm` format. + Returns: + CreateReplayJobResponse: Response data + """ + url = ( + self.client.base_url + + "/2/account_activity/replay/webhooks/{webhook_id}/subscriptions/all" + ) + url = url.replace("{webhook_id}", str(webhook_id)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + params = {} + if from_date is not None: + params["from_date"] = from_date + if to_date is not None: + params["to_date"] = to_date + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.post( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return CreateReplayJobResponse.model_validate(response_data) + + def validate_subscription(self, webhook_id: Any) -> ValidateSubscriptionResponse: """ Validate subscription @@ -179,24 +227,16 @@ def get_subscriptions(self, webhook_id: Any) -> GetSubscriptionsResponse: return GetSubscriptionsResponse.model_validate(response_data) - def create_replay_job( - self, webhook_id: Any, from_date: str, to_date: str - ) -> CreateReplayJobResponse: + def get_subscription_count( + self, + ) -> GetSubscriptionCountResponse: """ - Create replay job - Creates a replay job to retrieve activities from up to the past 5 days for all subscriptions associated with a given webhook. - Args: - webhook_id: The unique identifier for the webhook configuration. - from_date: The oldest (starting) UTC timestamp (inclusive) from which events will be provided, in `yyyymmddhhmm` format. - to_date: The latest (ending) UTC timestamp (exclusive) up to which events will be provided, in `yyyymmddhhmm` format. - Returns: - CreateReplayJobResponse: Response data + Get subscription count + Retrieves a count of currently active Account Activity subscriptions. + Returns: + GetSubscriptionCountResponse: Response data """ - url = ( - self.client.base_url - + "/2/account_activity/replay/webhooks/{webhook_id}/subscriptions/all" - ) - url = url.replace("{webhook_id}", str(webhook_id)) + url = self.client.base_url + "/2/account_activity/subscriptions/count" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -206,15 +246,11 @@ def create_replay_job( f"Bearer {self.client.access_token}" ) params = {} - if from_date is not None: - params["from_date"] = from_date - if to_date is not None: - params["to_date"] = to_date headers = {} # Prepare request data json_data = None # Make the request - response = self.client.session.post( + response = self.client.session.get( url, params=params, headers=headers, @@ -224,7 +260,7 @@ def create_replay_job( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return CreateReplayJobResponse.model_validate(response_data) + return GetSubscriptionCountResponse.model_validate(response_data) def delete_subscription( @@ -269,39 +305,3 @@ def delete_subscription( response_data = response.json() # Convert to Pydantic model if applicable return DeleteSubscriptionResponse.model_validate(response_data) - - - def get_subscription_count( - self, - ) -> GetSubscriptionCountResponse: - """ - Get subscription count - Retrieves a count of currently active Account Activity subscriptions. - Returns: - GetSubscriptionCountResponse: Response data - """ - url = self.client.base_url + "/2/account_activity/subscriptions/count" - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - params = {} - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return GetSubscriptionCountResponse.model_validate(response_data) diff --git a/xdk/account_activity/models.py b/xdk/account_activity/models.py index 7bee016..05545eb 100644 --- a/xdk/account_activity/models.py +++ b/xdk/account_activity/models.py @@ -16,6 +16,15 @@ from datetime import datetime +# Models for create_replay_job + + +class CreateReplayJobResponse(BaseModel): + """Response model for create_replay_job""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + # Models for validate_subscription @@ -49,11 +58,11 @@ class GetSubscriptionsResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for create_replay_job +# Models for get_subscription_count -class CreateReplayJobResponse(BaseModel): - """Response model for create_replay_job""" +class GetSubscriptionCountResponse(BaseModel): + """Response model for get_subscription_count""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -65,12 +74,3 @@ class DeleteSubscriptionResponse(BaseModel): """Response model for delete_subscription""" model_config = ConfigDict(populate_by_name=True, extra="allow") - - -# Models for get_subscription_count - - -class GetSubscriptionCountResponse(BaseModel): - """Response model for get_subscription_count""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/activity/client.py b/xdk/activity/client.py index f9b5c77..0c13490 100644 --- a/xdk/activity/client.py +++ b/xdk/activity/client.py @@ -34,10 +34,10 @@ GetSubscriptionsResponse, CreateSubscriptionRequest, CreateSubscriptionResponse, - StreamResponse, UpdateSubscriptionRequest, UpdateSubscriptionResponse, DeleteSubscriptionResponse, + StreamResponse, ) @@ -130,6 +130,91 @@ def create_subscription( return CreateSubscriptionResponse.model_validate(response_data) + def update_subscription( + self, subscription_id: Any, body: Optional[UpdateSubscriptionRequest] = None + ) -> UpdateSubscriptionResponse: + """ + Update X activity subscription + Updates a subscription for an X activity event + Args: + subscription_id: The ID of the subscription to update. + body: Request body + Returns: + UpdateSubscriptionResponse: Response data + """ + url = self.client.base_url + "/2/activity/subscriptions/{subscription_id}" + url = url.replace("{subscription_id}", str(subscription_id)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + params = {} + headers = {} + headers["Content-Type"] = "application/json" + # Prepare request data + json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) + # Make the request + response = self.client.session.put( + url, + params=params, + headers=headers, + json=json_data, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return UpdateSubscriptionResponse.model_validate(response_data) + + + def delete_subscription(self, subscription_id: Any) -> DeleteSubscriptionResponse: + """ + Deletes X activity subscription + Deletes a subscription for an X activity event + Args: + subscription_id: The ID of the subscription to delete. + Returns: + DeleteSubscriptionResponse: Response data + """ + url = self.client.base_url + "/2/activity/subscriptions/{subscription_id}" + url = url.replace("{subscription_id}", str(subscription_id)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + params = {} + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.delete( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return DeleteSubscriptionResponse.model_validate(response_data) + + def stream( self, backfill_minutes: int = None, @@ -226,88 +311,3 @@ def stream( raise except Exception: raise - - - def update_subscription( - self, subscription_id: Any, body: Optional[UpdateSubscriptionRequest] = None - ) -> UpdateSubscriptionResponse: - """ - Update X activity subscription - Updates a subscription for an X activity event - Args: - subscription_id: The ID of the subscription to update. - body: Request body - Returns: - UpdateSubscriptionResponse: Response data - """ - url = self.client.base_url + "/2/activity/subscriptions/{subscription_id}" - url = url.replace("{subscription_id}", str(subscription_id)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - params = {} - headers = {} - headers["Content-Type"] = "application/json" - # Prepare request data - json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) - # Make the request - response = self.client.session.put( - url, - params=params, - headers=headers, - json=json_data, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return UpdateSubscriptionResponse.model_validate(response_data) - - - def delete_subscription(self, subscription_id: Any) -> DeleteSubscriptionResponse: - """ - Deletes X activity subscription - Deletes a subscription for an X activity event - Args: - subscription_id: The ID of the subscription to delete. - Returns: - DeleteSubscriptionResponse: Response data - """ - url = self.client.base_url + "/2/activity/subscriptions/{subscription_id}" - url = url.replace("{subscription_id}", str(subscription_id)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - params = {} - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.delete( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return DeleteSubscriptionResponse.model_validate(response_data) diff --git a/xdk/activity/models.py b/xdk/activity/models.py index 0ec34be..edda44c 100644 --- a/xdk/activity/models.py +++ b/xdk/activity/models.py @@ -40,15 +40,6 @@ class CreateSubscriptionResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for stream - - -class StreamResponse(BaseModel): - """Response model for stream""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - # Models for update_subscription @@ -71,3 +62,12 @@ class DeleteSubscriptionResponse(BaseModel): """Response model for delete_subscription""" model_config = ConfigDict(populate_by_name=True, extra="allow") + + +# Models for stream + + +class StreamResponse(BaseModel): + """Response model for stream""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/client.py b/xdk/client.py index 847a43c..eee577b 100644 --- a/xdk/client.py +++ b/xdk/client.py @@ -17,41 +17,41 @@ from .oauth2_auth import OAuth2PKCEAuth from .paginator import Cursor, cursor, PaginationError -from .connections.client import ConnectionsClient - -from .trends.client import TrendsClient +from .news.client import NewsClient -from .direct_messages.client import DirectMessagesClient +from .spaces.client import SpacesClient -from .users.client import UsersClient +from .media.client import MediaClient -from .posts.client import PostsClient +from .general.client import GeneralClient -from .spaces.client import SpacesClient +from .activity.client import ActivityClient -from .news.client import NewsClient +from .users.client import UsersClient -from .compliance.client import ComplianceClient +from .communities.client import CommunitiesClient -from .usage.client import UsageClient +from .direct_messages.client import DirectMessagesClient -from .media.client import MediaClient +from .trends.client import TrendsClient from .account_activity.client import AccountActivityClient -from .community_notes.client import CommunityNotesClient +from .compliance.client import ComplianceClient + +from .stream.client import StreamClient from .lists.client import ListsClient -from .webhooks.client import WebhooksClient +from .connections.client import ConnectionsClient -from .communities.client import CommunitiesClient +from .posts.client import PostsClient -from .activity.client import ActivityClient +from .usage.client import UsageClient -from .stream.client import StreamClient +from .webhooks.client import WebhooksClient -from .general.client import GeneralClient +from .community_notes.client import CommunityNotesClient class Client: @@ -81,7 +81,7 @@ def __init__( authorization_base_url: The base URL for OAuth2 authorization (defaults to https://x.com/i). """ self.session = requests.Session() - self.session.headers.update({"User-Agent": "xdk-python/0.4.1"}) + self.session.headers.update({"User-Agent": "xdk-python/0.4.2"}) self.base_url = base_url self.bearer_token = bearer_token # Set up OAuth2 PKCE authentication if credentials are provided @@ -97,24 +97,24 @@ def __init__( scope=scope, ) # Initialize clients for each tag - self.connections = ConnectionsClient(self) - self.trends = TrendsClient(self) - self.direct_messages = DirectMessagesClient(self) - self.users = UsersClient(self) - self.posts = PostsClient(self) - self.spaces = SpacesClient(self) self.news = NewsClient(self) - self.compliance = ComplianceClient(self) - self.usage = UsageClient(self) + self.spaces = SpacesClient(self) self.media = MediaClient(self) + self.general = GeneralClient(self) + self.activity = ActivityClient(self) + self.users = UsersClient(self) + self.communities = CommunitiesClient(self) + self.direct_messages = DirectMessagesClient(self) + self.trends = TrendsClient(self) self.account_activity = AccountActivityClient(self) - self.community_notes = CommunityNotesClient(self) + self.compliance = ComplianceClient(self) + self.stream = StreamClient(self) self.lists = ListsClient(self) + self.connections = ConnectionsClient(self) + self.posts = PostsClient(self) + self.usage = UsageClient(self) self.webhooks = WebhooksClient(self) - self.communities = CommunitiesClient(self) - self.activity = ActivityClient(self) - self.stream = StreamClient(self) - self.general = GeneralClient(self) + self.community_notes = CommunityNotesClient(self) @property @@ -144,15 +144,38 @@ def access_token(self): return None - def get_authorization_url(self): - """Get the authorization URL for the OAuth2 PKCE flow.""" + def get_authorization_url(self, state=None): + """Get the authorization URL for the OAuth2 PKCE flow. + Args: + state: Optional state parameter for security. + Returns: + str: The authorization URL. + """ if not self.oauth2_auth: raise ValueError("OAuth2 credentials not configured") - return self.oauth2_auth.get_authorization_url() + return self.oauth2_auth.get_authorization_url(state) + + + def exchange_code(self, code, code_verifier=None): + """Exchange authorization code for tokens (matches TypeScript API). + Args: + code: The authorization code from the callback. + code_verifier: Optional code verifier (uses stored verifier if not provided). + Returns: + Dict[str, Any]: The token dictionary + """ + if not self.oauth2_auth: + raise ValueError("OAuth2 credentials not configured") + return self.oauth2_auth.exchange_code(code, code_verifier) def fetch_token(self, authorization_response): - """Fetch token using authorization response.""" + """Fetch token using authorization response URL (legacy method). + Args: + authorization_response: The full callback URL received after authorization. + Returns: + Dict[str, Any]: The token dictionary + """ if not self.oauth2_auth: raise ValueError("OAuth2 credentials not configured") return self.oauth2_auth.fetch_token(authorization_response) diff --git a/xdk/community_notes/client.py b/xdk/community_notes/client.py index af5ad2d..8cdcbbb 100644 --- a/xdk/community_notes/client.py +++ b/xdk/community_notes/client.py @@ -21,13 +21,13 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - EvaluateRequest, - EvaluateResponse, + SearchEligiblePostsResponse, + DeleteResponse, CreateRequest, CreateResponse, + EvaluateRequest, + EvaluateResponse, SearchWrittenResponse, - SearchEligiblePostsResponse, - DeleteResponse, ) @@ -39,52 +39,126 @@ def __init__(self, client: Client): self.client = client - def evaluate(self, body: Optional[EvaluateRequest] = None) -> EvaluateResponse: + def search_eligible_posts( + self, + test_mode: bool, + pagination_token: str = None, + max_results: int = None, + post_selection: str = None, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + ) -> SearchEligiblePostsResponse: """ - Evaluate a Community Note - Endpoint to evaluate a community note. - body: Request body - Returns: - EvaluateResponse: Response data + Search for Posts Eligible for Community Notes + Returns all the posts that are eligible for community notes. + Args: + test_mode: If true, return a list of posts that are for the test. If false, return a list of posts that the bots can write proposed notes on the product. + pagination_token: Pagination token to get next set of posts eligible for notes. + max_results: Max results to return. + post_selection: The selection of posts to return. Valid values are 'feed_size: small' and 'feed_size: large'. Default is 'feed_size: small', only top AI writers have access to large size feed. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. + Returns: + SearchEligiblePostsResponse: Response data """ - url = self.client.base_url + "/2/evaluate_note" + url = self.client.base_url + "/2/notes/search/posts_eligible_for_notes" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if test_mode is not None: + params["test_mode"] = test_mode + if pagination_token is not None: + params["pagination_token"] = pagination_token + if max_results is not None: + params["max_results"] = max_results + if post_selection is not None: + params["post_selection"] = post_selection + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body + # Make the request + if self.client.oauth2_session: + response = self.client.oauth2_session.get( + url, + params=params, + headers=headers, + ) + else: + response = self.client.session.get( + url, + params=params, + headers=headers, ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return SearchEligiblePostsResponse.model_validate(response_data) + + + def delete(self, id: Any) -> DeleteResponse: + """ + Delete a Community Note + Deletes a community note. + Args: + id: The community note id to delete. + Returns: + DeleteResponse: Response data + """ + url = self.client.base_url + "/2/notes/{id}" + url = url.replace("{id}", str(id)) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + headers = {} + # Prepare request data + json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.post( + response = self.client.oauth2_session.delete( url, params=params, headers=headers, - json=json_data, ) else: - response = self.client.session.post( + response = self.client.session.delete( url, params=params, headers=headers, - json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return EvaluateResponse.model_validate(response_data) + return DeleteResponse.model_validate(response_data) def create(self, body: Optional[CreateRequest] = None) -> CreateResponse: @@ -135,94 +209,73 @@ def create(self, body: Optional[CreateRequest] = None) -> CreateResponse: return CreateResponse.model_validate(response_data) - def search_written( - self, - test_mode: bool, - pagination_token: str = None, - max_results: int = None, - note_fields: List = None, - ) -> SearchWrittenResponse: + def evaluate(self, body: Optional[EvaluateRequest] = None) -> EvaluateResponse: """ - Search for Community Notes Written - Returns all the community notes written by the user. - Args: - test_mode: If true, return the notes the caller wrote for the test. If false, return the notes the caller wrote on the product. - pagination_token: Pagination token to get next set of posts eligible for notes. - max_results: Max results to return. - note_fields: A comma separated list of Note fields to display. - Returns: - SearchWrittenResponse: Response data + Evaluate a Community Note + Endpoint to evaluate a community note. + body: Request body + Returns: + EvaluateResponse: Response data """ - url = self.client.base_url + "/2/notes/search/notes_written" + url = self.client.base_url + "/2/evaluate_note" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if test_mode is not None: - params["test_mode"] = test_mode - if pagination_token is not None: - params["pagination_token"] = pagination_token - if max_results is not None: - params["max_results"] = max_results - if note_fields is not None: - params["note.fields"] = ",".join(str(item) for item in note_fields) headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.get( + response = self.client.oauth2_session.post( url, params=params, headers=headers, + json=json_data, ) else: - response = self.client.session.get( + response = self.client.session.post( url, params=params, headers=headers, + json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return SearchWrittenResponse.model_validate(response_data) + return EvaluateResponse.model_validate(response_data) - def search_eligible_posts( + def search_written( self, test_mode: bool, pagination_token: str = None, max_results: int = None, - post_selection: str = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, - user_fields: List = None, - place_fields: List = None, - ) -> SearchEligiblePostsResponse: + note_fields: List = None, + ) -> SearchWrittenResponse: """ - Search for Posts Eligible for Community Notes - Returns all the posts that are eligible for community notes. + Search for Community Notes Written + Returns all the community notes written by the user. Args: - test_mode: If true, return a list of posts that are for the test. If false, return a list of posts that the bots can write proposed notes on the product. + test_mode: If true, return the notes the caller wrote for the test. If false, return the notes the caller wrote on the product. pagination_token: Pagination token to get next set of posts eligible for notes. max_results: Max results to return. - post_selection: The selection of posts to return. Valid values are 'feed_size: small' and 'feed_size: large'. Default is 'feed_size: small', only top AI writers have access to large size feed. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. - user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + note_fields: A comma separated list of Note fields to display. Returns: - SearchEligiblePostsResponse: Response data + SearchWrittenResponse: Response data """ - url = self.client.base_url + "/2/notes/search/posts_eligible_for_notes" + url = self.client.base_url + "/2/notes/search/notes_written" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -235,20 +288,8 @@ def search_eligible_posts( params["pagination_token"] = pagination_token if max_results is not None: params["max_results"] = max_results - if post_selection is not None: - params["post_selection"] = post_selection - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) + if note_fields is not None: + params["note.fields"] = ",".join(str(item) for item in note_fields) headers = {} # Prepare request data json_data = None @@ -270,45 +311,4 @@ def search_eligible_posts( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return SearchEligiblePostsResponse.model_validate(response_data) - - - def delete(self, id: Any) -> DeleteResponse: - """ - Delete a Community Note - Deletes a community note. - Args: - id: The community note id to delete. - Returns: - DeleteResponse: Response data - """ - url = self.client.base_url + "/2/notes/{id}" - url = url.replace("{id}", str(id)) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - headers = {} - # Prepare request data - json_data = None - # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.delete( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.delete( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return DeleteResponse.model_validate(response_data) + return SearchWrittenResponse.model_validate(response_data) diff --git a/xdk/community_notes/models.py b/xdk/community_notes/models.py index 5eb7a0f..82dcdb3 100644 --- a/xdk/community_notes/models.py +++ b/xdk/community_notes/models.py @@ -16,17 +16,20 @@ from datetime import datetime -# Models for evaluate +# Models for search_eligible_posts -class EvaluateRequest(BaseModel): - """Request model for evaluate""" +class SearchEligiblePostsResponse(BaseModel): + """Response model for search_eligible_posts""" - model_config = ConfigDict(populate_by_name=True) + model_config = ConfigDict(populate_by_name=True, extra="allow") -class EvaluateResponse(BaseModel): - """Response model for evaluate""" +# Models for delete + + +class DeleteResponse(BaseModel): + """Response model for delete""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -46,28 +49,25 @@ class CreateResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for search_written - - -class SearchWrittenResponse(BaseModel): - """Response model for search_written""" +# Models for evaluate - model_config = ConfigDict(populate_by_name=True, extra="allow") +class EvaluateRequest(BaseModel): + """Request model for evaluate""" -# Models for search_eligible_posts + model_config = ConfigDict(populate_by_name=True) -class SearchEligiblePostsResponse(BaseModel): - """Response model for search_eligible_posts""" +class EvaluateResponse(BaseModel): + """Response model for evaluate""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for delete +# Models for search_written -class DeleteResponse(BaseModel): - """Response model for delete""" +class SearchWrittenResponse(BaseModel): + """Response model for search_written""" model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/direct_messages/client.py b/xdk/direct_messages/client.py index 7a443ad..e41f31b 100644 --- a/xdk/direct_messages/client.py +++ b/xdk/direct_messages/client.py @@ -23,15 +23,15 @@ from .models import ( CreateConversationRequest, CreateConversationResponse, - CreateByConversationIdRequest, - CreateByConversationIdResponse, GetEventsByConversationIdResponse, + GetEventsResponse, GetEventsByIdResponse, DeleteEventsResponse, + CreateByConversationIdRequest, + CreateByConversationIdResponse, GetEventsByParticipantIdResponse, CreateByParticipantIdRequest, CreateByParticipantIdResponse, - GetEventsResponse, ) @@ -93,64 +93,84 @@ def create_conversation( return CreateConversationResponse.model_validate(response_data) - def create_by_conversation_id( + def get_events_by_conversation_id( self, - dm_conversation_id: str, - body: Optional[CreateByConversationIdRequest] = None, - ) -> Dict[str, Any]: + id: Any, + max_results: int = None, + pagination_token: Any = None, + event_types: List = None, + dm_event_fields: List = None, + expansions: List = None, + media_fields: List = None, + user_fields: List = None, + tweet_fields: List = None, + ) -> GetEventsByConversationIdResponse: """ - Create DM message by conversation ID - Sends a new direct message to a specific conversation by its ID. + Get DM events for a DM conversation + Retrieves direct message events for a specific conversation. Args: - dm_conversation_id: The DM Conversation ID. - body: Request body - Returns: - CreateByConversationIdResponse: Response data + id: The DM conversation ID. + max_results: The maximum number of results. + pagination_token: This parameter is used to get a specified 'page' of results. + event_types: The set of event_types to include in the results. + dm_event_fields: A comma separated list of DmEvent fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + user_fields: A comma separated list of User fields to display. + tweet_fields: A comma separated list of Tweet fields to display. + Returns: + GetEventsByConversationIdResponse: Response data """ - url = self.client.base_url + "/2/dm_conversations/{dm_conversation_id}/messages" - url = url.replace("{dm_conversation_id}", str(dm_conversation_id)) + url = self.client.base_url + "/2/dm_conversations/{id}/dm_events" + url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token + if event_types is not None: + params["event_types"] = ",".join(str(item) for item in event_types) + if dm_event_fields is not None: + params["dm_event.fields"] = ",".join(str(item) for item in dm_event_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.post( + response = self.client.oauth2_session.get( url, params=params, headers=headers, - json=json_data, ) else: - response = self.client.session.post( + response = self.client.session.get( url, params=params, headers=headers, - json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return CreateByConversationIdResponse.model_validate(response_data) + return GetEventsByConversationIdResponse.model_validate(response_data) - def get_events_by_conversation_id( + def get_events( self, - id: Any, max_results: int = None, pagination_token: Any = None, event_types: List = None, @@ -159,12 +179,11 @@ def get_events_by_conversation_id( media_fields: List = None, user_fields: List = None, tweet_fields: List = None, - ) -> GetEventsByConversationIdResponse: + ) -> GetEventsResponse: """ - Get DM events for a DM conversation - Retrieves direct message events for a specific conversation. + Get DM events + Retrieves a list of recent direct message events across all conversations. Args: - id: The DM conversation ID. max_results: The maximum number of results. pagination_token: This parameter is used to get a specified 'page' of results. event_types: The set of event_types to include in the results. @@ -174,10 +193,9 @@ def get_events_by_conversation_id( user_fields: A comma separated list of User fields to display. tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetEventsByConversationIdResponse: Response data + GetEventsResponse: Response data """ - url = self.client.base_url + "/2/dm_conversations/{id}/dm_events" - url = url.replace("{id}", str(id)) + url = self.client.base_url + "/2/dm_events" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -221,7 +239,7 @@ def get_events_by_conversation_id( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetEventsByConversationIdResponse.model_validate(response_data) + return GetEventsResponse.model_validate(response_data) def get_events_by_id( @@ -329,6 +347,61 @@ def delete_events(self, event_id: Any) -> DeleteEventsResponse: return DeleteEventsResponse.model_validate(response_data) + def create_by_conversation_id( + self, + dm_conversation_id: str, + body: Optional[CreateByConversationIdRequest] = None, + ) -> Dict[str, Any]: + """ + Create DM message by conversation ID + Sends a new direct message to a specific conversation by its ID. + Args: + dm_conversation_id: The DM Conversation ID. + body: Request body + Returns: + CreateByConversationIdResponse: Response data + """ + url = self.client.base_url + "/2/dm_conversations/{dm_conversation_id}/messages" + url = url.replace("{dm_conversation_id}", str(dm_conversation_id)) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + headers = {} + headers["Content-Type"] = "application/json" + # Prepare request data + json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) + # Make the request + if self.client.oauth2_session: + response = self.client.oauth2_session.post( + url, + params=params, + headers=headers, + json=json_data, + ) + else: + response = self.client.session.post( + url, + params=params, + headers=headers, + json=json_data, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return CreateByConversationIdResponse.model_validate(response_data) + + def get_events_by_participant_id( self, participant_id: Any, @@ -460,76 +533,3 @@ def create_by_participant_id( response_data = response.json() # Convert to Pydantic model if applicable return CreateByParticipantIdResponse.model_validate(response_data) - - - def get_events( - self, - max_results: int = None, - pagination_token: Any = None, - event_types: List = None, - dm_event_fields: List = None, - expansions: List = None, - media_fields: List = None, - user_fields: List = None, - tweet_fields: List = None, - ) -> GetEventsResponse: - """ - Get DM events - Retrieves a list of recent direct message events across all conversations. - Args: - max_results: The maximum number of results. - pagination_token: This parameter is used to get a specified 'page' of results. - event_types: The set of event_types to include in the results. - dm_event_fields: A comma separated list of DmEvent fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - user_fields: A comma separated list of User fields to display. - tweet_fields: A comma separated list of Tweet fields to display. - Returns: - GetEventsResponse: Response data - """ - url = self.client.base_url + "/2/dm_events" - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if event_types is not None: - params["event_types"] = ",".join(str(item) for item in event_types) - if dm_event_fields is not None: - params["dm_event.fields"] = ",".join(str(item) for item in dm_event_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - headers = {} - # Prepare request data - json_data = None - # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.get( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.get( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return GetEventsResponse.model_validate(response_data) diff --git a/xdk/direct_messages/models.py b/xdk/direct_messages/models.py index 27e469d..4ed5ff5 100644 --- a/xdk/direct_messages/models.py +++ b/xdk/direct_messages/models.py @@ -31,26 +31,20 @@ class CreateConversationResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for create_by_conversation_id - - -class CreateByConversationIdRequest(BaseModel): - """Request model for create_by_conversation_id""" - - model_config = ConfigDict(populate_by_name=True) +# Models for get_events_by_conversation_id -class CreateByConversationIdResponse(BaseModel): - """Response model for create_by_conversation_id""" +class GetEventsByConversationIdResponse(BaseModel): + """Response model for get_events_by_conversation_id""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_events_by_conversation_id +# Models for get_events -class GetEventsByConversationIdResponse(BaseModel): - """Response model for get_events_by_conversation_id""" +class GetEventsResponse(BaseModel): + """Response model for get_events""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -73,6 +67,21 @@ class DeleteEventsResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") +# Models for create_by_conversation_id + + +class CreateByConversationIdRequest(BaseModel): + """Request model for create_by_conversation_id""" + + model_config = ConfigDict(populate_by_name=True) + + +class CreateByConversationIdResponse(BaseModel): + """Response model for create_by_conversation_id""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + # Models for get_events_by_participant_id @@ -95,12 +104,3 @@ class CreateByParticipantIdResponse(BaseModel): """Response model for create_by_participant_id""" model_config = ConfigDict(populate_by_name=True, extra="allow") - - -# Models for get_events - - -class GetEventsResponse(BaseModel): - """Response model for get_events""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/lists/client.py b/xdk/lists/client.py index 1f3288e..42661e8 100644 --- a/xdk/lists/client.py +++ b/xdk/lists/client.py @@ -21,18 +21,18 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - GetPostsResponse, CreateRequest, CreateResponse, + GetPostsResponse, RemoveMemberByUserIdResponse, GetMembersResponse, AddMemberRequest, AddMemberResponse, - GetFollowersResponse, GetByIdResponse, UpdateRequest, UpdateResponse, DeleteResponse, + GetFollowersResponse, ) @@ -44,6 +44,54 @@ def __init__(self, client: Client): self.client = client + def create(self, body: Optional[CreateRequest] = None) -> CreateResponse: + """ + Create List + Creates a new List for the authenticated user. + body: Request body + Returns: + CreateResponse: Response data + """ + url = self.client.base_url + "/2/lists" + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + headers = {} + headers["Content-Type"] = "application/json" + # Prepare request data + json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) + # Make the request + if self.client.oauth2_session: + response = self.client.oauth2_session.post( + url, + params=params, + headers=headers, + json=json_data, + ) + else: + response = self.client.session.post( + url, + params=params, + headers=headers, + json=json_data, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return CreateResponse.model_validate(response_data) + + def get_posts( self, id: Any, @@ -121,54 +169,6 @@ def get_posts( return GetPostsResponse.model_validate(response_data) - def create(self, body: Optional[CreateRequest] = None) -> CreateResponse: - """ - Create List - Creates a new List for the authenticated user. - body: Request body - Returns: - CreateResponse: Response data - """ - url = self.client.base_url + "/2/lists" - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - headers = {} - headers["Content-Type"] = "application/json" - # Prepare request data - json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) - # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.post( - url, - params=params, - headers=headers, - json=json_data, - ) - else: - response = self.client.session.post( - url, - params=params, - headers=headers, - json=json_data, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return CreateResponse.model_validate(response_data) - - def remove_member_by_user_id( self, id: Any, user_id: Any ) -> RemoveMemberByUserIdResponse: @@ -332,71 +332,6 @@ def add_member( return AddMemberResponse.model_validate(response_data) - def get_followers( - self, - id: Any, - max_results: int = None, - pagination_token: Any = None, - user_fields: List = None, - expansions: List = None, - tweet_fields: List = None, - ) -> GetFollowersResponse: - """ - Get List followers - Retrieves a list of Users who follow a specific List by its ID. - Args: - id: The ID of the List. - max_results: The maximum number of results. - pagination_token: This parameter is used to get a specified 'page' of results. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. - tweet_fields: A comma separated list of Tweet fields to display. - Returns: - GetFollowersResponse: Response data - """ - url = self.client.base_url + "/2/lists/{id}/followers" - url = url.replace("{id}", str(id)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return GetFollowersResponse.model_validate(response_data) - - def get_by_id( self, id: Any, @@ -544,3 +479,68 @@ def delete(self, id: Any) -> DeleteResponse: response_data = response.json() # Convert to Pydantic model if applicable return DeleteResponse.model_validate(response_data) + + + def get_followers( + self, + id: Any, + max_results: int = None, + pagination_token: Any = None, + user_fields: List = None, + expansions: List = None, + tweet_fields: List = None, + ) -> GetFollowersResponse: + """ + Get List followers + Retrieves a list of Users who follow a specific List by its ID. + Args: + id: The ID of the List. + max_results: The maximum number of results. + pagination_token: This parameter is used to get a specified 'page' of results. + user_fields: A comma separated list of User fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. + Returns: + GetFollowersResponse: Response data + """ + url = self.client.base_url + "/2/lists/{id}/followers" + url = url.replace("{id}", str(id)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetFollowersResponse.model_validate(response_data) diff --git a/xdk/lists/models.py b/xdk/lists/models.py index 0a9f3ed..18dd919 100644 --- a/xdk/lists/models.py +++ b/xdk/lists/models.py @@ -16,15 +16,6 @@ from datetime import datetime -# Models for get_posts - - -class GetPostsResponse(BaseModel): - """Response model for get_posts""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - # Models for create @@ -40,6 +31,15 @@ class CreateResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") +# Models for get_posts + + +class GetPostsResponse(BaseModel): + """Response model for get_posts""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + # Models for remove_member_by_user_id @@ -73,15 +73,6 @@ class AddMemberResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_followers - - -class GetFollowersResponse(BaseModel): - """Response model for get_followers""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - # Models for get_by_id @@ -113,3 +104,12 @@ class DeleteResponse(BaseModel): """Response model for delete""" model_config = ConfigDict(populate_by_name=True, extra="allow") + + +# Models for get_followers + + +class GetFollowersResponse(BaseModel): + """Response model for get_followers""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/media/client.py b/xdk/media/client.py index b8745da..33978aa 100644 --- a/xdk/media/client.py +++ b/xdk/media/client.py @@ -21,23 +21,23 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - GetByKeyResponse, + AppendUploadRequest, + AppendUploadResponse, CreateSubtitlesRequest, CreateSubtitlesResponse, DeleteSubtitlesRequest, DeleteSubtitlesResponse, + FinalizeUploadResponse, GetAnalyticsResponse, + GetByKeysResponse, GetUploadStatusResponse, UploadRequest, UploadResponse, - AppendUploadRequest, - AppendUploadResponse, - GetByKeysResponse, - CreateMetadataRequest, - CreateMetadataResponse, + GetByKeyResponse, InitializeUploadRequest, InitializeUploadResponse, - FinalizeUploadResponse, + CreateMetadataRequest, + CreateMetadataResponse, ) @@ -49,49 +49,57 @@ def __init__(self, client: Client): self.client = client - def get_by_key(self, media_key: Any, media_fields: List = None) -> GetByKeyResponse: + def append_upload( + self, id: Any, body: Optional[AppendUploadRequest] = None + ) -> AppendUploadResponse: """ - Get Media by media key - Retrieves details of a specific Media file by its media key. + Append Media upload + Appends data to a Media upload request. Args: - media_key: A single Media Key. - media_fields: A comma separated list of Media fields to display. - Returns: - GetByKeyResponse: Response data + id: The media identifier for the media to perform the append operation. + body: Request body + Returns: + AppendUploadResponse: Response data """ - url = self.client.base_url + "/2/media/{media_key}" - url = url.replace("{media_key}", str(media_key)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) + url = self.client.base_url + "/2/media/upload/{id}/append" + url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) + if self.client.oauth2_session: + response = self.client.oauth2_session.post( + url, + params=params, + headers=headers, + json=json_data, + ) + else: + response = self.client.session.post( + url, + params=params, + headers=headers, + json=json_data, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetByKeyResponse.model_validate(response_data) + return AppendUploadResponse.model_validate(response_data) def create_subtitles( @@ -194,6 +202,47 @@ def delete_subtitles( return DeleteSubtitlesResponse.model_validate(response_data) + def finalize_upload(self, id: Any) -> FinalizeUploadResponse: + """ + Finalize Media upload + Finalizes a Media upload request. + Args: + id: The media id of the targeted media to finalize. + Returns: + FinalizeUploadResponse: Response data + """ + url = self.client.base_url + "/2/media/upload/{id}/finalize" + url = url.replace("{id}", str(id)) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + headers = {} + # Prepare request data + json_data = None + # Make the request + if self.client.oauth2_session: + response = self.client.oauth2_session.post( + url, + params=params, + headers=headers, + ) + else: + response = self.client.session.post( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return FinalizeUploadResponse.model_validate(response_data) + + def get_analytics( self, media_keys: List, @@ -257,6 +306,54 @@ def get_analytics( return GetAnalyticsResponse.model_validate(response_data) + def get_by_keys( + self, media_keys: List, media_fields: List = None + ) -> GetByKeysResponse: + """ + Get Media by media keys + Retrieves details of Media files by their media keys. + Args: + media_keys: A comma separated list of Media Keys. Up to 100 are allowed in a single request. + media_fields: A comma separated list of Media fields to display. + Returns: + GetByKeysResponse: Response data + """ + url = self.client.base_url + "/2/media" + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + if media_keys is not None: + params["media_keys"] = ",".join(str(item) for item in media_keys) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetByKeysResponse.model_validate(response_data) + + def get_upload_status( self, media_id: Any, command: str = None ) -> GetUploadStatusResponse: @@ -352,72 +449,18 @@ def upload(self, body: Optional[UploadRequest] = None) -> UploadResponse: return UploadResponse.model_validate(response_data) - def append_upload( - self, id: Any, body: Optional[AppendUploadRequest] = None - ) -> AppendUploadResponse: - """ - Append Media upload - Appends data to a Media upload request. - Args: - id: The media identifier for the media to perform the append operation. - body: Request body - Returns: - AppendUploadResponse: Response data - """ - url = self.client.base_url + "/2/media/upload/{id}/append" - url = url.replace("{id}", str(id)) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - headers = {} - headers["Content-Type"] = "application/json" - # Prepare request data - json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) - # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.post( - url, - params=params, - headers=headers, - json=json_data, - ) - else: - response = self.client.session.post( - url, - params=params, - headers=headers, - json=json_data, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return AppendUploadResponse.model_validate(response_data) - - - def get_by_keys( - self, media_keys: List, media_fields: List = None - ) -> GetByKeysResponse: + def get_by_key(self, media_key: Any, media_fields: List = None) -> GetByKeyResponse: """ - Get Media by media keys - Retrieves details of Media files by their media keys. + Get Media by media key + Retrieves details of a specific Media file by its media key. Args: - media_keys: A comma separated list of Media Keys. Up to 100 are allowed in a single request. + media_key: A single Media Key. media_fields: A comma separated list of Media fields to display. Returns: - GetByKeysResponse: Response data + GetByKeyResponse: Response data """ - url = self.client.base_url + "/2/media" + url = self.client.base_url + "/2/media/{media_key}" + url = url.replace("{media_key}", str(media_key)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -432,8 +475,6 @@ def get_by_keys( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if media_keys is not None: - params["media_keys"] = ",".join(str(item) for item in media_keys) if media_fields is not None: params["media.fields"] = ",".join(str(item) for item in media_fields) headers = {} @@ -450,20 +491,20 @@ def get_by_keys( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetByKeysResponse.model_validate(response_data) + return GetByKeyResponse.model_validate(response_data) - def create_metadata( - self, body: Optional[CreateMetadataRequest] = None - ) -> CreateMetadataResponse: + def initialize_upload( + self, body: Optional[InitializeUploadRequest] = None + ) -> InitializeUploadResponse: """ - Create Media metadata - Creates metadata for a Media file. + Initialize media upload + Initializes a media upload. body: Request body Returns: - CreateMetadataResponse: Response data + InitializeUploadResponse: Response data """ - url = self.client.base_url + "/2/media/metadata" + url = self.client.base_url + "/2/media/upload/initialize" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -500,20 +541,20 @@ def create_metadata( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return CreateMetadataResponse.model_validate(response_data) + return InitializeUploadResponse.model_validate(response_data) - def initialize_upload( - self, body: Optional[InitializeUploadRequest] = None - ) -> InitializeUploadResponse: + def create_metadata( + self, body: Optional[CreateMetadataRequest] = None + ) -> CreateMetadataResponse: """ - Initialize media upload - Initializes a media upload. + Create Media metadata + Creates metadata for a Media file. body: Request body Returns: - InitializeUploadResponse: Response data + CreateMetadataResponse: Response data """ - url = self.client.base_url + "/2/media/upload/initialize" + url = self.client.base_url + "/2/media/metadata" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -550,45 +591,4 @@ def initialize_upload( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return InitializeUploadResponse.model_validate(response_data) - - - def finalize_upload(self, id: Any) -> FinalizeUploadResponse: - """ - Finalize Media upload - Finalizes a Media upload request. - Args: - id: The media id of the targeted media to finalize. - Returns: - FinalizeUploadResponse: Response data - """ - url = self.client.base_url + "/2/media/upload/{id}/finalize" - url = url.replace("{id}", str(id)) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - headers = {} - # Prepare request data - json_data = None - # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.post( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.post( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return FinalizeUploadResponse.model_validate(response_data) + return CreateMetadataResponse.model_validate(response_data) diff --git a/xdk/media/models.py b/xdk/media/models.py index 330bf8b..884119b 100644 --- a/xdk/media/models.py +++ b/xdk/media/models.py @@ -16,11 +16,17 @@ from datetime import datetime -# Models for get_by_key +# Models for append_upload -class GetByKeyResponse(BaseModel): - """Response model for get_by_key""" +class AppendUploadRequest(BaseModel): + """Request model for append_upload""" + + model_config = ConfigDict(populate_by_name=True) + + +class AppendUploadResponse(BaseModel): + """Response model for append_upload""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -55,6 +61,15 @@ class DeleteSubtitlesResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") +# Models for finalize_upload + + +class FinalizeUploadResponse(BaseModel): + """Response model for finalize_upload""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + # Models for get_analytics @@ -64,6 +79,15 @@ class GetAnalyticsResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") +# Models for get_by_keys + + +class GetByKeysResponse(BaseModel): + """Response model for get_by_keys""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + # Models for get_upload_status @@ -88,26 +112,26 @@ class UploadResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for append_upload +# Models for get_by_key -class AppendUploadRequest(BaseModel): - """Request model for append_upload""" +class GetByKeyResponse(BaseModel): + """Response model for get_by_key""" - model_config = ConfigDict(populate_by_name=True) + model_config = ConfigDict(populate_by_name=True, extra="allow") -class AppendUploadResponse(BaseModel): - """Response model for append_upload""" +# Models for initialize_upload - model_config = ConfigDict(populate_by_name=True, extra="allow") +class InitializeUploadRequest(BaseModel): + """Request model for initialize_upload""" -# Models for get_by_keys + model_config = ConfigDict(populate_by_name=True) -class GetByKeysResponse(BaseModel): - """Response model for get_by_keys""" +class InitializeUploadResponse(BaseModel): + """Response model for initialize_upload""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -125,27 +149,3 @@ class CreateMetadataResponse(BaseModel): """Response model for create_metadata""" model_config = ConfigDict(populate_by_name=True, extra="allow") - - -# Models for initialize_upload - - -class InitializeUploadRequest(BaseModel): - """Request model for initialize_upload""" - - model_config = ConfigDict(populate_by_name=True) - - -class InitializeUploadResponse(BaseModel): - """Response model for initialize_upload""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - -# Models for finalize_upload - - -class FinalizeUploadResponse(BaseModel): - """Response model for finalize_upload""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/news/client.py b/xdk/news/client.py index c8c592c..f717d7f 100644 --- a/xdk/news/client.py +++ b/xdk/news/client.py @@ -21,8 +21,8 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - GetResponse, SearchResponse, + GetResponse, ) @@ -34,18 +34,25 @@ def __init__(self, client: Client): self.client = client - def get(self, id: Any, news_fields: List = None) -> GetResponse: + def search( + self, + query: str, + max_results: int = None, + max_age_hours: int = None, + news_fields: List = None, + ) -> SearchResponse: """ - Get news stories by ID - Retrieves news story by its ID. + Search News + Retrieves a list of News stories matching the specified search query. Args: - id: The ID of the news story. + query: The search query. + max_results: The number of results to return. + max_age_hours: The maximum age of the News story to search for. news_fields: A comma separated list of News fields to display. Returns: - GetResponse: Response data + SearchResponse: Response data """ - url = self.client.base_url + "/2/news/{id}" - url = url.replace("{id}", str(id)) + url = self.client.base_url + "/2/news/search" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -60,6 +67,12 @@ def get(self, id: Any, news_fields: List = None) -> GetResponse: if self.client.is_token_expired(): self.client.refresh_token() params = {} + if query is not None: + params["query"] = query + if max_results is not None: + params["max_results"] = max_results + if max_age_hours is not None: + params["max_age_hours"] = max_age_hours if news_fields is not None: params["news.fields"] = ",".join(str(item) for item in news_fields) headers = {} @@ -76,28 +89,21 @@ def get(self, id: Any, news_fields: List = None) -> GetResponse: # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetResponse.model_validate(response_data) + return SearchResponse.model_validate(response_data) - def search( - self, - query: str, - max_results: int = None, - max_age_hours: int = None, - news_fields: List = None, - ) -> SearchResponse: + def get(self, id: Any, news_fields: List = None) -> GetResponse: """ - Search News - Retrieves a list of News stories matching the specified search query. + Get news stories by ID + Retrieves news story by its ID. Args: - query: The search query. - max_results: The number of results to return. - max_age_hours: The maximum age of the News story to search for. + id: The ID of the news story. news_fields: A comma separated list of News fields to display. Returns: - SearchResponse: Response data + GetResponse: Response data """ - url = self.client.base_url + "/2/news/search" + url = self.client.base_url + "/2/news/{id}" + url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -112,12 +118,6 @@ def search( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if query is not None: - params["query"] = query - if max_results is not None: - params["max_results"] = max_results - if max_age_hours is not None: - params["max_age_hours"] = max_age_hours if news_fields is not None: params["news.fields"] = ",".join(str(item) for item in news_fields) headers = {} @@ -134,4 +134,4 @@ def search( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return SearchResponse.model_validate(response_data) + return GetResponse.model_validate(response_data) diff --git a/xdk/news/models.py b/xdk/news/models.py index 0256531..fb79195 100644 --- a/xdk/news/models.py +++ b/xdk/news/models.py @@ -16,19 +16,19 @@ from datetime import datetime -# Models for get +# Models for search -class GetResponse(BaseModel): - """Response model for get""" +class SearchResponse(BaseModel): + """Response model for search""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for search +# Models for get -class SearchResponse(BaseModel): - """Response model for search""" +class GetResponse(BaseModel): + """Response model for get""" model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/oauth2_auth.py b/xdk/oauth2_auth.py index 464c80e..228c13f 100644 --- a/xdk/oauth2_auth.py +++ b/xdk/oauth2_auth.py @@ -17,6 +17,8 @@ import time import urllib.parse from typing import Dict, Optional, Any, Tuple, Union, List +import requests +from requests.auth import HTTPBasicAuth from requests_oauthlib import OAuth2Session @@ -96,48 +98,123 @@ def _generate_code_challenge(self, code_verifier: str) -> str: return code_challenge - def get_authorization_url(self) -> Tuple[str, str]: + def set_pkce_parameters( + self, code_verifier: str, code_challenge: Optional[str] = None + ): + """Manually set PKCE parameters. + Args: + code_verifier: The code verifier to use. + code_challenge: Optional code challenge (will be generated if not provided). + """ + self.code_verifier = code_verifier + if code_challenge: + self.code_challenge = code_challenge + else: + self.code_challenge = self._generate_code_challenge(code_verifier) + + + def get_authorization_url(self, state: Optional[str] = None) -> str: """Get the authorization URL for the OAuth2 PKCE flow. + Args: + state: Optional state parameter for security. Returns: - tuple: (authorization_url, state) + str: The authorization URL. """ - self.code_verifier = self._generate_code_verifier() - self.code_challenge = self._generate_code_challenge(self.code_verifier) + # Auto-generate PKCE parameters if not already set + if not self.code_verifier or not self.code_challenge: + self.code_verifier = self._generate_code_verifier() + self.code_challenge = self._generate_code_challenge(self.code_verifier) self.oauth2_session = OAuth2Session( - client_id=self.client_id, redirect_uri=self.redirect_uri, scope=self.scope + client_id=self.client_id, + redirect_uri=self.redirect_uri, + scope=self.scope, + state=state, ) # Use authorization_base_url for authorization endpoint # base_url is used for API token endpoints - auth_url, state = self.oauth2_session.authorization_url( + auth_url, generated_state = self.oauth2_session.authorization_url( f"{self.authorization_base_url}/oauth2/authorize", code_challenge=self.code_challenge, code_challenge_method="S256", ) - return auth_url, state + return auth_url - def fetch_token(self, authorization_response: str) -> Dict[str, Any]: - """Fetch token using authorization response and code verifier. + def exchange_code( + self, code: str, code_verifier: Optional[str] = None + ) -> Dict[str, Any]: + """Exchange authorization code for tokens (matches TypeScript API). Args: - authorization_response: The full callback URL received after authorization + code: The authorization code from the callback. + code_verifier: Optional code verifier (uses stored verifier if not provided). Returns: Dict[str, Any]: The token dictionary """ - if not self.oauth2_session: + if not code_verifier: + code_verifier = self.code_verifier + if not code_verifier: raise ValueError( - "OAuth2 session not initialized. Call get_authorization_url first." + "Code verifier is required. Call get_authorization_url() or set_pkce_parameters() first." ) - self.token = self.oauth2_session.fetch_token( - f"{self.base_url}/2/oauth2/token", - authorization_response=authorization_response, - code_verifier=self.code_verifier, - client_id=self.client_id, - client_secret=self.client_secret, - include_client_id=True, + # Build the token exchange request manually to match TypeScript implementation + params = { + "grant_type": "authorization_code", + "code": code, + "redirect_uri": self.redirect_uri, + "code_verifier": code_verifier, + } + headers = {"Content-Type": "application/x-www-form-urlencoded"} + # Add Basic Auth header if client secret is provided (matches TypeScript) + auth = None + if self.client_secret: + auth = HTTPBasicAuth(self.client_id, self.client_secret) + else: + # Only add client_id to body if no client_secret (public client) + params["client_id"] = self.client_id + response = requests.post( + f"{self.base_url}/2/oauth2/token", data=params, headers=headers, auth=auth ) + if not response.ok: + try: + error_data = response.json() + except: + error_data = response.text + raise ValueError( + f"HTTP error! status: {response.status_code}, body: {error_data}" + ) + data = response.json() + self.token = { + "access_token": data.get("access_token"), + "token_type": data.get("token_type"), + "expires_in": data.get("expires_in"), + "refresh_token": data.get("refresh_token"), + "scope": data.get("scope"), + } + # Calculate expires_at if expires_in is provided + if "expires_in" in data and data["expires_in"]: + self.token["expires_at"] = time.time() + data["expires_in"] + # Set up OAuth2 session with the new token + if self.client_id: + self._setup_oauth_session() return self.token + def fetch_token(self, authorization_response: str) -> Dict[str, Any]: + """Fetch token using authorization response URL (legacy method, uses exchange_code internally). + Args: + authorization_response: The full callback URL received after authorization + Returns: + Dict[str, Any]: The token dictionary + """ + # Parse the authorization code from the callback URL + parsed = urllib.parse.urlparse(authorization_response) + query_params = urllib.parse.parse_qs(parsed.query) + if "code" not in query_params: + raise ValueError("No authorization code found in callback URL") + code = query_params["code"][0] + return self.exchange_code(code) + + def refresh_token(self) -> Dict[str, Any]: """Refresh the access token. Returns: @@ -151,6 +228,22 @@ def refresh_token(self) -> Dict[str, Any]: ) return self.token + + def get_code_verifier(self) -> Optional[str]: + """Get the current code verifier (for PKCE). + Returns: + Optional[str]: The current code verifier, or None if not set. + """ + return self.code_verifier + + + def get_code_challenge(self) -> Optional[str]: + """Get the current code challenge (for PKCE). + Returns: + Optional[str]: The current code challenge, or None if not set. + """ + return self.code_challenge + @property diff --git a/xdk/posts/client.py b/xdk/posts/client.py index 3e835ea..451d376 100644 --- a/xdk/posts/client.py +++ b/xdk/posts/client.py @@ -21,24 +21,24 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - SearchAllResponse, - GetByIdResponse, - DeleteResponse, + GetLikingUsersResponse, + GetQuotedResponse, GetCountsRecentResponse, HideReplyRequest, HideReplyResponse, - GetLikingUsersResponse, - GetQuotedResponse, - GetInsightsHistoricalResponse, + GetCountsAllResponse, GetAnalyticsResponse, - GetRepostedByResponse, - GetInsights28hrResponse, + SearchAllResponse, GetRepostsResponse, - SearchRecentResponse, + GetByIdResponse, + DeleteResponse, + GetInsights28hrResponse, GetByIdsResponse, CreateRequest, CreateResponse, - GetCountsAllResponse, + GetRepostedByResponse, + SearchRecentResponse, + GetInsightsHistoricalResponse, ) @@ -50,118 +50,91 @@ def __init__(self, client: Client): self.client = client - def search_all( + def get_liking_users( self, - query: str, - start_time: str = None, - end_time: str = None, - since_id: Any = None, - until_id: Any = None, + id: Any, max_results: int = None, - next_token: Any = None, pagination_token: Any = None, - sort_order: str = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, user_fields: List = None, - place_fields: List = None, - ) -> SearchAllResponse: + expansions: List = None, + tweet_fields: List = None, + ) -> GetLikingUsersResponse: """ - Search all Posts - Retrieves Posts from the full archive matching a search query. + Get Liking Users + Retrieves a list of Users who liked a specific Post by its ID. Args: - query: One query/rule/filter for matching Posts. Refer to https://t.co/rulelength to identify the max query length. - start_time: YYYY-MM-DDTHH:mm:ssZ. The oldest UTC timestamp from which the Posts will be provided. Timestamp is in second granularity and is inclusive (i.e. 12:00:01 includes the first second of the minute). - end_time: YYYY-MM-DDTHH:mm:ssZ. The newest, most recent UTC timestamp to which the Posts will be provided. Timestamp is in second granularity and is exclusive (i.e. 12:00:01 excludes the first second of the minute). - since_id: Returns results with a Post ID greater than (that is, more recent than) the specified ID. - until_id: Returns results with a Post ID less than (that is, older than) the specified ID. - max_results: The maximum number of search results to be returned by a request. - next_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. - pagination_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. - sort_order: This order in which to return results. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. + id: A single Post ID. + max_results: The maximum number of results. + pagination_token: This parameter is used to get the next 'page' of results. user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. Returns: - SearchAllResponse: Response data + GetLikingUsersResponse: Response data """ - url = self.client.base_url + "/2/tweets/search/all" - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) + url = self.client.base_url + "/2/tweets/{id}/liking_users" + url = url.replace("{id}", str(id)) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() params = {} - if query is not None: - params["query"] = query - if start_time is not None: - params["start_time"] = start_time - if end_time is not None: - params["end_time"] = end_time - if since_id is not None: - params["since_id"] = since_id - if until_id is not None: - params["until_id"] = until_id if max_results is not None: params["max_results"] = max_results - if next_token is not None: - params["next_token"] = next_token if pagination_token is not None: params["pagination_token"] = pagination_token - if sort_order is not None: - params["sort_order"] = sort_order - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) + if self.client.oauth2_session: + response = self.client.oauth2_session.get( + url, + params=params, + headers=headers, + ) + else: + response = self.client.session.get( + url, + params=params, + headers=headers, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return SearchAllResponse.model_validate(response_data) + return GetLikingUsersResponse.model_validate(response_data) - def get_by_id( + def get_quoted( self, id: Any, + max_results: int = None, + pagination_token: Any = None, + exclude: List = None, tweet_fields: List = None, expansions: List = None, media_fields: List = None, poll_fields: List = None, user_fields: List = None, place_fields: List = None, - ) -> GetByIdResponse: + ) -> GetQuotedResponse: """ - Get Post by ID - Retrieves details of a specific Post by its ID. + Get Quoted Posts + Retrieves a list of Posts that quote a specific Post by its ID. Args: id: A single Post ID. + max_results: The maximum number of results to be returned. + pagination_token: This parameter is used to get a specified 'page' of results. + exclude: The set of entities to exclude (e.g. 'replies' or 'retweets'). tweet_fields: A comma separated list of Tweet fields to display. expansions: A comma separated list of fields to expand. media_fields: A comma separated list of Media fields to display. @@ -169,9 +142,9 @@ def get_by_id( user_fields: A comma separated list of User fields to display. place_fields: A comma separated list of Place fields to display. Returns: - GetByIdResponse: Response data + GetQuotedResponse: Response data """ - url = self.client.base_url + "/2/tweets/{id}" + url = self.client.base_url + "/2/tweets/{id}/quote_tweets" url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( @@ -187,6 +160,12 @@ def get_by_id( if self.client.is_token_expired(): self.client.refresh_token() params = {} + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token + if exclude is not None: + params["exclude"] = ",".join(str(item) for item in exclude) if tweet_fields is not None: params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) if expansions is not None: @@ -213,48 +192,7 @@ def get_by_id( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetByIdResponse.model_validate(response_data) - - - def delete(self, id: Any) -> DeleteResponse: - """ - Delete Post - Deletes a specific Post by its ID, if owned by the authenticated user. - Args: - id: The ID of the Post to be deleted. - Returns: - DeleteResponse: Response data - """ - url = self.client.base_url + "/2/tweets/{id}" - url = url.replace("{id}", str(id)) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - headers = {} - # Prepare request data - json_data = None - # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.delete( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.delete( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return DeleteResponse.model_validate(response_data) + return GetQuotedResponse.model_validate(response_data) def get_counts_recent( @@ -385,46 +323,120 @@ def hide_reply( return HideReplyResponse.model_validate(response_data) - def get_liking_users( + def get_counts_all( self, - id: Any, - max_results: int = None, + query: str, + start_time: str = None, + end_time: str = None, + since_id: Any = None, + until_id: Any = None, + next_token: Any = None, pagination_token: Any = None, - user_fields: List = None, - expansions: List = None, - tweet_fields: List = None, - ) -> GetLikingUsersResponse: + granularity: str = None, + search_count_fields: List = None, + ) -> GetCountsAllResponse: """ - Get Liking Users - Retrieves a list of Users who liked a specific Post by its ID. + Get count of all Posts + Retrieves the count of Posts matching a search query from the full archive. Args: - id: A single Post ID. - max_results: The maximum number of results. - pagination_token: This parameter is used to get the next 'page' of results. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. - tweet_fields: A comma separated list of Tweet fields to display. + query: One query/rule/filter for matching Posts. Refer to https://t.co/rulelength to identify the max query length. + start_time: YYYY-MM-DDTHH:mm:ssZ. The oldest UTC timestamp (from most recent 7 days) from which the Posts will be provided. Timestamp is in second granularity and is inclusive (i.e. 12:00:01 includes the first second of the minute). + end_time: YYYY-MM-DDTHH:mm:ssZ. The newest, most recent UTC timestamp to which the Posts will be provided. Timestamp is in second granularity and is exclusive (i.e. 12:00:01 excludes the first second of the minute). + since_id: Returns results with a Post ID greater than (that is, more recent than) the specified ID. + until_id: Returns results with a Post ID less than (that is, older than) the specified ID. + next_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. + pagination_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. + granularity: The granularity for the search counts results. + search_count_fields: A comma separated list of SearchCount fields to display. Returns: - GetLikingUsersResponse: Response data + GetCountsAllResponse: Response data """ - url = self.client.base_url + "/2/tweets/{id}/liking_users" - url = url.replace("{id}", str(id)) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() + url = self.client.base_url + "/2/tweets/counts/all" + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if query is not None: + params["query"] = query + if start_time is not None: + params["start_time"] = start_time + if end_time is not None: + params["end_time"] = end_time + if since_id is not None: + params["since_id"] = since_id + if until_id is not None: + params["until_id"] = until_id + if next_token is not None: + params["next_token"] = next_token + if pagination_token is not None: + params["pagination_token"] = pagination_token + if granularity is not None: + params["granularity"] = granularity + if search_count_fields is not None: + params["search_count.fields"] = ",".join( + str(item) for item in search_count_fields + ) + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetCountsAllResponse.model_validate(response_data) + + + def get_analytics( + self, + ids: List, + end_time: str, + start_time: str, + granularity: str, + analytics_fields: List = None, + ) -> GetAnalyticsResponse: + """ + Get Post analytics + Retrieves analytics data for specified Posts within a defined time range. + Args: + ids: A comma separated list of Post IDs. Up to 100 are allowed in a single request. + end_time: YYYY-MM-DDTHH:mm:ssZ. The UTC timestamp representing the end of the time range. + start_time: YYYY-MM-DDTHH:mm:ssZ. The UTC timestamp representing the start of the time range. + granularity: The granularity for the search counts results. + analytics_fields: A comma separated list of Analytics fields to display. + Returns: + GetAnalyticsResponse: Response data + """ + url = self.client.base_url + "/2/tweets/analytics" + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + if ids is not None: + params["ids"] = ",".join(str(item) for item in ids) + if end_time is not None: + params["end_time"] = end_time + if start_time is not None: + params["start_time"] = start_time + if granularity is not None: + params["granularity"] = granularity + if analytics_fields is not None: + params["analytics.fields"] = ",".join( + str(item) for item in analytics_fields + ) headers = {} # Prepare request data json_data = None @@ -446,30 +458,125 @@ def get_liking_users( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetLikingUsersResponse.model_validate(response_data) + return GetAnalyticsResponse.model_validate(response_data) - def get_quoted( + def search_all( + self, + query: str, + start_time: str = None, + end_time: str = None, + since_id: Any = None, + until_id: Any = None, + max_results: int = None, + next_token: Any = None, + pagination_token: Any = None, + sort_order: str = None, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + ) -> SearchAllResponse: + """ + Search all Posts + Retrieves Posts from the full archive matching a search query. + Args: + query: One query/rule/filter for matching Posts. Refer to https://t.co/rulelength to identify the max query length. + start_time: YYYY-MM-DDTHH:mm:ssZ. The oldest UTC timestamp from which the Posts will be provided. Timestamp is in second granularity and is inclusive (i.e. 12:00:01 includes the first second of the minute). + end_time: YYYY-MM-DDTHH:mm:ssZ. The newest, most recent UTC timestamp to which the Posts will be provided. Timestamp is in second granularity and is exclusive (i.e. 12:00:01 excludes the first second of the minute). + since_id: Returns results with a Post ID greater than (that is, more recent than) the specified ID. + until_id: Returns results with a Post ID less than (that is, older than) the specified ID. + max_results: The maximum number of search results to be returned by a request. + next_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. + pagination_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. + sort_order: This order in which to return results. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. + Returns: + SearchAllResponse: Response data + """ + url = self.client.base_url + "/2/tweets/search/all" + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + params = {} + if query is not None: + params["query"] = query + if start_time is not None: + params["start_time"] = start_time + if end_time is not None: + params["end_time"] = end_time + if since_id is not None: + params["since_id"] = since_id + if until_id is not None: + params["until_id"] = until_id + if max_results is not None: + params["max_results"] = max_results + if next_token is not None: + params["next_token"] = next_token + if pagination_token is not None: + params["pagination_token"] = pagination_token + if sort_order is not None: + params["sort_order"] = sort_order + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return SearchAllResponse.model_validate(response_data) + + + def get_reposts( self, id: Any, max_results: int = None, pagination_token: Any = None, - exclude: List = None, tweet_fields: List = None, expansions: List = None, media_fields: List = None, poll_fields: List = None, user_fields: List = None, place_fields: List = None, - ) -> GetQuotedResponse: + ) -> GetRepostsResponse: """ - Get Quoted Posts - Retrieves a list of Posts that quote a specific Post by its ID. + Get Reposts + Retrieves a list of Posts that repost a specific Post by its ID. Args: id: A single Post ID. - max_results: The maximum number of results to be returned. - pagination_token: This parameter is used to get a specified 'page' of results. - exclude: The set of entities to exclude (e.g. 'replies' or 'retweets'). + max_results: The maximum number of results. + pagination_token: This parameter is used to get the next 'page' of results. tweet_fields: A comma separated list of Tweet fields to display. expansions: A comma separated list of fields to expand. media_fields: A comma separated list of Media fields to display. @@ -477,9 +584,9 @@ def get_quoted( user_fields: A comma separated list of User fields to display. place_fields: A comma separated list of Place fields to display. Returns: - GetQuotedResponse: Response data + GetRepostsResponse: Response data """ - url = self.client.base_url + "/2/tweets/{id}/quote_tweets" + url = self.client.base_url + "/2/tweets/{id}/retweets" url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( @@ -499,8 +606,6 @@ def get_quoted( params["max_results"] = max_results if pagination_token is not None: params["pagination_token"] = pagination_token - if exclude is not None: - params["exclude"] = ",".join(str(item) for item in exclude) if tweet_fields is not None: params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) if expansions is not None: @@ -527,66 +632,107 @@ def get_quoted( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetQuotedResponse.model_validate(response_data) + return GetRepostsResponse.model_validate(response_data) - def get_insights_historical( + def get_by_id( self, - tweet_ids: List, - end_time: str, - start_time: str, - granularity: str, - requested_metrics: List, - engagement_fields: List = None, - ) -> GetInsightsHistoricalResponse: + id: Any, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + ) -> GetByIdResponse: """ - Get historical Post insights - Retrieves historical engagement metrics for specified Posts within a defined time range. + Get Post by ID + Retrieves details of a specific Post by its ID. Args: - tweet_ids: List of PostIds for historical metrics. - end_time: YYYY-MM-DDTHH:mm:ssZ. The UTC timestamp representing the end of the time range. - start_time: YYYY-MM-DDTHH:mm:ssZ. The UTC timestamp representing the start of the time range. - granularity: granularity of metrics response. - requested_metrics: request metrics for historical request. - engagement_fields: A comma separated list of Engagement fields to display. + id: A single Post ID. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. Returns: - GetInsightsHistoricalResponse: Response data + GetByIdResponse: Response data """ - url = self.client.base_url + "/2/insights/historical" + url = self.client.base_url + "/2/tweets/{id}" + url = url.replace("{id}", str(id)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetByIdResponse.model_validate(response_data) + + + def delete(self, id: Any) -> DeleteResponse: + """ + Delete Post + Deletes a specific Post by its ID, if owned by the authenticated user. + Args: + id: The ID of the Post to be deleted. + Returns: + DeleteResponse: Response data + """ + url = self.client.base_url + "/2/tweets/{id}" + url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if tweet_ids is not None: - params["tweet_ids"] = ",".join(str(item) for item in tweet_ids) - if end_time is not None: - params["end_time"] = end_time - if start_time is not None: - params["start_time"] = start_time - if granularity is not None: - params["granularity"] = granularity - if requested_metrics is not None: - params["requested_metrics"] = ",".join( - str(item) for item in requested_metrics - ) - if engagement_fields is not None: - params["engagement.fields"] = ",".join( - str(item) for item in engagement_fields - ) headers = {} # Prepare request data json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.get( + response = self.client.oauth2_session.delete( url, params=params, headers=headers, ) else: - response = self.client.session.get( + response = self.client.session.delete( url, params=params, headers=headers, @@ -596,47 +742,45 @@ def get_insights_historical( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetInsightsHistoricalResponse.model_validate(response_data) + return DeleteResponse.model_validate(response_data) - def get_analytics( + def get_insights28hr( self, - ids: List, - end_time: str, - start_time: str, + tweet_ids: List, granularity: str, - analytics_fields: List = None, - ) -> GetAnalyticsResponse: + requested_metrics: List, + engagement_fields: List = None, + ) -> GetInsights28hrResponse: """ - Get Post analytics - Retrieves analytics data for specified Posts within a defined time range. + Get 28-hour Post insights + Retrieves engagement metrics for specified Posts over the last 28 hours. Args: - ids: A comma separated list of Post IDs. Up to 100 are allowed in a single request. - end_time: YYYY-MM-DDTHH:mm:ssZ. The UTC timestamp representing the end of the time range. - start_time: YYYY-MM-DDTHH:mm:ssZ. The UTC timestamp representing the start of the time range. - granularity: The granularity for the search counts results. - analytics_fields: A comma separated list of Analytics fields to display. + tweet_ids: List of PostIds for 28hr metrics. + granularity: granularity of metrics response. + requested_metrics: request metrics for historical request. + engagement_fields: A comma separated list of Engagement fields to display. Returns: - GetAnalyticsResponse: Response data + GetInsights28hrResponse: Response data """ - url = self.client.base_url + "/2/tweets/analytics" + url = self.client.base_url + "/2/insights/28hr" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if ids is not None: - params["ids"] = ",".join(str(item) for item in ids) - if end_time is not None: - params["end_time"] = end_time - if start_time is not None: - params["start_time"] = start_time + if tweet_ids is not None: + params["tweet_ids"] = ",".join(str(item) for item in tweet_ids) if granularity is not None: params["granularity"] = granularity - if analytics_fields is not None: - params["analytics.fields"] = ",".join( - str(item) for item in analytics_fields + if requested_metrics is not None: + params["requested_metrics"] = ",".join( + str(item) for item in requested_metrics + ) + if engagement_fields is not None: + params["engagement.fields"] = ",".join( + str(item) for item in engagement_fields ) headers = {} # Prepare request data @@ -659,33 +803,34 @@ def get_analytics( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetAnalyticsResponse.model_validate(response_data) + return GetInsights28hrResponse.model_validate(response_data) - def get_reposted_by( + def get_by_ids( self, - id: Any, - max_results: int = None, - pagination_token: Any = None, - user_fields: List = None, - expansions: List = None, + ids: List, tweet_fields: List = None, - ) -> GetRepostedByResponse: + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + ) -> GetByIdsResponse: """ - Get Reposted by - Retrieves a list of Users who reposted a specific Post by its ID. + Get Posts by IDs + Retrieves details of multiple Posts by their IDs. Args: - id: A single Post ID. - max_results: The maximum number of results. - pagination_token: This parameter is used to get the next 'page' of results. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. + ids: A comma separated list of Post IDs. Up to 100 are allowed in a single request. tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. Returns: - GetRepostedByResponse: Response data + GetByIdsResponse: Response data """ - url = self.client.base_url + "/2/tweets/{id}/retweeted_by" - url = url.replace("{id}", str(id)) + url = self.client.base_url + "/2/tweets" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -700,16 +845,20 @@ def get_reposted_by( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) + if ids is not None: + params["ids"] = ",".join(str(item) for item in ids) if tweet_fields is not None: params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} # Prepare request data json_data = None @@ -724,99 +873,80 @@ def get_reposted_by( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetRepostedByResponse.model_validate(response_data) + return GetByIdsResponse.model_validate(response_data) - def get_insights28hr( - self, - tweet_ids: List, - granularity: str, - requested_metrics: List, - engagement_fields: List = None, - ) -> GetInsights28hrResponse: + def create(self, body: CreateRequest) -> Dict[str, Any]: """ - Get 28-hour Post insights - Retrieves engagement metrics for specified Posts over the last 28 hours. - Args: - tweet_ids: List of PostIds for 28hr metrics. - granularity: granularity of metrics response. - requested_metrics: request metrics for historical request. - engagement_fields: A comma separated list of Engagement fields to display. - Returns: - GetInsights28hrResponse: Response data + Create or Edit Post + Creates a new Post for the authenticated user, or edits an existing Post when edit_options are provided. + body: Request body + Returns: + CreateResponse: Response data """ - url = self.client.base_url + "/2/insights/28hr" + url = self.client.base_url + "/2/tweets" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() - params = {} - if tweet_ids is not None: - params["tweet_ids"] = ",".join(str(item) for item in tweet_ids) - if granularity is not None: - params["granularity"] = granularity - if requested_metrics is not None: - params["requested_metrics"] = ",".join( - str(item) for item in requested_metrics - ) - if engagement_fields is not None: - params["engagement.fields"] = ",".join( - str(item) for item in engagement_fields - ) + params = {} headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.get( + response = self.client.oauth2_session.post( url, params=params, headers=headers, + json=json_data, ) else: - response = self.client.session.get( + response = self.client.session.post( url, params=params, headers=headers, + json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetInsights28hrResponse.model_validate(response_data) + return CreateResponse.model_validate(response_data) - def get_reposts( + def get_reposted_by( self, id: Any, max_results: int = None, pagination_token: Any = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, user_fields: List = None, - place_fields: List = None, - ) -> GetRepostsResponse: + expansions: List = None, + tweet_fields: List = None, + ) -> GetRepostedByResponse: """ - Get Reposts - Retrieves a list of Posts that repost a specific Post by its ID. + Get Reposted by + Retrieves a list of Users who reposted a specific Post by its ID. Args: id: A single Post ID. max_results: The maximum number of results. pagination_token: This parameter is used to get the next 'page' of results. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetRepostsResponse: Response data + GetRepostedByResponse: Response data """ - url = self.client.base_url + "/2/tweets/{id}/retweets" + url = self.client.base_url + "/2/tweets/{id}/retweeted_by" url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( @@ -836,18 +966,12 @@ def get_reposts( params["max_results"] = max_results if pagination_token is not None: params["pagination_token"] = pagination_token - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None @@ -862,7 +986,7 @@ def get_reposts( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetRepostsResponse.model_validate(response_data) + return GetRepostedByResponse.model_validate(response_data) def search_recent( @@ -967,194 +1091,70 @@ def search_recent( return SearchRecentResponse.model_validate(response_data) - def get_by_ids( + def get_insights_historical( self, - ids: List, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, - user_fields: List = None, - place_fields: List = None, - ) -> GetByIdsResponse: + tweet_ids: List, + end_time: str, + start_time: str, + granularity: str, + requested_metrics: List, + engagement_fields: List = None, + ) -> GetInsightsHistoricalResponse: """ - Get Posts by IDs - Retrieves details of multiple Posts by their IDs. + Get historical Post insights + Retrieves historical engagement metrics for specified Posts within a defined time range. Args: - ids: A comma separated list of Post IDs. Up to 100 are allowed in a single request. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. - user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + tweet_ids: List of PostIds for historical metrics. + end_time: YYYY-MM-DDTHH:mm:ssZ. The UTC timestamp representing the end of the time range. + start_time: YYYY-MM-DDTHH:mm:ssZ. The UTC timestamp representing the start of the time range. + granularity: granularity of metrics response. + requested_metrics: request metrics for historical request. + engagement_fields: A comma separated list of Engagement fields to display. Returns: - GetByIdsResponse: Response data - """ - url = self.client.base_url + "/2/tweets" - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - if ids is not None: - params["ids"] = ",".join(str(item) for item in ids) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return GetByIdsResponse.model_validate(response_data) - - - def create(self, body: CreateRequest) -> Dict[str, Any]: - """ - Create or Edit Post - Creates a new Post for the authenticated user, or edits an existing Post when edit_options are provided. - body: Request body - Returns: - CreateResponse: Response data + GetInsightsHistoricalResponse: Response data """ - url = self.client.base_url + "/2/tweets" + url = self.client.base_url + "/2/insights/historical" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if tweet_ids is not None: + params["tweet_ids"] = ",".join(str(item) for item in tweet_ids) + if end_time is not None: + params["end_time"] = end_time + if start_time is not None: + params["start_time"] = start_time + if granularity is not None: + params["granularity"] = granularity + if requested_metrics is not None: + params["requested_metrics"] = ",".join( + str(item) for item in requested_metrics + ) + if engagement_fields is not None: + params["engagement.fields"] = ",".join( + str(item) for item in engagement_fields + ) headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.post( + response = self.client.oauth2_session.get( url, params=params, headers=headers, - json=json_data, ) else: - response = self.client.session.post( + response = self.client.session.get( url, params=params, headers=headers, - json=json_data, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return CreateResponse.model_validate(response_data) - - - def get_counts_all( - self, - query: str, - start_time: str = None, - end_time: str = None, - since_id: Any = None, - until_id: Any = None, - next_token: Any = None, - pagination_token: Any = None, - granularity: str = None, - search_count_fields: List = None, - ) -> GetCountsAllResponse: - """ - Get count of all Posts - Retrieves the count of Posts matching a search query from the full archive. - Args: - query: One query/rule/filter for matching Posts. Refer to https://t.co/rulelength to identify the max query length. - start_time: YYYY-MM-DDTHH:mm:ssZ. The oldest UTC timestamp (from most recent 7 days) from which the Posts will be provided. Timestamp is in second granularity and is inclusive (i.e. 12:00:01 includes the first second of the minute). - end_time: YYYY-MM-DDTHH:mm:ssZ. The newest, most recent UTC timestamp to which the Posts will be provided. Timestamp is in second granularity and is exclusive (i.e. 12:00:01 excludes the first second of the minute). - since_id: Returns results with a Post ID greater than (that is, more recent than) the specified ID. - until_id: Returns results with a Post ID less than (that is, older than) the specified ID. - next_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. - pagination_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. - granularity: The granularity for the search counts results. - search_count_fields: A comma separated list of SearchCount fields to display. - Returns: - GetCountsAllResponse: Response data - """ - url = self.client.base_url + "/2/tweets/counts/all" - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" ) - params = {} - if query is not None: - params["query"] = query - if start_time is not None: - params["start_time"] = start_time - if end_time is not None: - params["end_time"] = end_time - if since_id is not None: - params["since_id"] = since_id - if until_id is not None: - params["until_id"] = until_id - if next_token is not None: - params["next_token"] = next_token - if pagination_token is not None: - params["pagination_token"] = pagination_token - if granularity is not None: - params["granularity"] = granularity - if search_count_fields is not None: - params["search_count.fields"] = ",".join( - str(item) for item in search_count_fields - ) - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetCountsAllResponse.model_validate(response_data) + return GetInsightsHistoricalResponse.model_validate(response_data) diff --git a/xdk/posts/models.py b/xdk/posts/models.py index a47e405..2ecf044 100644 --- a/xdk/posts/models.py +++ b/xdk/posts/models.py @@ -16,29 +16,20 @@ from datetime import datetime -# Models for search_all - - -class SearchAllResponse(BaseModel): - """Response model for search_all""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - -# Models for get_by_id +# Models for get_liking_users -class GetByIdResponse(BaseModel): - """Response model for get_by_id""" +class GetLikingUsersResponse(BaseModel): + """Response model for get_liking_users""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for delete +# Models for get_quoted -class DeleteResponse(BaseModel): - """Response model for delete""" +class GetQuotedResponse(BaseModel): + """Response model for get_quoted""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -67,74 +58,65 @@ class HideReplyResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_liking_users - - -class GetLikingUsersResponse(BaseModel): - """Response model for get_liking_users""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") - - -# Models for get_quoted +# Models for get_counts_all -class GetQuotedResponse(BaseModel): - """Response model for get_quoted""" +class GetCountsAllResponse(BaseModel): + """Response model for get_counts_all""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_insights_historical +# Models for get_analytics -class GetInsightsHistoricalResponse(BaseModel): - """Response model for get_insights_historical""" +class GetAnalyticsResponse(BaseModel): + """Response model for get_analytics""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_analytics +# Models for search_all -class GetAnalyticsResponse(BaseModel): - """Response model for get_analytics""" +class SearchAllResponse(BaseModel): + """Response model for search_all""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_reposted_by +# Models for get_reposts -class GetRepostedByResponse(BaseModel): - """Response model for get_reposted_by""" +class GetRepostsResponse(BaseModel): + """Response model for get_reposts""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_insights28hr +# Models for get_by_id -class GetInsights28hrResponse(BaseModel): - """Response model for get_insights28hr""" +class GetByIdResponse(BaseModel): + """Response model for get_by_id""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_reposts +# Models for delete -class GetRepostsResponse(BaseModel): - """Response model for get_reposts""" +class DeleteResponse(BaseModel): + """Response model for delete""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for search_recent +# Models for get_insights28hr -class SearchRecentResponse(BaseModel): - """Response model for search_recent""" +class GetInsights28hrResponse(BaseModel): + """Response model for get_insights28hr""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -163,10 +145,28 @@ class CreateResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_counts_all +# Models for get_reposted_by -class GetCountsAllResponse(BaseModel): - """Response model for get_counts_all""" +class GetRepostedByResponse(BaseModel): + """Response model for get_reposted_by""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + +# Models for search_recent + + +class SearchRecentResponse(BaseModel): + """Response model for search_recent""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + +# Models for get_insights_historical + + +class GetInsightsHistoricalResponse(BaseModel): + """Response model for get_insights_historical""" model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/spaces/client.py b/xdk/spaces/client.py index 397fa3e..82a84a6 100644 --- a/xdk/spaces/client.py +++ b/xdk/spaces/client.py @@ -21,12 +21,12 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - GetBuyersResponse, + GetByCreatorIdsResponse, GetByIdsResponse, SearchResponse, GetPostsResponse, - GetByCreatorIdsResponse, GetByIdResponse, + GetBuyersResponse, ) @@ -38,68 +38,66 @@ def __init__(self, client: Client): self.client = client - def get_buyers( + def get_by_creator_ids( self, - id: str, - pagination_token: Any = None, - max_results: int = None, - user_fields: List = None, + user_ids: List, + space_fields: List = None, expansions: List = None, - tweet_fields: List = None, - ) -> GetBuyersResponse: + user_fields: List = None, + topic_fields: List = None, + ) -> GetByCreatorIdsResponse: """ - Get Space ticket buyers - Retrieves a list of Users who purchased tickets to a specific Space by its ID. + Get Spaces by creator IDs + Retrieves details of Spaces created by specified User IDs. Args: - id: The ID of the Space to be retrieved. - pagination_token: This parameter is used to get a specified 'page' of results. - max_results: The maximum number of results. - user_fields: A comma separated list of User fields to display. + user_ids: The IDs of Users to search through. + space_fields: A comma separated list of Space fields to display. expansions: A comma separated list of fields to expand. - tweet_fields: A comma separated list of Tweet fields to display. + user_fields: A comma separated list of User fields to display. + topic_fields: A comma separated list of Topic fields to display. Returns: - GetBuyersResponse: Response data + GetByCreatorIdsResponse: Response data """ - url = self.client.base_url + "/2/spaces/{id}/buyers" - url = url.replace("{id}", str(id)) + url = self.client.base_url + "/2/spaces/by/creator_ids" + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if pagination_token is not None: - params["pagination_token"] = pagination_token - if max_results is not None: - params["max_results"] = max_results - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) + if user_ids is not None: + params["user_ids"] = ",".join(str(item) for item in user_ids) + if space_fields is not None: + params["space.fields"] = ",".join(str(item) for item in space_fields) if expansions is not None: params["expansions"] = ",".join(str(item) for item in expansions) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if topic_fields is not None: + params["topic.fields"] = ",".join(str(item) for item in topic_fields) headers = {} # Prepare request data json_data = None # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.get( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.get( - url, - params=params, - headers=headers, - ) + response = self.client.session.get( + url, + params=params, + headers=headers, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetBuyersResponse.model_validate(response_data) + return GetByCreatorIdsResponse.model_validate(response_data) def get_by_ids( @@ -307,27 +305,28 @@ def get_posts( return GetPostsResponse.model_validate(response_data) - def get_by_creator_ids( + def get_by_id( self, - user_ids: List, + id: str, space_fields: List = None, expansions: List = None, user_fields: List = None, topic_fields: List = None, - ) -> GetByCreatorIdsResponse: + ) -> GetByIdResponse: """ - Get Spaces by creator IDs - Retrieves details of Spaces created by specified User IDs. + Get space by ID + Retrieves details of a specific space by its ID. Args: - user_ids: The IDs of Users to search through. + id: The ID of the Space to be retrieved. space_fields: A comma separated list of Space fields to display. expansions: A comma separated list of fields to expand. user_fields: A comma separated list of User fields to display. topic_fields: A comma separated list of Topic fields to display. Returns: - GetByCreatorIdsResponse: Response data + GetByIdResponse: Response data """ - url = self.client.base_url + "/2/spaces/by/creator_ids" + url = self.client.base_url + "/2/spaces/{id}" + url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -342,8 +341,6 @@ def get_by_creator_ids( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if user_ids is not None: - params["user_ids"] = ",".join(str(item) for item in user_ids) if space_fields is not None: params["space.fields"] = ",".join(str(item) for item in space_fields) if expansions is not None: @@ -366,65 +363,68 @@ def get_by_creator_ids( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetByCreatorIdsResponse.model_validate(response_data) + return GetByIdResponse.model_validate(response_data) - def get_by_id( + def get_buyers( self, id: str, - space_fields: List = None, - expansions: List = None, + pagination_token: Any = None, + max_results: int = None, user_fields: List = None, - topic_fields: List = None, - ) -> GetByIdResponse: + expansions: List = None, + tweet_fields: List = None, + ) -> GetBuyersResponse: """ - Get space by ID - Retrieves details of a specific space by its ID. + Get Space ticket buyers + Retrieves a list of Users who purchased tickets to a specific Space by its ID. Args: id: The ID of the Space to be retrieved. - space_fields: A comma separated list of Space fields to display. - expansions: A comma separated list of fields to expand. + pagination_token: This parameter is used to get a specified 'page' of results. + max_results: The maximum number of results. user_fields: A comma separated list of User fields to display. - topic_fields: A comma separated list of Topic fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetByIdResponse: Response data + GetBuyersResponse: Response data """ - url = self.client.base_url + "/2/spaces/{id}" + url = self.client.base_url + "/2/spaces/{id}/buyers" url = url.replace("{id}", str(id)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if space_fields is not None: - params["space.fields"] = ",".join(str(item) for item in space_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) + if pagination_token is not None: + params["pagination_token"] = pagination_token + if max_results is not None: + params["max_results"] = max_results if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if topic_fields is not None: - params["topic.fields"] = ",".join(str(item) for item in topic_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) + if self.client.oauth2_session: + response = self.client.oauth2_session.get( + url, + params=params, + headers=headers, + ) + else: + response = self.client.session.get( + url, + params=params, + headers=headers, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetByIdResponse.model_validate(response_data) + return GetBuyersResponse.model_validate(response_data) diff --git a/xdk/spaces/models.py b/xdk/spaces/models.py index c277e44..26b172d 100644 --- a/xdk/spaces/models.py +++ b/xdk/spaces/models.py @@ -16,11 +16,11 @@ from datetime import datetime -# Models for get_buyers +# Models for get_by_creator_ids -class GetBuyersResponse(BaseModel): - """Response model for get_buyers""" +class GetByCreatorIdsResponse(BaseModel): + """Response model for get_by_creator_ids""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -52,19 +52,19 @@ class GetPostsResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_by_creator_ids +# Models for get_by_id -class GetByCreatorIdsResponse(BaseModel): - """Response model for get_by_creator_ids""" +class GetByIdResponse(BaseModel): + """Response model for get_by_id""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_by_id +# Models for get_buyers -class GetByIdResponse(BaseModel): - """Response model for get_by_id""" +class GetBuyersResponse(BaseModel): + """Response model for get_buyers""" model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/stream/client.py b/xdk/stream/client.py index d8a1e43..c1aa1af 100644 --- a/xdk/stream/client.py +++ b/xdk/stream/client.py @@ -31,24 +31,24 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - LabelsComplianceResponse, - PostsFirehoseJaResponse, LikesComplianceResponse, - PostsFirehoseResponse, GetRulesResponse, UpdateRulesRequest, UpdateRulesResponse, - PostsSampleResponse, PostsFirehoseEnResponse, - PostsSample10Response, - GetRuleCountsResponse, - PostsFirehosePtResponse, - PostsFirehoseKoResponse, - LikesFirehoseResponse, - LikesSample10Response, - UsersComplianceResponse, PostsResponse, + PostsFirehoseResponse, + PostsFirehoseJaResponse, + LikesSample10Response, PostsComplianceResponse, + UsersComplianceResponse, + LikesFirehoseResponse, + PostsFirehoseKoResponse, + LabelsComplianceResponse, + PostsSample10Response, + PostsFirehosePtResponse, + GetRuleCountsResponse, + PostsSampleResponse, ) @@ -60,32 +60,32 @@ def __init__(self, client: Client): self.client = client - def labels_compliance( + def likes_compliance( self, backfill_minutes: int = None, start_time: str = None, end_time: str = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[LabelsComplianceResponse, None, None]: + ) -> Generator[LikesComplianceResponse, None, None]: """ - Stream Post labels (Streaming) - Streams all labeling events applied to Posts. + Stream Likes compliance data (Streaming) + Streams all compliance data related to Likes for Users. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Post labels will be provided. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the Post labels will be provided. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Likes Compliance events will be provided. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the Likes Compliance events will be provided. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - LabelsComplianceResponse: Individual streaming data items + LikesComplianceResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/label/stream" + url = self.client.base_url + "/2/likes/compliance/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -137,7 +137,7 @@ def labels_compliance( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield LabelsComplianceResponse.model_validate(data) + yield LikesComplianceResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -148,7 +148,7 @@ def labels_compliance( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield LabelsComplianceResponse.model_validate(data) + yield LikesComplianceResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -158,7 +158,105 @@ def labels_compliance( raise - def posts_firehose_ja( + def get_rules( + self, ids: List = None, max_results: int = None, pagination_token: str = None + ) -> GetRulesResponse: + """ + Get stream rules + Retrieves the active rule set or a subset of rules for the filtered stream. + Args: + ids: A comma-separated list of Rule IDs. + max_results: The maximum number of results. + pagination_token: This value is populated by passing the 'next_token' returned in a request to paginate through results. + Returns: + GetRulesResponse: Response data + """ + url = self.client.base_url + "/2/tweets/search/stream/rules" + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + params = {} + if ids is not None: + params["ids"] = ",".join(str(item) for item in ids) + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetRulesResponse.model_validate(response_data) + + + def update_rules( + self, body: UpdateRulesRequest, dry_run: bool = None, delete_all: bool = None + ) -> UpdateRulesResponse: + """ + Update stream rules + Adds or deletes rules from the active rule set for the filtered stream. + Args: + dry_run: Dry Run can be used with both the add and delete action, with the expected result given, but without actually taking any action in the system (meaning the end state will always be as it was when the request was submitted). This is particularly useful to validate rule changes. + delete_all: Delete All can be used to delete all of the rules associated this client app, it should be specified with no other parameters. Once deleted, rules cannot be recovered. + body: Request body + Returns: + UpdateRulesResponse: Response data + """ + url = self.client.base_url + "/2/tweets/search/stream/rules" + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + params = {} + if dry_run is not None: + params["dry_run"] = dry_run + if delete_all is not None: + params["delete_all"] = delete_all + headers = {} + headers["Content-Type"] = "application/json" + # Prepare request data + json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) + # Make the request + response = self.client.session.post( + url, + params=params, + headers=headers, + json=json_data, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return UpdateRulesResponse.model_validate(response_data) + + + def posts_firehose_en( self, partition: int, backfill_minutes: int = None, @@ -172,10 +270,10 @@ def posts_firehose_ja( place_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[PostsFirehoseJaResponse, None, None]: + ) -> Generator[PostsFirehoseEnResponse, None, None]: """ - Stream Japanese Posts (Streaming) - Streams all public Japanese-language Posts in real-time. + Stream English Posts (Streaming) + Streams all public English-language Posts in real-time. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: @@ -192,12 +290,12 @@ def posts_firehose_ja( timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - PostsFirehoseJaResponse: Individual streaming data items + PostsFirehoseEnResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/firehose/stream/lang/ja" + url = self.client.base_url + "/2/tweets/firehose/stream/lang/en" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -263,7 +361,7 @@ def posts_firehose_ja( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield PostsFirehoseJaResponse.model_validate(data) + yield PostsFirehoseEnResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -274,7 +372,7 @@ def posts_firehose_ja( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield PostsFirehoseJaResponse.model_validate(data) + yield PostsFirehoseEnResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -284,32 +382,44 @@ def posts_firehose_ja( raise - def likes_compliance( + def posts( self, backfill_minutes: int = None, start_time: str = None, end_time: str = None, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[LikesComplianceResponse, None, None]: + ) -> Generator[PostsResponse, None, None]: """ - Stream Likes compliance data (Streaming) - Streams all compliance data related to Likes for Users. + Stream filtered Posts (Streaming) + Streams Posts in real-time matching the active rule set. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Likes Compliance events will be provided. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the Likes Compliance events will be provided. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - LikesComplianceResponse: Individual streaming data items + PostsResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/likes/compliance/stream" + url = self.client.base_url + "/2/tweets/search/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -325,6 +435,18 @@ def likes_compliance( params["start_time"] = start_time if end_time is not None: params["end_time"] = end_time + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = { "Accept": "application/json", } @@ -361,7 +483,7 @@ def likes_compliance( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield LikesComplianceResponse.model_validate(data) + yield PostsResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -372,7 +494,7 @@ def likes_compliance( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield LikesComplianceResponse.model_validate(data) + yield PostsResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -508,138 +630,46 @@ def posts_firehose( raise - def get_rules( - self, ids: List = None, max_results: int = None, pagination_token: str = None - ) -> GetRulesResponse: + def posts_firehose_ja( + self, + partition: int, + backfill_minutes: int = None, + start_time: str = None, + end_time: str = None, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + timeout: Optional[float] = None, + chunk_size: int = 1024, + ) -> Generator[PostsFirehoseJaResponse, None, None]: """ - Get stream rules - Retrieves the active rule set or a subset of rules for the filtered stream. + Stream Japanese Posts (Streaming) + Streams all public Japanese-language Posts in real-time. + This is a streaming endpoint that yields data in real-time as it becomes available. + Each yielded item represents a single data point from the stream. Args: - ids: A comma-separated list of Rule IDs. - max_results: The maximum number of results. - pagination_token: This value is populated by passing the 'next_token' returned in a request to paginate through results. - Returns: - GetRulesResponse: Response data + backfill_minutes: The number of minutes of backfill requested. + partition: The partition number. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. + timeout: Request timeout in seconds (default: None for no timeout) + chunk_size: Size of chunks to read from the stream (default: 1024 bytes) + Yields: + PostsFirehoseJaResponse: Individual streaming data items + Raises: + requests.exceptions.RequestException: If the streaming connection fails + json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/search/stream/rules" - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - params = {} - if ids is not None: - params["ids"] = ",".join(str(item) for item in ids) - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return GetRulesResponse.model_validate(response_data) - - - def update_rules( - self, body: UpdateRulesRequest, dry_run: bool = None, delete_all: bool = None - ) -> UpdateRulesResponse: - """ - Update stream rules - Adds or deletes rules from the active rule set for the filtered stream. - Args: - dry_run: Dry Run can be used with both the add and delete action, with the expected result given, but without actually taking any action in the system (meaning the end state will always be as it was when the request was submitted). This is particularly useful to validate rule changes. - delete_all: Delete All can be used to delete all of the rules associated this client app, it should be specified with no other parameters. Once deleted, rules cannot be recovered. - body: Request body - Returns: - UpdateRulesResponse: Response data - """ - url = self.client.base_url + "/2/tweets/search/stream/rules" - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - params = {} - if dry_run is not None: - params["dry_run"] = dry_run - if delete_all is not None: - params["delete_all"] = delete_all - headers = {} - headers["Content-Type"] = "application/json" - # Prepare request data - json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) - # Make the request - response = self.client.session.post( - url, - params=params, - headers=headers, - json=json_data, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return UpdateRulesResponse.model_validate(response_data) - - - def posts_sample( - self, - backfill_minutes: int = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, - user_fields: List = None, - place_fields: List = None, - timeout: Optional[float] = None, - chunk_size: int = 1024, - ) -> Generator[PostsSampleResponse, None, None]: - """ - Stream sampled Posts (Streaming) - Streams a 1% sample of public Posts in real-time. - This is a streaming endpoint that yields data in real-time as it becomes available. - Each yielded item represents a single data point from the stream. - Args: - backfill_minutes: The number of minutes of backfill requested. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. - user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. - timeout: Request timeout in seconds (default: None for no timeout) - chunk_size: Size of chunks to read from the stream (default: 1024 bytes) - Yields: - PostsSampleResponse: Individual streaming data items - Raises: - requests.exceptions.RequestException: If the streaming connection fails - json.JSONDecodeError: If the streamed data is not valid JSON - """ - url = self.client.base_url + "/2/tweets/sample/stream" + url = self.client.base_url + "/2/tweets/firehose/stream/lang/ja" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -651,6 +681,12 @@ def posts_sample( params = {} if backfill_minutes is not None: params["backfill_minutes"] = backfill_minutes + if partition is not None: + params["partition"] = partition + if start_time is not None: + params["start_time"] = start_time + if end_time is not None: + params["end_time"] = end_time if tweet_fields is not None: params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) if expansions is not None: @@ -699,7 +735,7 @@ def posts_sample( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield PostsSampleResponse.model_validate(data) + yield PostsFirehoseJaResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -710,7 +746,7 @@ def posts_sample( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield PostsSampleResponse.model_validate(data) + yield PostsFirehoseJaResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -720,46 +756,42 @@ def posts_sample( raise - def posts_firehose_en( + def likes_sample10( self, partition: int, backfill_minutes: int = None, start_time: str = None, end_time: str = None, - tweet_fields: List = None, + like_with_tweet_author_fields: List = None, expansions: List = None, - media_fields: List = None, - poll_fields: List = None, user_fields: List = None, - place_fields: List = None, + tweet_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[PostsFirehoseEnResponse, None, None]: + ) -> Generator[LikesSample10Response, None, None]: """ - Stream English Posts (Streaming) - Streams all public English-language Posts in real-time. + Stream sampled Likes (Streaming) + Streams a 10% sample of public Likes in real-time. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. partition: The partition number. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Likes will be provided. end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. - tweet_fields: A comma separated list of Tweet fields to display. + like_with_tweet_author_fields: A comma separated list of LikeWithTweetAuthor fields to display. expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + tweet_fields: A comma separated list of Tweet fields to display. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - PostsFirehoseEnResponse: Individual streaming data items + LikesSample10Response: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/firehose/stream/lang/en" + url = self.client.base_url + "/2/likes/sample10/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -777,18 +809,16 @@ def posts_firehose_en( params["start_time"] = start_time if end_time is not None: params["end_time"] = end_time - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if like_with_tweet_author_fields is not None: + params["like_with_tweet_author.fields"] = ",".join( + str(item) for item in like_with_tweet_author_fields + ) if expansions is not None: params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = { "Accept": "application/json", } @@ -825,7 +855,7 @@ def posts_firehose_en( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield PostsFirehoseEnResponse.model_validate(data) + yield LikesSample10Response.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -836,7 +866,7 @@ def posts_firehose_en( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield PostsFirehoseEnResponse.model_validate(data) + yield LikesSample10Response.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -846,46 +876,34 @@ def posts_firehose_en( raise - def posts_sample10( + def posts_compliance( self, partition: int, backfill_minutes: int = None, start_time: str = None, end_time: str = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, - user_fields: List = None, - place_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[PostsSample10Response, None, None]: + ) -> Generator[PostsComplianceResponse, None, None]: """ - Stream 10% sampled Posts (Streaming) - Streams a 10% sample of public Posts in real-time. + Stream Posts compliance data (Streaming) + Streams all compliance data related to Posts. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. partition: The partition number. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. - user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Post Compliance events will be provided. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Post Compliance events will be provided. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - PostsSample10Response: Individual streaming data items + PostsComplianceResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/sample10/stream" + url = self.client.base_url + "/2/tweets/compliance/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -903,18 +921,6 @@ def posts_sample10( params["start_time"] = start_time if end_time is not None: params["end_time"] = end_time - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) headers = { "Accept": "application/json", } @@ -951,7 +957,7 @@ def posts_sample10( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield PostsSample10Response.model_validate(data) + yield PostsComplianceResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -962,7 +968,7 @@ def posts_sample10( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield PostsSample10Response.model_validate(data) + yield PostsComplianceResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -972,86 +978,34 @@ def posts_sample10( raise - def get_rule_counts(self, rules_count_fields: List = None) -> GetRuleCountsResponse: - """ - Get stream rule counts - Retrieves the count of rules in the active rule set for the filtered stream. - Args: - rules_count_fields: A comma separated list of RulesCount fields to display. - Returns: - GetRuleCountsResponse: Response data - """ - url = self.client.base_url + "/2/tweets/search/stream/rules/counts" - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - params = {} - if rules_count_fields is not None: - params["rules_count.fields"] = ",".join( - str(item) for item in rules_count_fields - ) - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return GetRuleCountsResponse.model_validate(response_data) - - - def posts_firehose_pt( + def users_compliance( self, partition: int, backfill_minutes: int = None, start_time: str = None, end_time: str = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, - user_fields: List = None, - place_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[PostsFirehosePtResponse, None, None]: + ) -> Generator[UsersComplianceResponse, None, None]: """ - Stream Portuguese Posts (Streaming) - Streams all public Portuguese-language Posts in real-time. + Stream Users compliance data (Streaming) + Streams all compliance data related to Users. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. partition: The partition number. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. - user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the User Compliance events will be provided. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the User Compliance events will be provided. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - PostsFirehosePtResponse: Individual streaming data items + UsersComplianceResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/firehose/stream/lang/pt" + url = self.client.base_url + "/2/users/compliance/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1069,18 +1023,6 @@ def posts_firehose_pt( params["start_time"] = start_time if end_time is not None: params["end_time"] = end_time - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) headers = { "Accept": "application/json", } @@ -1117,7 +1059,7 @@ def posts_firehose_pt( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield PostsFirehosePtResponse.model_validate(data) + yield UsersComplianceResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -1128,7 +1070,7 @@ def posts_firehose_pt( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield PostsFirehosePtResponse.model_validate(data) + yield UsersComplianceResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -1138,46 +1080,42 @@ def posts_firehose_pt( raise - def posts_firehose_ko( + def likes_firehose( self, partition: int, backfill_minutes: int = None, start_time: str = None, end_time: str = None, - tweet_fields: List = None, + like_with_tweet_author_fields: List = None, expansions: List = None, - media_fields: List = None, - poll_fields: List = None, user_fields: List = None, - place_fields: List = None, + tweet_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[PostsFirehoseKoResponse, None, None]: + ) -> Generator[LikesFirehoseResponse, None, None]: """ - Stream Korean Posts (Streaming) - Streams all public Korean-language Posts in real-time. + Stream all Likes (Streaming) + Streams all public Likes in real-time. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. partition: The partition number. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Likes will be provided. end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. - tweet_fields: A comma separated list of Tweet fields to display. + like_with_tweet_author_fields: A comma separated list of LikeWithTweetAuthor fields to display. expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + tweet_fields: A comma separated list of Tweet fields to display. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - PostsFirehoseKoResponse: Individual streaming data items + LikesFirehoseResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/firehose/stream/lang/ko" + url = self.client.base_url + "/2/likes/firehose/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1195,18 +1133,16 @@ def posts_firehose_ko( params["start_time"] = start_time if end_time is not None: params["end_time"] = end_time - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if like_with_tweet_author_fields is not None: + params["like_with_tweet_author.fields"] = ",".join( + str(item) for item in like_with_tweet_author_fields + ) if expansions is not None: params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = { "Accept": "application/json", } @@ -1243,7 +1179,7 @@ def posts_firehose_ko( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield PostsFirehoseKoResponse.model_validate(data) + yield LikesFirehoseResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -1254,7 +1190,7 @@ def posts_firehose_ko( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield PostsFirehoseKoResponse.model_validate(data) + yield LikesFirehoseResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -1264,42 +1200,46 @@ def posts_firehose_ko( raise - def likes_firehose( + def posts_firehose_ko( self, partition: int, backfill_minutes: int = None, start_time: str = None, end_time: str = None, - like_with_tweet_author_fields: List = None, + tweet_fields: List = None, expansions: List = None, + media_fields: List = None, + poll_fields: List = None, user_fields: List = None, - tweet_fields: List = None, + place_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[LikesFirehoseResponse, None, None]: + ) -> Generator[PostsFirehoseKoResponse, None, None]: """ - Stream all Likes (Streaming) - Streams all public Likes in real-time. + Stream Korean Posts (Streaming) + Streams all public Korean-language Posts in real-time. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. partition: The partition number. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Likes will be provided. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided. end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. - like_with_tweet_author_fields: A comma separated list of LikeWithTweetAuthor fields to display. + tweet_fields: A comma separated list of Tweet fields to display. expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. user_fields: A comma separated list of User fields to display. - tweet_fields: A comma separated list of Tweet fields to display. + place_fields: A comma separated list of Place fields to display. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - LikesFirehoseResponse: Individual streaming data items + PostsFirehoseKoResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/likes/firehose/stream" + url = self.client.base_url + "/2/tweets/firehose/stream/lang/ko" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1317,16 +1257,18 @@ def likes_firehose( params["start_time"] = start_time if end_time is not None: params["end_time"] = end_time - if like_with_tweet_author_fields is not None: - params["like_with_tweet_author.fields"] = ",".join( - str(item) for item in like_with_tweet_author_fields - ) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) if expansions is not None: params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = { "Accept": "application/json", } @@ -1363,7 +1305,7 @@ def likes_firehose( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield LikesFirehoseResponse.model_validate(data) + yield PostsFirehoseKoResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -1374,7 +1316,7 @@ def likes_firehose( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield LikesFirehoseResponse.model_validate(data) + yield PostsFirehoseKoResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -1384,42 +1326,32 @@ def likes_firehose( raise - def likes_sample10( + def labels_compliance( self, - partition: int, backfill_minutes: int = None, start_time: str = None, end_time: str = None, - like_with_tweet_author_fields: List = None, - expansions: List = None, - user_fields: List = None, - tweet_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[LikesSample10Response, None, None]: + ) -> Generator[LabelsComplianceResponse, None, None]: """ - Stream sampled Likes (Streaming) - Streams a 10% sample of public Likes in real-time. + Stream Post labels (Streaming) + Streams all labeling events applied to Posts. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. - partition: The partition number. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Likes will be provided. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. - like_with_tweet_author_fields: A comma separated list of LikeWithTweetAuthor fields to display. - expansions: A comma separated list of fields to expand. - user_fields: A comma separated list of User fields to display. - tweet_fields: A comma separated list of Tweet fields to display. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Post labels will be provided. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the Post labels will be provided. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - LikesSample10Response: Individual streaming data items + LabelsComplianceResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/likes/sample10/stream" + url = self.client.base_url + "/2/tweets/label/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1431,22 +1363,10 @@ def likes_sample10( params = {} if backfill_minutes is not None: params["backfill_minutes"] = backfill_minutes - if partition is not None: - params["partition"] = partition if start_time is not None: params["start_time"] = start_time if end_time is not None: params["end_time"] = end_time - if like_with_tweet_author_fields is not None: - params["like_with_tweet_author.fields"] = ",".join( - str(item) for item in like_with_tweet_author_fields - ) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = { "Accept": "application/json", } @@ -1483,7 +1403,7 @@ def likes_sample10( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield LikesSample10Response.model_validate(data) + yield LabelsComplianceResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -1494,7 +1414,7 @@ def likes_sample10( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield LikesSample10Response.model_validate(data) + yield LabelsComplianceResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -1504,34 +1424,46 @@ def likes_sample10( raise - def users_compliance( + def posts_sample10( self, partition: int, backfill_minutes: int = None, start_time: str = None, end_time: str = None, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[UsersComplianceResponse, None, None]: + ) -> Generator[PostsSample10Response, None, None]: """ - Stream Users compliance data (Streaming) - Streams all compliance data related to Users. + Stream 10% sampled Posts (Streaming) + Streams a 10% sample of public Posts in real-time. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. partition: The partition number. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the User Compliance events will be provided. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp from which the User Compliance events will be provided. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - UsersComplianceResponse: Individual streaming data items + PostsSample10Response: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/users/compliance/stream" + url = self.client.base_url + "/2/tweets/sample10/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1549,6 +1481,18 @@ def users_compliance( params["start_time"] = start_time if end_time is not None: params["end_time"] = end_time + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = { "Accept": "application/json", } @@ -1585,7 +1529,7 @@ def users_compliance( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield UsersComplianceResponse.model_validate(data) + yield PostsSample10Response.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -1596,7 +1540,7 @@ def users_compliance( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield UsersComplianceResponse.model_validate(data) + yield PostsSample10Response.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -1606,8 +1550,9 @@ def users_compliance( raise - def posts( + def posts_firehose_pt( self, + partition: int, backfill_minutes: int = None, start_time: str = None, end_time: str = None, @@ -1619,15 +1564,16 @@ def posts( place_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[PostsResponse, None, None]: + ) -> Generator[PostsFirehosePtResponse, None, None]: """ - Stream filtered Posts (Streaming) - Streams Posts in real-time matching the active rule set. + Stream Portuguese Posts (Streaming) + Streams all public Portuguese-language Posts in real-time. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided. + partition: The partition number. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp to which the Posts will be provided. end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. tweet_fields: A comma separated list of Tweet fields to display. expansions: A comma separated list of fields to expand. @@ -1638,12 +1584,12 @@ def posts( timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - PostsResponse: Individual streaming data items + PostsFirehosePtResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/search/stream" + url = self.client.base_url + "/2/tweets/firehose/stream/lang/pt" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1655,6 +1601,8 @@ def posts( params = {} if backfill_minutes is not None: params["backfill_minutes"] = backfill_minutes + if partition is not None: + params["partition"] = partition if start_time is not None: params["start_time"] = start_time if end_time is not None: @@ -1707,7 +1655,7 @@ def posts( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield PostsResponse.model_validate(data) + yield PostsFirehosePtResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -1718,7 +1666,7 @@ def posts( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield PostsResponse.model_validate(data) + yield PostsFirehosePtResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass @@ -1728,34 +1676,80 @@ def posts( raise - def posts_compliance( + def get_rule_counts(self, rules_count_fields: List = None) -> GetRuleCountsResponse: + """ + Get stream rule counts + Retrieves the count of rules in the active rule set for the filtered stream. + Args: + rules_count_fields: A comma separated list of RulesCount fields to display. + Returns: + GetRuleCountsResponse: Response data + """ + url = self.client.base_url + "/2/tweets/search/stream/rules/counts" + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + params = {} + if rules_count_fields is not None: + params["rules_count.fields"] = ",".join( + str(item) for item in rules_count_fields + ) + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetRuleCountsResponse.model_validate(response_data) + + + def posts_sample( self, - partition: int, backfill_minutes: int = None, - start_time: str = None, - end_time: str = None, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, timeout: Optional[float] = None, chunk_size: int = 1024, - ) -> Generator[PostsComplianceResponse, None, None]: + ) -> Generator[PostsSampleResponse, None, None]: """ - Stream Posts compliance data (Streaming) - Streams all compliance data related to Posts. + Stream sampled Posts (Streaming) + Streams a 1% sample of public Posts in real-time. This is a streaming endpoint that yields data in real-time as it becomes available. Each yielded item represents a single data point from the stream. Args: backfill_minutes: The number of minutes of backfill requested. - partition: The partition number. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Post Compliance events will be provided. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Post Compliance events will be provided. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. timeout: Request timeout in seconds (default: None for no timeout) chunk_size: Size of chunks to read from the stream (default: 1024 bytes) Yields: - PostsComplianceResponse: Individual streaming data items + PostsSampleResponse: Individual streaming data items Raises: requests.exceptions.RequestException: If the streaming connection fails json.JSONDecodeError: If the streamed data is not valid JSON """ - url = self.client.base_url + "/2/tweets/compliance/stream" + url = self.client.base_url + "/2/tweets/sample/stream" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1767,12 +1761,18 @@ def posts_compliance( params = {} if backfill_minutes is not None: params["backfill_minutes"] = backfill_minutes - if partition is not None: - params["partition"] = partition - if start_time is not None: - params["start_time"] = start_time - if end_time is not None: - params["end_time"] = end_time + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = { "Accept": "application/json", } @@ -1809,7 +1809,7 @@ def posts_compliance( # Parse JSON line data = json.loads(line) # Convert to response model if available - yield PostsComplianceResponse.model_validate(data) + yield PostsSampleResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON lines continue @@ -1820,7 +1820,7 @@ def posts_compliance( if buffer.strip(): try: data = json.loads(buffer.strip()) - yield PostsComplianceResponse.model_validate(data) + yield PostsSampleResponse.model_validate(data) except json.JSONDecodeError: # Skip invalid JSON in final buffer pass diff --git a/xdk/stream/models.py b/xdk/stream/models.py index 95583bc..a994c4b 100644 --- a/xdk/stream/models.py +++ b/xdk/stream/models.py @@ -16,107 +16,107 @@ from datetime import datetime -# Models for labels_compliance +# Models for likes_compliance -class LabelsComplianceResponse(BaseModel): - """Response model for labels_compliance""" +class LikesComplianceResponse(BaseModel): + """Response model for likes_compliance""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for posts_firehose_ja +# Models for get_rules -class PostsFirehoseJaResponse(BaseModel): - """Response model for posts_firehose_ja""" +class GetRulesResponse(BaseModel): + """Response model for get_rules""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for likes_compliance - +# Models for update_rules -class LikesComplianceResponse(BaseModel): - """Response model for likes_compliance""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") +class UpdateRulesRequest(BaseModel): + """Request model for update_rules""" -# Models for posts_firehose + model_config = ConfigDict(populate_by_name=True) -class PostsFirehoseResponse(BaseModel): - """Response model for posts_firehose""" +class UpdateRulesResponse(BaseModel): + """Response model for update_rules""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_rules +# Models for posts_firehose_en -class GetRulesResponse(BaseModel): - """Response model for get_rules""" +class PostsFirehoseEnResponse(BaseModel): + """Response model for posts_firehose_en""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for update_rules +# Models for posts -class UpdateRulesRequest(BaseModel): - """Request model for update_rules""" +class PostsResponse(BaseModel): + """Response model for posts""" - model_config = ConfigDict(populate_by_name=True) + model_config = ConfigDict(populate_by_name=True, extra="allow") -class UpdateRulesResponse(BaseModel): - """Response model for update_rules""" +# Models for posts_firehose + + +class PostsFirehoseResponse(BaseModel): + """Response model for posts_firehose""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for posts_sample +# Models for posts_firehose_ja -class PostsSampleResponse(BaseModel): - """Response model for posts_sample""" +class PostsFirehoseJaResponse(BaseModel): + """Response model for posts_firehose_ja""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for posts_firehose_en +# Models for likes_sample10 -class PostsFirehoseEnResponse(BaseModel): - """Response model for posts_firehose_en""" +class LikesSample10Response(BaseModel): + """Response model for likes_sample10""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for posts_sample10 +# Models for posts_compliance -class PostsSample10Response(BaseModel): - """Response model for posts_sample10""" +class PostsComplianceResponse(BaseModel): + """Response model for posts_compliance""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_rule_counts +# Models for users_compliance -class GetRuleCountsResponse(BaseModel): - """Response model for get_rule_counts""" +class UsersComplianceResponse(BaseModel): + """Response model for users_compliance""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for posts_firehose_pt +# Models for likes_firehose -class PostsFirehosePtResponse(BaseModel): - """Response model for posts_firehose_pt""" +class LikesFirehoseResponse(BaseModel): + """Response model for likes_firehose""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -130,46 +130,46 @@ class PostsFirehoseKoResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for likes_firehose +# Models for labels_compliance -class LikesFirehoseResponse(BaseModel): - """Response model for likes_firehose""" +class LabelsComplianceResponse(BaseModel): + """Response model for labels_compliance""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for likes_sample10 +# Models for posts_sample10 -class LikesSample10Response(BaseModel): - """Response model for likes_sample10""" +class PostsSample10Response(BaseModel): + """Response model for posts_sample10""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for users_compliance +# Models for posts_firehose_pt -class UsersComplianceResponse(BaseModel): - """Response model for users_compliance""" +class PostsFirehosePtResponse(BaseModel): + """Response model for posts_firehose_pt""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for posts +# Models for get_rule_counts -class PostsResponse(BaseModel): - """Response model for posts""" +class GetRuleCountsResponse(BaseModel): + """Response model for get_rule_counts""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for posts_compliance +# Models for posts_sample -class PostsComplianceResponse(BaseModel): - """Response model for posts_compliance""" +class PostsSampleResponse(BaseModel): + """Response model for posts_sample""" model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/trends/client.py b/xdk/trends/client.py index e438c1e..ea59713 100644 --- a/xdk/trends/client.py +++ b/xdk/trends/client.py @@ -21,9 +21,9 @@ if TYPE_CHECKING: from ..client import Client from .models import ( + GetAiResponse, GetPersonalizedResponse, GetByWoeidResponse, - GetAiResponse, ) @@ -35,6 +35,46 @@ def __init__(self, client: Client): self.client = client + def get_ai(self, id: Any, news_fields: List = None) -> GetAiResponse: + """ + Get AI Trends by ID + Retrieves an AI trend by its ID. + Args: + id: The ID of the ai trend. + news_fields: A comma separated list of News fields to display. + Returns: + GetAiResponse: Response data + """ + url = self.client.base_url + "/2/ai_trends/{id}" + url = url.replace("{id}", str(id)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + params = {} + if news_fields is not None: + params["news.fields"] = ",".join(str(item) for item in news_fields) + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetAiResponse.model_validate(response_data) + + def get_personalized( self, personalized_trend_fields: List = None ) -> GetPersonalizedResponse: @@ -124,43 +164,3 @@ def get_by_woeid( response_data = response.json() # Convert to Pydantic model if applicable return GetByWoeidResponse.model_validate(response_data) - - - def get_ai(self, id: Any, news_fields: List = None) -> GetAiResponse: - """ - Get AI Trends by ID - Retrieves an AI trend by its ID. - Args: - id: The ID of the ai trend. - news_fields: A comma separated list of News fields to display. - Returns: - GetAiResponse: Response data - """ - url = self.client.base_url + "/2/ai_trends/{id}" - url = url.replace("{id}", str(id)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - params = {} - if news_fields is not None: - params["news.fields"] = ",".join(str(item) for item in news_fields) - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return GetAiResponse.model_validate(response_data) diff --git a/xdk/trends/models.py b/xdk/trends/models.py index 5a96547..8b7d65e 100644 --- a/xdk/trends/models.py +++ b/xdk/trends/models.py @@ -16,28 +16,28 @@ from datetime import datetime -# Models for get_personalized +# Models for get_ai -class GetPersonalizedResponse(BaseModel): - """Response model for get_personalized""" +class GetAiResponse(BaseModel): + """Response model for get_ai""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_by_woeid +# Models for get_personalized -class GetByWoeidResponse(BaseModel): - """Response model for get_by_woeid""" +class GetPersonalizedResponse(BaseModel): + """Response model for get_personalized""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_ai +# Models for get_by_woeid -class GetAiResponse(BaseModel): - """Response model for get_ai""" +class GetByWoeidResponse(BaseModel): + """Response model for get_by_woeid""" model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/users/client.py b/xdk/users/client.py index b6363fd..659c979 100644 --- a/xdk/users/client.py +++ b/xdk/users/client.py @@ -21,51 +21,51 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - GetByIdsResponse, - UnfollowUserResponse, - GetLikedPostsResponse, - UnfollowListResponse, - GetFollowingResponse, - FollowUserRequest, - FollowUserResponse, - UnmuteUserResponse, - RepostPostRequest, - RepostPostResponse, - GetBlockingResponse, - GetFollowersResponse, + GetBookmarksResponse, + CreateBookmarkRequest, + CreateBookmarkResponse, GetMentionsResponse, - GetPinnedListsResponse, - PinListRequest, - PinListResponse, + GetOwnedListsResponse, UnpinListResponse, - DeleteBookmarkResponse, BlockDmsResponse, - UnlikePostResponse, - GetBookmarksByFolderIdResponse, - GetBookmarkFoldersResponse, + RepostPostRequest, + RepostPostResponse, + GetFollowingResponse, + FollowUserRequest, + FollowUserResponse, UnrepostPostResponse, + GetByUsernamesResponse, + GetLikedPostsResponse, GetRepostsOfMeResponse, - GetListMembershipsResponse, - GetTimelineResponse, + GetBookmarkFoldersResponse, + DeleteBookmarkResponse, LikePostRequest, LikePostResponse, - GetMeResponse, - GetByUsernamesResponse, - GetPostsResponse, - GetFollowedListsResponse, - FollowListRequest, - FollowListResponse, - GetByUsernameResponse, - GetOwnedListsResponse, - GetByIdResponse, UnblockDmsResponse, - SearchResponse, + UnfollowUserResponse, + GetListMembershipsResponse, GetMutingResponse, MuteUserRequest, MuteUserResponse, - GetBookmarksResponse, - CreateBookmarkRequest, - CreateBookmarkResponse, + GetByUsernameResponse, + GetByIdResponse, + GetByIdsResponse, + GetBookmarksByFolderIdResponse, + GetFollowedListsResponse, + FollowListRequest, + FollowListResponse, + GetMeResponse, + UnlikePostResponse, + GetPostsResponse, + UnfollowListResponse, + SearchResponse, + GetBlockingResponse, + GetTimelineResponse, + UnmuteUserResponse, + GetPinnedListsResponse, + PinListRequest, + PinListResponse, + GetFollowersResponse, ) @@ -77,113 +77,7 @@ def __init__(self, client: Client): self.client = client - def get_by_ids( - self, - ids: List, - user_fields: List = None, - expansions: List = None, - tweet_fields: List = None, - ) -> GetByIdsResponse: - """ - Get Users by IDs - Retrieves details of multiple Users by their IDs. - Args: - ids: A list of User IDs, comma-separated. You can specify up to 100 IDs. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. - tweet_fields: A comma separated list of Tweet fields to display. - Returns: - GetByIdsResponse: Response data - """ - url = self.client.base_url + "/2/users" - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - if ids is not None: - params["ids"] = ",".join(str(item) for item in ids) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - headers = {} - # Prepare request data - json_data = None - # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return GetByIdsResponse.model_validate(response_data) - - - def unfollow_user( - self, source_user_id: Any, target_user_id: Any - ) -> UnfollowUserResponse: - """ - Unfollow User - Causes the authenticated user to unfollow a specific user by their ID. - Args: - source_user_id: The ID of the authenticated source User that is requesting to unfollow the target User. - target_user_id: The ID of the User that the source User is requesting to unfollow. - Returns: - UnfollowUserResponse: Response data - """ - url = ( - self.client.base_url - + "/2/users/{source_user_id}/following/{target_user_id}" - ) - url = url.replace("{source_user_id}", str(source_user_id)) - url = url.replace("{target_user_id}", str(target_user_id)) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - headers = {} - # Prepare request data - json_data = None - # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.delete( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.delete( - url, - params=params, - headers=headers, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return UnfollowUserResponse.model_validate(response_data) - - - def get_liked_posts( + def get_bookmarks( self, id: Any, max_results: int = None, @@ -194,12 +88,12 @@ def get_liked_posts( poll_fields: List = None, user_fields: List = None, place_fields: List = None, - ) -> GetLikedPostsResponse: + ) -> GetBookmarksResponse: """ - Get liked Posts - Retrieves a list of Posts liked by a specific User by their ID. + Get Bookmarks + Retrieves a list of Posts bookmarked by the authenticated user. Args: - id: The ID of the User to lookup. + id: The ID of the authenticated source User for whom to return results. max_results: The maximum number of results. pagination_token: This parameter is used to get the next 'page' of results. tweet_fields: A comma separated list of Tweet fields to display. @@ -209,9 +103,9 @@ def get_liked_posts( user_fields: A comma separated list of User fields to display. place_fields: A comma separated list of Place fields to display. Returns: - GetLikedPostsResponse: Response data + GetBookmarksResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/liked_tweets" + url = self.client.base_url + "/2/users/{id}/bookmarks" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -256,22 +150,23 @@ def get_liked_posts( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetLikedPostsResponse.model_validate(response_data) + return GetBookmarksResponse.model_validate(response_data) - def unfollow_list(self, id: Any, list_id: Any) -> UnfollowListResponse: + def create_bookmark( + self, id: Any, body: CreateBookmarkRequest + ) -> CreateBookmarkResponse: """ - Unfollow List - Causes the authenticated user to unfollow a specific List by its ID. + Create Bookmark + Adds a post to the authenticated user’s bookmarks. Args: - id: The ID of the authenticated source User that will unfollow the List. - list_id: The ID of the List to unfollow. - Returns: - UnfollowListResponse: Response data + id: The ID of the authenticated source User for whom to add bookmarks. + body: Request body + Returns: + CreateBookmarkResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/followed_lists/{list_id}" + url = self.client.base_url + "/2/users/{id}/bookmarks" url = url.replace("{id}", str(id)) - url = url.replace("{list_id}", str(list_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -279,52 +174,75 @@ def unfollow_list(self, id: Any, list_id: Any) -> UnfollowListResponse: self.client.refresh_token() params = {} headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.delete( + response = self.client.oauth2_session.post( url, params=params, headers=headers, + json=json_data, ) else: - response = self.client.session.delete( + response = self.client.session.post( url, params=params, headers=headers, + json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return UnfollowListResponse.model_validate(response_data) + return CreateBookmarkResponse.model_validate(response_data) - def get_following( + def get_mentions( self, id: Any, + since_id: Any = None, + until_id: Any = None, max_results: int = None, pagination_token: Any = None, - user_fields: List = None, - expansions: List = None, + start_time: str = None, + end_time: str = None, tweet_fields: List = None, - ) -> GetFollowingResponse: + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + ) -> GetMentionsResponse: """ - Get following - Retrieves a list of Users followed by a specific User by their ID. + Get mentions + Retrieves a list of Posts that mention a specific User by their ID. Args: id: The ID of the User to lookup. + since_id: The minimum Post ID to be included in the result set. This parameter takes precedence over start_time if both are specified. + until_id: The maximum Post ID to be included in the result set. This parameter takes precedence over end_time if both are specified. max_results: The maximum number of results. - pagination_token: This parameter is used to get a specified 'page' of results. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. + pagination_token: This parameter is used to get the next 'page' of results. + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided. The since_id parameter takes precedence if it is also specified. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. The until_id parameter takes precedence if it is also specified. tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. Returns: - GetFollowingResponse: Response data + GetMentionsResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/following" + url = self.client.base_url + "/2/users/{id}/mentions" url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( @@ -340,16 +258,30 @@ def get_following( if self.client.is_token_expired(): self.client.refresh_token() params = {} + if since_id is not None: + params["since_id"] = since_id + if until_id is not None: + params["until_id"] = until_id if max_results is not None: params["max_results"] = max_results if pagination_token is not None: params["pagination_token"] = pagination_token - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) + if start_time is not None: + params["start_time"] = start_time + if end_time is not None: + params["end_time"] = end_time if tweet_fields is not None: params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} # Prepare request data json_data = None @@ -364,77 +296,87 @@ def get_following( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetFollowingResponse.model_validate(response_data) + return GetMentionsResponse.model_validate(response_data) - def follow_user( - self, id: Any, body: Optional[FollowUserRequest] = None - ) -> FollowUserResponse: + def get_owned_lists( + self, + id: Any, + max_results: int = None, + pagination_token: Any = None, + list_fields: List = None, + expansions: List = None, + user_fields: List = None, + ) -> GetOwnedListsResponse: """ - Follow User - Causes the authenticated user to follow a specific user by their ID. + Get owned Lists + Retrieves a list of Lists owned by a specific User by their ID. Args: - id: The ID of the authenticated source User that is requesting to follow the target User. - body: Request body - Returns: - FollowUserResponse: Response data + id: The ID of the User to lookup. + max_results: The maximum number of results. + pagination_token: This parameter is used to get a specified 'page' of results. + list_fields: A comma separated list of List fields to display. + expansions: A comma separated list of fields to expand. + user_fields: A comma separated list of User fields to display. + Returns: + GetOwnedListsResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/following" + url = self.client.base_url + "/2/users/{id}/owned_lists" url = url.replace("{id}", str(id)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token + if list_fields is not None: + params["list.fields"] = ",".join(str(item) for item in list_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.post( - url, - params=params, - headers=headers, - json=json_data, - ) - else: - response = self.client.session.post( - url, - params=params, - headers=headers, - json=json_data, - ) + response = self.client.session.get( + url, + params=params, + headers=headers, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return FollowUserResponse.model_validate(response_data) + return GetOwnedListsResponse.model_validate(response_data) - def unmute_user( - self, source_user_id: Any, target_user_id: Any - ) -> UnmuteUserResponse: + def unpin_list(self, id: Any, list_id: Any) -> UnpinListResponse: """ - Unmute User - Causes the authenticated user to unmute a specific user by their ID. + Unpin List + Causes the authenticated user to unpin a specific List by its ID. Args: - source_user_id: The ID of the authenticated source User that is requesting to unmute the target User. - target_user_id: The ID of the User that the source User is requesting to unmute. + id: The ID of the authenticated source User for whom to return results. + list_id: The ID of the List to unpin. Returns: - UnmuteUserResponse: Response data + UnpinListResponse: Response data """ - url = self.client.base_url + "/2/users/{source_user_id}/muting/{target_user_id}" - url = url.replace("{source_user_id}", str(source_user_id)) - url = url.replace("{target_user_id}", str(target_user_id)) + url = self.client.base_url + "/2/users/{id}/pinned_lists/{list_id}" + url = url.replace("{id}", str(id)) + url = url.replace("{list_id}", str(list_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -462,22 +404,19 @@ def unmute_user( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return UnmuteUserResponse.model_validate(response_data) + return UnpinListResponse.model_validate(response_data) - def repost_post( - self, id: Any, body: Optional[RepostPostRequest] = None - ) -> RepostPostResponse: + def block_dms(self, id: Any) -> BlockDmsResponse: """ - Repost Post - Causes the authenticated user to repost a specific Post by its ID. + Block DMs + Blocks direct messages to or from a specific User by their ID for the authenticated user. Args: - id: The ID of the authenticated source User that is requesting to repost the Post. - body: Request body - Returns: - RepostPostResponse: Response data + id: The ID of the target User that the authenticated user requesting to block dms for. + Returns: + BlockDmsResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/retweets" + url = self.client.base_url + "/2/users/{id}/dm/block" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -486,204 +425,105 @@ def repost_post( self.client.refresh_token() params = {} headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) # Make the request if self.client.oauth2_session: response = self.client.oauth2_session.post( url, params=params, headers=headers, - json=json_data, ) else: response = self.client.session.post( url, params=params, headers=headers, - json=json_data, - ) - # Check for errors - response.raise_for_status() - # Parse the response data - response_data = response.json() - # Convert to Pydantic model if applicable - return RepostPostResponse.model_validate(response_data) - - - def get_blocking( - self, - id: Any, - max_results: int = None, - pagination_token: Any = None, - user_fields: List = None, - expansions: List = None, - tweet_fields: List = None, - ) -> GetBlockingResponse: - """ - Get blocking - Retrieves a list of Users blocked by the specified User ID. - Args: - id: The ID of the authenticated source User for whom to return results. - max_results: The maximum number of results. - pagination_token: This parameter is used to get a specified 'page' of results. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. - tweet_fields: A comma separated list of Tweet fields to display. - Returns: - GetBlockingResponse: Response data - """ - url = self.client.base_url + "/2/users/{id}/blocking" - url = url.replace("{id}", str(id)) - # Ensure we have a valid access token - if self.client.oauth2_auth and self.client.token: - # Check if token needs refresh - if self.client.is_token_expired(): - self.client.refresh_token() - params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - headers = {} - # Prepare request data - json_data = None - # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.get( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.get( - url, - params=params, - headers=headers, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetBlockingResponse.model_validate(response_data) + return BlockDmsResponse.model_validate(response_data) - def get_followers( - self, - id: Any, - max_results: int = None, - pagination_token: Any = None, - user_fields: List = None, - expansions: List = None, - tweet_fields: List = None, - ) -> GetFollowersResponse: + def repost_post( + self, id: Any, body: Optional[RepostPostRequest] = None + ) -> RepostPostResponse: """ - Get followers - Retrieves a list of Users who follow a specific User by their ID. + Repost Post + Causes the authenticated user to repost a specific Post by its ID. Args: - id: The ID of the User to lookup. - max_results: The maximum number of results. - pagination_token: This parameter is used to get a specified 'page' of results. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. - tweet_fields: A comma separated list of Tweet fields to display. - Returns: - GetFollowersResponse: Response data - """ - url = self.client.base_url + "/2/users/{id}/followers" - url = url.replace("{id}", str(id)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) + id: The ID of the authenticated source User that is requesting to repost the Post. + body: Request body + Returns: + RepostPostResponse: Response data + """ + url = self.client.base_url + "/2/users/{id}/retweets" + url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) + if self.client.oauth2_session: + response = self.client.oauth2_session.post( + url, + params=params, + headers=headers, + json=json_data, + ) + else: + response = self.client.session.post( + url, + params=params, + headers=headers, + json=json_data, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetFollowersResponse.model_validate(response_data) + return RepostPostResponse.model_validate(response_data) - def get_mentions( + def get_following( self, id: Any, - since_id: Any = None, - until_id: Any = None, max_results: int = None, pagination_token: Any = None, - start_time: str = None, - end_time: str = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, user_fields: List = None, - place_fields: List = None, - ) -> GetMentionsResponse: + expansions: List = None, + tweet_fields: List = None, + ) -> GetFollowingResponse: """ - Get mentions - Retrieves a list of Posts that mention a specific User by their ID. + Get following + Retrieves a list of Users followed by a specific User by their ID. Args: id: The ID of the User to lookup. - since_id: The minimum Post ID to be included in the result set. This parameter takes precedence over start_time if both are specified. - until_id: The maximum Post ID to be included in the result set. This parameter takes precedence over end_time if both are specified. max_results: The maximum number of results. - pagination_token: This parameter is used to get the next 'page' of results. - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided. The since_id parameter takes precedence if it is also specified. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. The until_id parameter takes precedence if it is also specified. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. + pagination_token: This parameter is used to get a specified 'page' of results. user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetMentionsResponse: Response data + GetFollowingResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/mentions" + url = self.client.base_url + "/2/users/{id}/following" url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( @@ -699,30 +539,16 @@ def get_mentions( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if since_id is not None: - params["since_id"] = since_id - if until_id is not None: - params["until_id"] = until_id if max_results is not None: params["max_results"] = max_results if pagination_token is not None: params["pagination_token"] = pagination_token - if start_time is not None: - params["start_time"] = start_time - if end_time is not None: - params["end_time"] = end_time - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None @@ -737,28 +563,22 @@ def get_mentions( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetMentionsResponse.model_validate(response_data) + return GetFollowingResponse.model_validate(response_data) - def get_pinned_lists( - self, - id: Any, - list_fields: List = None, - expansions: List = None, - user_fields: List = None, - ) -> GetPinnedListsResponse: + def follow_user( + self, id: Any, body: Optional[FollowUserRequest] = None + ) -> FollowUserResponse: """ - Get pinned Lists - Retrieves a list of Lists pinned by the authenticated user. + Follow User + Causes the authenticated user to follow a specific user by their ID. Args: - id: The ID of the authenticated source User for whom to return results. - list_fields: A comma separated list of List fields to display. - expansions: A comma separated list of fields to expand. - user_fields: A comma separated list of User fields to display. - Returns: - GetPinnedListsResponse: Response data + id: The ID of the authenticated source User that is requesting to follow the target User. + body: Request body + Returns: + FollowUserResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/pinned_lists" + url = self.client.base_url + "/2/users/{id}/following" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -766,48 +586,52 @@ def get_pinned_lists( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if list_fields is not None: - params["list.fields"] = ",".join(str(item) for item in list_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.get( + response = self.client.oauth2_session.post( url, params=params, headers=headers, + json=json_data, ) else: - response = self.client.session.get( + response = self.client.session.post( url, params=params, headers=headers, + json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetPinnedListsResponse.model_validate(response_data) + return FollowUserResponse.model_validate(response_data) - def pin_list(self, id: Any, body: PinListRequest) -> PinListResponse: + def unrepost_post(self, id: Any, source_tweet_id: Any) -> UnrepostPostResponse: """ - Pin List - Causes the authenticated user to pin a specific List by its ID. + Unrepost Post + Causes the authenticated user to unrepost a specific Post by its ID. Args: - id: The ID of the authenticated source User that will pin the List. - body: Request body - Returns: - PinListResponse: Response data + id: The ID of the authenticated source User that is requesting to repost the Post. + source_tweet_id: The ID of the Post that the User is requesting to unretweet. + Returns: + UnrepostPostResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/pinned_lists" + url = self.client.base_url + "/2/users/{id}/retweets/{source_tweet_id}" url = url.replace("{id}", str(id)) + url = url.replace("{source_tweet_id}", str(source_tweet_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -815,112 +639,151 @@ def pin_list(self, id: Any, body: PinListRequest) -> PinListResponse: self.client.refresh_token() params = {} headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.post( + response = self.client.oauth2_session.delete( url, params=params, headers=headers, - json=json_data, ) else: - response = self.client.session.post( + response = self.client.session.delete( url, params=params, headers=headers, - json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return PinListResponse.model_validate(response_data) + return UnrepostPostResponse.model_validate(response_data) - def unpin_list(self, id: Any, list_id: Any) -> UnpinListResponse: + def get_by_usernames( + self, + usernames: List, + user_fields: List = None, + expansions: List = None, + tweet_fields: List = None, + ) -> GetByUsernamesResponse: """ - Unpin List - Causes the authenticated user to unpin a specific List by its ID. + Get Users by usernames + Retrieves details of multiple Users by their usernames. Args: - id: The ID of the authenticated source User for whom to return results. - list_id: The ID of the List to unpin. + usernames: A list of usernames, comma-separated. + user_fields: A comma separated list of User fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. Returns: - UnpinListResponse: Response data + GetByUsernamesResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/pinned_lists/{list_id}" - url = url.replace("{id}", str(id)) - url = url.replace("{list_id}", str(list_id)) + url = self.client.base_url + "/2/users/by" + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if usernames is not None: + params["usernames"] = ",".join(str(item) for item in usernames) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.delete( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.delete( - url, - params=params, - headers=headers, - ) + response = self.client.session.get( + url, + params=params, + headers=headers, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return UnpinListResponse.model_validate(response_data) + return GetByUsernamesResponse.model_validate(response_data) - def delete_bookmark(self, id: Any, tweet_id: Any) -> DeleteBookmarkResponse: + def get_liked_posts( + self, + id: Any, + max_results: int = None, + pagination_token: Any = None, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + ) -> GetLikedPostsResponse: """ - Delete Bookmark - Removes a Post from the authenticated user’s Bookmarks by its ID. + Get liked Posts + Retrieves a list of Posts liked by a specific User by their ID. Args: - id: The ID of the authenticated source User whose bookmark is to be removed. - tweet_id: The ID of the Post that the source User is removing from bookmarks. + id: The ID of the User to lookup. + max_results: The maximum number of results. + pagination_token: This parameter is used to get the next 'page' of results. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. Returns: - DeleteBookmarkResponse: Response data + GetLikedPostsResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/bookmarks/{tweet_id}" + url = self.client.base_url + "/2/users/{id}/liked_tweets" url = url.replace("{id}", str(id)) - url = url.replace("{tweet_id}", str(tweet_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} # Prepare request data json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.delete( + response = self.client.oauth2_session.get( url, params=params, headers=headers, ) else: - response = self.client.session.delete( + response = self.client.session.get( url, params=params, headers=headers, @@ -930,38 +793,70 @@ def delete_bookmark(self, id: Any, tweet_id: Any) -> DeleteBookmarkResponse: # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return DeleteBookmarkResponse.model_validate(response_data) + return GetLikedPostsResponse.model_validate(response_data) - def block_dms(self, id: Any) -> BlockDmsResponse: + def get_reposts_of_me( + self, + max_results: int = None, + pagination_token: Any = None, + tweet_fields: List = None, + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + ) -> GetRepostsOfMeResponse: """ - Block DMs - Blocks direct messages to or from a specific User by their ID for the authenticated user. + Get Reposts of me + Retrieves a list of Posts that repost content from the authenticated user. Args: - id: The ID of the target User that the authenticated user requesting to block dms for. + max_results: The maximum number of results. + pagination_token: This parameter is used to get the next 'page' of results. + tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. Returns: - BlockDmsResponse: Response data + GetRepostsOfMeResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/dm/block" - url = url.replace("{id}", str(id)) + url = self.client.base_url + "/2/users/reposts_of_me" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} # Prepare request data json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.post( + response = self.client.oauth2_session.get( url, params=params, headers=headers, ) else: - response = self.client.session.post( + response = self.client.session.get( url, params=params, headers=headers, @@ -971,40 +866,46 @@ def block_dms(self, id: Any) -> BlockDmsResponse: # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return BlockDmsResponse.model_validate(response_data) + return GetRepostsOfMeResponse.model_validate(response_data) - def unlike_post(self, id: Any, tweet_id: Any) -> UnlikePostResponse: + def get_bookmark_folders( + self, id: Any, max_results: int = None, pagination_token: Any = None + ) -> GetBookmarkFoldersResponse: """ - Unlike Post - Causes the authenticated user to Unlike a specific Post by its ID. + Get Bookmark folders + Retrieves a list of Bookmark folders created by the authenticated user. Args: - id: The ID of the authenticated source User that is requesting to unlike the Post. - tweet_id: The ID of the Post that the User is requesting to unlike. + id: The ID of the authenticated source User for whom to return results. + max_results: The maximum number of results. + pagination_token: This parameter is used to get the next 'page' of results. Returns: - UnlikePostResponse: Response data + GetBookmarkFoldersResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/likes/{tweet_id}" + url = self.client.base_url + "/2/users/{id}/bookmarks/folders" url = url.replace("{id}", str(id)) - url = url.replace("{tweet_id}", str(tweet_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token headers = {} # Prepare request data json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.delete( + response = self.client.oauth2_session.get( url, params=params, headers=headers, ) else: - response = self.client.session.delete( + response = self.client.session.get( url, params=params, headers=headers, @@ -1014,24 +915,22 @@ def unlike_post(self, id: Any, tweet_id: Any) -> UnlikePostResponse: # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return UnlikePostResponse.model_validate(response_data) + return GetBookmarkFoldersResponse.model_validate(response_data) - def get_bookmarks_by_folder_id( - self, id: Any, folder_id: Any - ) -> GetBookmarksByFolderIdResponse: + def delete_bookmark(self, id: Any, tweet_id: Any) -> DeleteBookmarkResponse: """ - Get Bookmarks by folder ID - Retrieves Posts in a specific Bookmark folder by its ID for the authenticated user. + Delete Bookmark + Removes a Post from the authenticated user’s Bookmarks by its ID. Args: - id: The ID of the authenticated source User for whom to return results. - folder_id: The ID of the Bookmark Folder that the authenticated User is trying to fetch Posts for. + id: The ID of the authenticated source User whose bookmark is to be removed. + tweet_id: The ID of the Post that the source User is removing from bookmarks. Returns: - GetBookmarksByFolderIdResponse: Response data + DeleteBookmarkResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/bookmarks/folders/{folder_id}" + url = self.client.base_url + "/2/users/{id}/bookmarks/{tweet_id}" url = url.replace("{id}", str(id)) - url = url.replace("{folder_id}", str(folder_id)) + url = url.replace("{tweet_id}", str(tweet_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -1043,13 +942,13 @@ def get_bookmarks_by_folder_id( json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.get( + response = self.client.oauth2_session.delete( url, params=params, headers=headers, ) else: - response = self.client.session.get( + response = self.client.session.delete( url, params=params, headers=headers, @@ -1059,23 +958,22 @@ def get_bookmarks_by_folder_id( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetBookmarksByFolderIdResponse.model_validate(response_data) + return DeleteBookmarkResponse.model_validate(response_data) - def get_bookmark_folders( - self, id: Any, max_results: int = None, pagination_token: Any = None - ) -> GetBookmarkFoldersResponse: + def like_post( + self, id: Any, body: Optional[LikePostRequest] = None + ) -> LikePostResponse: """ - Get Bookmark folders - Retrieves a list of Bookmark folders created by the authenticated user. + Like Post + Causes the authenticated user to Like a specific Post by its ID. Args: - id: The ID of the authenticated source User for whom to return results. - max_results: The maximum number of results. - pagination_token: This parameter is used to get the next 'page' of results. - Returns: - GetBookmarkFoldersResponse: Response data + id: The ID of the authenticated source User that is requesting to like the Post. + body: Request body + Returns: + LikePostResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/bookmarks/folders" + url = self.client.base_url + "/2/users/{id}/likes" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -1083,47 +981,50 @@ def get_bookmark_folders( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.get( + response = self.client.oauth2_session.post( url, params=params, headers=headers, + json=json_data, ) else: - response = self.client.session.get( + response = self.client.session.post( url, params=params, headers=headers, + json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetBookmarkFoldersResponse.model_validate(response_data) + return LikePostResponse.model_validate(response_data) - def unrepost_post(self, id: Any, source_tweet_id: Any) -> UnrepostPostResponse: + def unblock_dms(self, id: Any) -> UnblockDmsResponse: """ - Unrepost Post - Causes the authenticated user to unrepost a specific Post by its ID. + Unblock DMs + Unblocks direct messages to or from a specific User by their ID for the authenticated user. Args: - id: The ID of the authenticated source User that is requesting to repost the Post. - source_tweet_id: The ID of the Post that the User is requesting to unretweet. + id: The ID of the target User that the authenticated user requesting to unblock dms for. Returns: - UnrepostPostResponse: Response data + UnblockDmsResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/retweets/{source_tweet_id}" + url = self.client.base_url + "/2/users/{id}/dm/unblock" url = url.replace("{id}", str(id)) - url = url.replace("{source_tweet_id}", str(source_tweet_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -1135,13 +1036,13 @@ def unrepost_post(self, id: Any, source_tweet_id: Any) -> UnrepostPostResponse: json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.delete( + response = self.client.oauth2_session.post( url, params=params, headers=headers, ) else: - response = self.client.session.delete( + response = self.client.session.post( url, params=params, headers=headers, @@ -1151,70 +1052,45 @@ def unrepost_post(self, id: Any, source_tweet_id: Any) -> UnrepostPostResponse: # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return UnrepostPostResponse.model_validate(response_data) + return UnblockDmsResponse.model_validate(response_data) - def get_reposts_of_me( - self, - max_results: int = None, - pagination_token: Any = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, - user_fields: List = None, - place_fields: List = None, - ) -> GetRepostsOfMeResponse: + def unfollow_user( + self, source_user_id: Any, target_user_id: Any + ) -> UnfollowUserResponse: """ - Get Reposts of me - Retrieves a list of Posts that repost content from the authenticated user. + Unfollow User + Causes the authenticated user to unfollow a specific user by their ID. Args: - max_results: The maximum number of results. - pagination_token: This parameter is used to get the next 'page' of results. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. - user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + source_user_id: The ID of the authenticated source User that is requesting to unfollow the target User. + target_user_id: The ID of the User that the source User is requesting to unfollow. Returns: - GetRepostsOfMeResponse: Response data + UnfollowUserResponse: Response data """ - url = self.client.base_url + "/2/users/reposts_of_me" + url = ( + self.client.base_url + + "/2/users/{source_user_id}/following/{target_user_id}" + ) + url = url.replace("{source_user_id}", str(source_user_id)) + url = url.replace("{target_user_id}", str(target_user_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} # Prepare request data json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.get( + response = self.client.oauth2_session.delete( url, params=params, headers=headers, ) else: - response = self.client.session.get( + response = self.client.session.delete( url, params=params, headers=headers, @@ -1224,7 +1100,7 @@ def get_reposts_of_me( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetRepostsOfMeResponse.model_validate(response_data) + return UnfollowUserResponse.model_validate(response_data) def get_list_memberships( @@ -1292,45 +1168,29 @@ def get_list_memberships( return GetListMembershipsResponse.model_validate(response_data) - def get_timeline( + def get_muting( self, id: Any, - since_id: Any = None, - until_id: Any = None, max_results: int = None, pagination_token: Any = None, - exclude: List = None, - start_time: str = None, - end_time: str = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, user_fields: List = None, - place_fields: List = None, - ) -> GetTimelineResponse: + expansions: List = None, + tweet_fields: List = None, + ) -> GetMutingResponse: """ - Get Timeline - Retrieves a reverse chronological list of Posts in the authenticated User’s Timeline. + Get muting + Retrieves a list of Users muted by the authenticated user. Args: - id: The ID of the authenticated source User to list Reverse Chronological Timeline Posts of. - since_id: The minimum Post ID to be included in the result set. This parameter takes precedence over start_time if both are specified. - until_id: The maximum Post ID to be included in the result set. This parameter takes precedence over end_time if both are specified. + id: The ID of the authenticated source User for whom to return results. max_results: The maximum number of results. pagination_token: This parameter is used to get the next 'page' of results. - exclude: The set of entities to exclude (e.g. 'replies' or 'retweets'). - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided. The since_id parameter takes precedence if it is also specified. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. The until_id parameter takes precedence if it is also specified. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetTimelineResponse: Response data + GetMutingResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/timelines/reverse_chronological" + url = self.client.base_url + "/2/users/{id}/muting" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -1338,32 +1198,16 @@ def get_timeline( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if since_id is not None: - params["since_id"] = since_id - if until_id is not None: - params["until_id"] = until_id if max_results is not None: params["max_results"] = max_results if pagination_token is not None: params["pagination_token"] = pagination_token - if exclude is not None: - params["exclude"] = ",".join(str(item) for item in exclude) - if start_time is not None: - params["start_time"] = start_time - if end_time is not None: - params["end_time"] = end_time - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None @@ -1385,22 +1229,22 @@ def get_timeline( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetTimelineResponse.model_validate(response_data) + return GetMutingResponse.model_validate(response_data) - def like_post( - self, id: Any, body: Optional[LikePostRequest] = None - ) -> LikePostResponse: + def mute_user( + self, id: Any, body: Optional[MuteUserRequest] = None + ) -> MuteUserResponse: """ - Like Post - Causes the authenticated user to Like a specific Post by its ID. + Mute User + Causes the authenticated user to mute a specific User by their ID. Args: - id: The ID of the authenticated source User that is requesting to like the Post. + id: The ID of the authenticated source User that is requesting to mute the target User. body: Request body Returns: - LikePostResponse: Response data + MuteUserResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/likes" + url = self.client.base_url + "/2/users/{id}/muting" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -1438,26 +1282,37 @@ def like_post( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return LikePostResponse.model_validate(response_data) + return MuteUserResponse.model_validate(response_data) - def get_me( + def get_by_username( self, + username: str, user_fields: List = None, expansions: List = None, tweet_fields: List = None, - ) -> GetMeResponse: + ) -> GetByUsernameResponse: """ - Get my User - Retrieves details of the authenticated user. + Get User by username + Retrieves details of a specific User by their username. Args: + username: A username. user_fields: A comma separated list of User fields to display. expansions: A comma separated list of fields to expand. tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetMeResponse: Response data + GetByUsernameResponse: Response data """ - url = self.client.base_url + "/2/users/me" + url = self.client.base_url + "/2/users/by/username/{username}" + url = url.replace("{username}", str(username)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -1474,45 +1329,39 @@ def get_me( # Prepare request data json_data = None # Make the request - if self.client.oauth2_session: - response = self.client.oauth2_session.get( - url, - params=params, - headers=headers, - ) - else: - response = self.client.session.get( - url, - params=params, - headers=headers, - ) + response = self.client.session.get( + url, + params=params, + headers=headers, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetMeResponse.model_validate(response_data) + return GetByUsernameResponse.model_validate(response_data) - def get_by_usernames( + def get_by_id( self, - usernames: List, + id: Any, user_fields: List = None, expansions: List = None, tweet_fields: List = None, - ) -> GetByUsernamesResponse: + ) -> GetByIdResponse: """ - Get Users by usernames - Retrieves details of multiple Users by their usernames. + Get User by ID + Retrieves details of a specific User by their ID. Args: - usernames: A list of usernames, comma-separated. + id: The ID of the User to lookup. user_fields: A comma separated list of User fields to display. expansions: A comma separated list of fields to expand. tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetByUsernamesResponse: Response data + GetByIdResponse: Response data """ - url = self.client.base_url + "/2/users/by" + url = self.client.base_url + "/2/users/{id}" + url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1527,8 +1376,6 @@ def get_by_usernames( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if usernames is not None: - params["usernames"] = ",".join(str(item) for item in usernames) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) if expansions is not None: @@ -1549,49 +1396,28 @@ def get_by_usernames( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetByUsernamesResponse.model_validate(response_data) + return GetByIdResponse.model_validate(response_data) - def get_posts( + def get_by_ids( self, - id: Any, - since_id: Any = None, - until_id: Any = None, - max_results: int = None, - pagination_token: Any = None, - exclude: List = None, - start_time: str = None, - end_time: str = None, - tweet_fields: List = None, - expansions: List = None, - media_fields: List = None, - poll_fields: List = None, + ids: List, user_fields: List = None, - place_fields: List = None, - ) -> GetPostsResponse: + expansions: List = None, + tweet_fields: List = None, + ) -> GetByIdsResponse: """ - Get Posts - Retrieves a list of posts authored by a specific User by their ID. + Get Users by IDs + Retrieves details of multiple Users by their IDs. Args: - id: The ID of the User to lookup. - since_id: The minimum Post ID to be included in the result set. This parameter takes precedence over start_time if both are specified. - until_id: The maximum Post ID to be included in the result set. This parameter takes precedence over end_time if both are specified. - max_results: The maximum number of results. - pagination_token: This parameter is used to get the next 'page' of results. - exclude: The set of entities to exclude (e.g. 'replies' or 'retweets'). - start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided. The since_id parameter takes precedence if it is also specified. - end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. The until_id parameter takes precedence if it is also specified. - tweet_fields: A comma separated list of Tweet fields to display. - expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. + ids: A list of User IDs, comma-separated. You can specify up to 100 IDs. user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetPostsResponse: Response data + GetByIdsResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/tweets" - url = url.replace("{id}", str(id)) + url = self.client.base_url + "/2/users" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -1606,32 +1432,14 @@ def get_posts( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if since_id is not None: - params["since_id"] = since_id - if until_id is not None: - params["until_id"] = until_id - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if exclude is not None: - params["exclude"] = ",".join(str(item) for item in exclude) - if start_time is not None: - params["start_time"] = start_time - if end_time is not None: - params["end_time"] = end_time - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if ids is not None: + params["ids"] = ",".join(str(item) for item in ids) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None @@ -1646,7 +1454,52 @@ def get_posts( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetPostsResponse.model_validate(response_data) + return GetByIdsResponse.model_validate(response_data) + + + def get_bookmarks_by_folder_id( + self, id: Any, folder_id: Any + ) -> GetBookmarksByFolderIdResponse: + """ + Get Bookmarks by folder ID + Retrieves Posts in a specific Bookmark folder by its ID for the authenticated user. + Args: + id: The ID of the authenticated source User for whom to return results. + folder_id: The ID of the Bookmark Folder that the authenticated User is trying to fetch Posts for. + Returns: + GetBookmarksByFolderIdResponse: Response data + """ + url = self.client.base_url + "/2/users/{id}/bookmarks/folders/{folder_id}" + url = url.replace("{id}", str(id)) + url = url.replace("{folder_id}", str(folder_id)) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + headers = {} + # Prepare request data + json_data = None + # Make the request + if self.client.oauth2_session: + response = self.client.oauth2_session.get( + url, + params=params, + headers=headers, + ) + else: + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetBookmarksByFolderIdResponse.model_validate(response_data) def get_followed_lists( @@ -1767,34 +1620,23 @@ def follow_list( return FollowListResponse.model_validate(response_data) - def get_by_username( + def get_me( self, - username: str, user_fields: List = None, expansions: List = None, tweet_fields: List = None, - ) -> GetByUsernameResponse: + ) -> GetMeResponse: """ - Get User by username - Retrieves details of a specific User by their username. + Get my User + Retrieves details of the authenticated user. Args: - username: A username. user_fields: A comma separated list of User fields to display. expansions: A comma separated list of fields to expand. tweet_fields: A comma separated list of Tweet fields to display. Returns: - GetByUsernameResponse: Response data + GetMeResponse: Response data """ - url = self.client.base_url + "/2/users/by/username/{username}" - url = url.replace("{username}", str(username)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) + url = self.client.base_url + "/2/users/me" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -1811,42 +1653,108 @@ def get_by_username( # Prepare request data json_data = None # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) + if self.client.oauth2_session: + response = self.client.oauth2_session.get( + url, + params=params, + headers=headers, + ) + else: + response = self.client.session.get( + url, + params=params, + headers=headers, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetByUsernameResponse.model_validate(response_data) + return GetMeResponse.model_validate(response_data) - def get_owned_lists( + def unlike_post(self, id: Any, tweet_id: Any) -> UnlikePostResponse: + """ + Unlike Post + Causes the authenticated user to Unlike a specific Post by its ID. + Args: + id: The ID of the authenticated source User that is requesting to unlike the Post. + tweet_id: The ID of the Post that the User is requesting to unlike. + Returns: + UnlikePostResponse: Response data + """ + url = self.client.base_url + "/2/users/{id}/likes/{tweet_id}" + url = url.replace("{id}", str(id)) + url = url.replace("{tweet_id}", str(tweet_id)) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + headers = {} + # Prepare request data + json_data = None + # Make the request + if self.client.oauth2_session: + response = self.client.oauth2_session.delete( + url, + params=params, + headers=headers, + ) + else: + response = self.client.session.delete( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return UnlikePostResponse.model_validate(response_data) + + + def get_posts( self, id: Any, + since_id: Any = None, + until_id: Any = None, max_results: int = None, pagination_token: Any = None, - list_fields: List = None, + exclude: List = None, + start_time: str = None, + end_time: str = None, + tweet_fields: List = None, expansions: List = None, + media_fields: List = None, + poll_fields: List = None, user_fields: List = None, - ) -> GetOwnedListsResponse: + place_fields: List = None, + ) -> GetPostsResponse: """ - Get owned Lists - Retrieves a list of Lists owned by a specific User by their ID. + Get Posts + Retrieves a list of posts authored by a specific User by their ID. Args: id: The ID of the User to lookup. + since_id: The minimum Post ID to be included in the result set. This parameter takes precedence over start_time if both are specified. + until_id: The maximum Post ID to be included in the result set. This parameter takes precedence over end_time if both are specified. max_results: The maximum number of results. - pagination_token: This parameter is used to get a specified 'page' of results. - list_fields: A comma separated list of List fields to display. + pagination_token: This parameter is used to get the next 'page' of results. + exclude: The set of entities to exclude (e.g. 'replies' or 'retweets'). + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided. The since_id parameter takes precedence if it is also specified. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. The until_id parameter takes precedence if it is also specified. + tweet_fields: A comma separated list of Tweet fields to display. expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. Returns: - GetOwnedListsResponse: Response data + GetPostsResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/owned_lists" + url = self.client.base_url + "/2/users/{id}/tweets" url = url.replace("{id}", str(id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( @@ -1862,16 +1770,32 @@ def get_owned_lists( if self.client.is_token_expired(): self.client.refresh_token() params = {} + if since_id is not None: + params["since_id"] = since_id + if until_id is not None: + params["until_id"] = until_id if max_results is not None: params["max_results"] = max_results if pagination_token is not None: params["pagination_token"] = pagination_token - if list_fields is not None: - params["list.fields"] = ",".join(str(item) for item in list_fields) + if exclude is not None: + params["exclude"] = ",".join(str(item) for item in exclude) + if start_time is not None: + params["start_time"] = start_time + if end_time is not None: + params["end_time"] = end_time + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) if expansions is not None: params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} # Prepare request data json_data = None @@ -1886,95 +1810,105 @@ def get_owned_lists( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetOwnedListsResponse.model_validate(response_data) + return GetPostsResponse.model_validate(response_data) - def get_by_id( - self, - id: Any, - user_fields: List = None, - expansions: List = None, - tweet_fields: List = None, - ) -> GetByIdResponse: + def unfollow_list(self, id: Any, list_id: Any) -> UnfollowListResponse: """ - Get User by ID - Retrieves details of a specific User by their ID. + Unfollow List + Causes the authenticated user to unfollow a specific List by its ID. Args: - id: The ID of the User to lookup. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. - tweet_fields: A comma separated list of Tweet fields to display. + id: The ID of the authenticated source User that will unfollow the List. + list_id: The ID of the List to unfollow. Returns: - GetByIdResponse: Response data + UnfollowListResponse: Response data """ - url = self.client.base_url + "/2/users/{id}" + url = self.client.base_url + "/2/users/{id}/followed_lists/{list_id}" url = url.replace("{id}", str(id)) - if self.client.bearer_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.bearer_token}" - ) - elif self.client.access_token: - self.client.session.headers["Authorization"] = ( - f"Bearer {self.client.access_token}" - ) + url = url.replace("{list_id}", str(list_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None # Make the request - response = self.client.session.get( - url, - params=params, - headers=headers, - ) + if self.client.oauth2_session: + response = self.client.oauth2_session.delete( + url, + params=params, + headers=headers, + ) + else: + response = self.client.session.delete( + url, + params=params, + headers=headers, + ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetByIdResponse.model_validate(response_data) + return UnfollowListResponse.model_validate(response_data) - def unblock_dms(self, id: Any) -> UnblockDmsResponse: + def search( + self, + query: Any, + max_results: int = None, + next_token: Any = None, + user_fields: List = None, + expansions: List = None, + tweet_fields: List = None, + ) -> SearchResponse: """ - Unblock DMs - Unblocks direct messages to or from a specific User by their ID for the authenticated user. + Search Users + Retrieves a list of Users matching a search query. Args: - id: The ID of the target User that the authenticated user requesting to unblock dms for. + query: TThe the query string by which to query for users. + max_results: The maximum number of results. + next_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. + user_fields: A comma separated list of User fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. Returns: - UnblockDmsResponse: Response data + SearchResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/dm/unblock" - url = url.replace("{id}", str(id)) + url = self.client.base_url + "/2/users/search" # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} + if query is not None: + params["query"] = query + if max_results is not None: + params["max_results"] = max_results + if next_token is not None: + params["next_token"] = next_token + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) headers = {} # Prepare request data json_data = None # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.post( + response = self.client.oauth2_session.get( url, params=params, headers=headers, ) else: - response = self.client.session.post( + response = self.client.session.get( url, params=params, headers=headers, @@ -1984,44 +1918,43 @@ def unblock_dms(self, id: Any) -> UnblockDmsResponse: # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return UnblockDmsResponse.model_validate(response_data) + return SearchResponse.model_validate(response_data) - def search( + def get_blocking( self, - query: Any, + id: Any, max_results: int = None, - next_token: Any = None, + pagination_token: Any = None, user_fields: List = None, expansions: List = None, tweet_fields: List = None, - ) -> SearchResponse: + ) -> GetBlockingResponse: """ - Search Users - Retrieves a list of Users matching a search query. + Get blocking + Retrieves a list of Users blocked by the specified User ID. Args: - query: TThe the query string by which to query for users. + id: The ID of the authenticated source User for whom to return results. max_results: The maximum number of results. - next_token: This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified. + pagination_token: This parameter is used to get a specified 'page' of results. user_fields: A comma separated list of User fields to display. expansions: A comma separated list of fields to expand. tweet_fields: A comma separated list of Tweet fields to display. Returns: - SearchResponse: Response data + GetBlockingResponse: Response data """ - url = self.client.base_url + "/2/users/search" + url = self.client.base_url + "/2/users/{id}/blocking" + url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh if self.client.is_token_expired(): self.client.refresh_token() params = {} - if query is not None: - params["query"] = query if max_results is not None: params["max_results"] = max_results - if next_token is not None: - params["next_token"] = next_token + if pagination_token is not None: + params["pagination_token"] = pagination_token if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) if expansions is not None: @@ -2049,32 +1982,48 @@ def search( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return SearchResponse.model_validate(response_data) + return GetBlockingResponse.model_validate(response_data) - def get_muting( + def get_timeline( self, id: Any, + since_id: Any = None, + until_id: Any = None, max_results: int = None, pagination_token: Any = None, - user_fields: List = None, - expansions: List = None, + exclude: List = None, + start_time: str = None, + end_time: str = None, tweet_fields: List = None, - ) -> GetMutingResponse: + expansions: List = None, + media_fields: List = None, + poll_fields: List = None, + user_fields: List = None, + place_fields: List = None, + ) -> GetTimelineResponse: """ - Get muting - Retrieves a list of Users muted by the authenticated user. + Get Timeline + Retrieves a reverse chronological list of Posts in the authenticated User’s Timeline. Args: - id: The ID of the authenticated source User for whom to return results. + id: The ID of the authenticated source User to list Reverse Chronological Timeline Posts of. + since_id: The minimum Post ID to be included in the result set. This parameter takes precedence over start_time if both are specified. + until_id: The maximum Post ID to be included in the result set. This parameter takes precedence over end_time if both are specified. max_results: The maximum number of results. pagination_token: This parameter is used to get the next 'page' of results. - user_fields: A comma separated list of User fields to display. - expansions: A comma separated list of fields to expand. + exclude: The set of entities to exclude (e.g. 'replies' or 'retweets'). + start_time: YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Posts will be provided. The since_id parameter takes precedence if it is also specified. + end_time: YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Posts will be provided. The until_id parameter takes precedence if it is also specified. tweet_fields: A comma separated list of Tweet fields to display. + expansions: A comma separated list of fields to expand. + media_fields: A comma separated list of Media fields to display. + poll_fields: A comma separated list of Poll fields to display. + user_fields: A comma separated list of User fields to display. + place_fields: A comma separated list of Place fields to display. Returns: - GetMutingResponse: Response data + GetTimelineResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/muting" + url = self.client.base_url + "/2/users/{id}/timelines/reverse_chronological" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -2082,16 +2031,32 @@ def get_muting( if self.client.is_token_expired(): self.client.refresh_token() params = {} + if since_id is not None: + params["since_id"] = since_id + if until_id is not None: + params["until_id"] = until_id if max_results is not None: params["max_results"] = max_results if pagination_token is not None: params["pagination_token"] = pagination_token - if user_fields is not None: - params["user.fields"] = ",".join(str(item) for item in user_fields) - if expansions is not None: - params["expansions"] = ",".join(str(item) for item in expansions) + if exclude is not None: + params["exclude"] = ",".join(str(item) for item in exclude) + if start_time is not None: + params["start_time"] = start_time + if end_time is not None: + params["end_time"] = end_time if tweet_fields is not None: params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if media_fields is not None: + params["media.fields"] = ",".join(str(item) for item in media_fields) + if poll_fields is not None: + params["poll.fields"] = ",".join(str(item) for item in poll_fields) + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if place_fields is not None: + params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} # Prepare request data json_data = None @@ -2113,23 +2078,24 @@ def get_muting( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetMutingResponse.model_validate(response_data) + return GetTimelineResponse.model_validate(response_data) - def mute_user( - self, id: Any, body: Optional[MuteUserRequest] = None - ) -> MuteUserResponse: + def unmute_user( + self, source_user_id: Any, target_user_id: Any + ) -> UnmuteUserResponse: """ - Mute User - Causes the authenticated user to mute a specific User by their ID. + Unmute User + Causes the authenticated user to unmute a specific user by their ID. Args: - id: The ID of the authenticated source User that is requesting to mute the target User. - body: Request body - Returns: - MuteUserResponse: Response data + source_user_id: The ID of the authenticated source User that is requesting to unmute the target User. + target_user_id: The ID of the User that the source User is requesting to unmute. + Returns: + UnmuteUserResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/muting" - url = url.replace("{id}", str(id)) + url = self.client.base_url + "/2/users/{source_user_id}/muting/{target_user_id}" + url = url.replace("{source_user_id}", str(source_user_id)) + url = url.replace("{target_user_id}", str(target_user_id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: # Check if token needs refresh @@ -2137,67 +2103,48 @@ def mute_user( self.client.refresh_token() params = {} headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) # Make the request if self.client.oauth2_session: - response = self.client.oauth2_session.post( + response = self.client.oauth2_session.delete( url, params=params, headers=headers, - json=json_data, ) else: - response = self.client.session.post( + response = self.client.session.delete( url, params=params, headers=headers, - json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return MuteUserResponse.model_validate(response_data) + return UnmuteUserResponse.model_validate(response_data) - def get_bookmarks( + def get_pinned_lists( self, id: Any, - max_results: int = None, - pagination_token: Any = None, - tweet_fields: List = None, + list_fields: List = None, expansions: List = None, - media_fields: List = None, - poll_fields: List = None, user_fields: List = None, - place_fields: List = None, - ) -> GetBookmarksResponse: + ) -> GetPinnedListsResponse: """ - Get Bookmarks - Retrieves a list of Posts bookmarked by the authenticated user. + Get pinned Lists + Retrieves a list of Lists pinned by the authenticated user. Args: id: The ID of the authenticated source User for whom to return results. - max_results: The maximum number of results. - pagination_token: This parameter is used to get the next 'page' of results. - tweet_fields: A comma separated list of Tweet fields to display. + list_fields: A comma separated list of List fields to display. expansions: A comma separated list of fields to expand. - media_fields: A comma separated list of Media fields to display. - poll_fields: A comma separated list of Poll fields to display. user_fields: A comma separated list of User fields to display. - place_fields: A comma separated list of Place fields to display. Returns: - GetBookmarksResponse: Response data + GetPinnedListsResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/bookmarks" + url = self.client.base_url + "/2/users/{id}/pinned_lists" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -2205,22 +2152,12 @@ def get_bookmarks( if self.client.is_token_expired(): self.client.refresh_token() params = {} - if max_results is not None: - params["max_results"] = max_results - if pagination_token is not None: - params["pagination_token"] = pagination_token - if tweet_fields is not None: - params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + if list_fields is not None: + params["list.fields"] = ",".join(str(item) for item in list_fields) if expansions is not None: params["expansions"] = ",".join(str(item) for item in expansions) - if media_fields is not None: - params["media.fields"] = ",".join(str(item) for item in media_fields) - if poll_fields is not None: - params["poll.fields"] = ",".join(str(item) for item in poll_fields) if user_fields is not None: params["user.fields"] = ",".join(str(item) for item in user_fields) - if place_fields is not None: - params["place.fields"] = ",".join(str(item) for item in place_fields) headers = {} # Prepare request data json_data = None @@ -2242,22 +2179,20 @@ def get_bookmarks( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetBookmarksResponse.model_validate(response_data) + return GetPinnedListsResponse.model_validate(response_data) - def create_bookmark( - self, id: Any, body: CreateBookmarkRequest - ) -> CreateBookmarkResponse: + def pin_list(self, id: Any, body: PinListRequest) -> PinListResponse: """ - Create Bookmark - Adds a post to the authenticated user’s bookmarks. + Pin List + Causes the authenticated user to pin a specific List by its ID. Args: - id: The ID of the authenticated source User for whom to add bookmarks. + id: The ID of the authenticated source User that will pin the List. body: Request body Returns: - CreateBookmarkResponse: Response data + PinListResponse: Response data """ - url = self.client.base_url + "/2/users/{id}/bookmarks" + url = self.client.base_url + "/2/users/{id}/pinned_lists" url = url.replace("{id}", str(id)) # Ensure we have a valid access token if self.client.oauth2_auth and self.client.token: @@ -2295,4 +2230,69 @@ def create_bookmark( # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return CreateBookmarkResponse.model_validate(response_data) + return PinListResponse.model_validate(response_data) + + + def get_followers( + self, + id: Any, + max_results: int = None, + pagination_token: Any = None, + user_fields: List = None, + expansions: List = None, + tweet_fields: List = None, + ) -> GetFollowersResponse: + """ + Get followers + Retrieves a list of Users who follow a specific User by their ID. + Args: + id: The ID of the User to lookup. + max_results: The maximum number of results. + pagination_token: This parameter is used to get a specified 'page' of results. + user_fields: A comma separated list of User fields to display. + expansions: A comma separated list of fields to expand. + tweet_fields: A comma separated list of Tweet fields to display. + Returns: + GetFollowersResponse: Response data + """ + url = self.client.base_url + "/2/users/{id}/followers" + url = url.replace("{id}", str(id)) + if self.client.bearer_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.bearer_token}" + ) + elif self.client.access_token: + self.client.session.headers["Authorization"] = ( + f"Bearer {self.client.access_token}" + ) + # Ensure we have a valid access token + if self.client.oauth2_auth and self.client.token: + # Check if token needs refresh + if self.client.is_token_expired(): + self.client.refresh_token() + params = {} + if max_results is not None: + params["max_results"] = max_results + if pagination_token is not None: + params["pagination_token"] = pagination_token + if user_fields is not None: + params["user.fields"] = ",".join(str(item) for item in user_fields) + if expansions is not None: + params["expansions"] = ",".join(str(item) for item in expansions) + if tweet_fields is not None: + params["tweet.fields"] = ",".join(str(item) for item in tweet_fields) + headers = {} + # Prepare request data + json_data = None + # Make the request + response = self.client.session.get( + url, + params=params, + headers=headers, + ) + # Check for errors + response.raise_for_status() + # Parse the response data + response_data = response.json() + # Convert to Pydantic model if applicable + return GetFollowersResponse.model_validate(response_data) diff --git a/xdk/users/models.py b/xdk/users/models.py index 2b1835b..84abe4d 100644 --- a/xdk/users/models.py +++ b/xdk/users/models.py @@ -16,71 +16,62 @@ from datetime import datetime -# Models for get_by_ids +# Models for get_bookmarks -class GetByIdsResponse(BaseModel): - """Response model for get_by_ids""" +class GetBookmarksResponse(BaseModel): + """Response model for get_bookmarks""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for unfollow_user - - -class UnfollowUserResponse(BaseModel): - """Response model for unfollow_user""" +# Models for create_bookmark - model_config = ConfigDict(populate_by_name=True, extra="allow") +class CreateBookmarkRequest(BaseModel): + """Request model for create_bookmark""" -# Models for get_liked_posts + model_config = ConfigDict(populate_by_name=True) -class GetLikedPostsResponse(BaseModel): - """Response model for get_liked_posts""" +class CreateBookmarkResponse(BaseModel): + """Response model for create_bookmark""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for unfollow_list +# Models for get_mentions -class UnfollowListResponse(BaseModel): - """Response model for unfollow_list""" +class GetMentionsResponse(BaseModel): + """Response model for get_mentions""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_following +# Models for get_owned_lists -class GetFollowingResponse(BaseModel): - """Response model for get_following""" +class GetOwnedListsResponse(BaseModel): + """Response model for get_owned_lists""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for follow_user - - -class FollowUserRequest(BaseModel): - """Request model for follow_user""" - - model_config = ConfigDict(populate_by_name=True) +# Models for unpin_list -class FollowUserResponse(BaseModel): - """Response model for follow_user""" +class UnpinListResponse(BaseModel): + """Response model for unpin_list""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for unmute_user +# Models for block_dms -class UnmuteUserResponse(BaseModel): - """Response model for unmute_user""" +class BlockDmsResponse(BaseModel): + """Response model for block_dms""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -100,125 +91,113 @@ class RepostPostResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_blocking +# Models for get_following -class GetBlockingResponse(BaseModel): - """Response model for get_blocking""" +class GetFollowingResponse(BaseModel): + """Response model for get_following""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_followers - - -class GetFollowersResponse(BaseModel): - """Response model for get_followers""" +# Models for follow_user - model_config = ConfigDict(populate_by_name=True, extra="allow") +class FollowUserRequest(BaseModel): + """Request model for follow_user""" -# Models for get_mentions + model_config = ConfigDict(populate_by_name=True) -class GetMentionsResponse(BaseModel): - """Response model for get_mentions""" +class FollowUserResponse(BaseModel): + """Response model for follow_user""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_pinned_lists +# Models for unrepost_post -class GetPinnedListsResponse(BaseModel): - """Response model for get_pinned_lists""" +class UnrepostPostResponse(BaseModel): + """Response model for unrepost_post""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for pin_list - - -class PinListRequest(BaseModel): - """Request model for pin_list""" - - model_config = ConfigDict(populate_by_name=True) +# Models for get_by_usernames -class PinListResponse(BaseModel): - """Response model for pin_list""" +class GetByUsernamesResponse(BaseModel): + """Response model for get_by_usernames""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for unpin_list +# Models for get_liked_posts -class UnpinListResponse(BaseModel): - """Response model for unpin_list""" +class GetLikedPostsResponse(BaseModel): + """Response model for get_liked_posts""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for delete_bookmark +# Models for get_reposts_of_me -class DeleteBookmarkResponse(BaseModel): - """Response model for delete_bookmark""" +class GetRepostsOfMeResponse(BaseModel): + """Response model for get_reposts_of_me""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for block_dms +# Models for get_bookmark_folders -class BlockDmsResponse(BaseModel): - """Response model for block_dms""" +class GetBookmarkFoldersResponse(BaseModel): + """Response model for get_bookmark_folders""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for unlike_post +# Models for delete_bookmark -class UnlikePostResponse(BaseModel): - """Response model for unlike_post""" +class DeleteBookmarkResponse(BaseModel): + """Response model for delete_bookmark""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_bookmarks_by_folder_id - +# Models for like_post -class GetBookmarksByFolderIdResponse(BaseModel): - """Response model for get_bookmarks_by_folder_id""" - - model_config = ConfigDict(populate_by_name=True, extra="allow") +class LikePostRequest(BaseModel): + """Request model for like_post""" -# Models for get_bookmark_folders + model_config = ConfigDict(populate_by_name=True) -class GetBookmarkFoldersResponse(BaseModel): - """Response model for get_bookmark_folders""" +class LikePostResponse(BaseModel): + """Response model for like_post""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for unrepost_post +# Models for unblock_dms -class UnrepostPostResponse(BaseModel): - """Response model for unrepost_post""" +class UnblockDmsResponse(BaseModel): + """Response model for unblock_dms""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_reposts_of_me +# Models for unfollow_user -class GetRepostsOfMeResponse(BaseModel): - """Response model for get_reposts_of_me""" +class UnfollowUserResponse(BaseModel): + """Response model for unfollow_user""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -232,53 +211,62 @@ class GetListMembershipsResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_timeline +# Models for get_muting -class GetTimelineResponse(BaseModel): - """Response model for get_timeline""" +class GetMutingResponse(BaseModel): + """Response model for get_muting""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for like_post +# Models for mute_user -class LikePostRequest(BaseModel): - """Request model for like_post""" +class MuteUserRequest(BaseModel): + """Request model for mute_user""" model_config = ConfigDict(populate_by_name=True) -class LikePostResponse(BaseModel): - """Response model for like_post""" +class MuteUserResponse(BaseModel): + """Response model for mute_user""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_me +# Models for get_by_username -class GetMeResponse(BaseModel): - """Response model for get_me""" +class GetByUsernameResponse(BaseModel): + """Response model for get_by_username""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_by_usernames +# Models for get_by_id -class GetByUsernamesResponse(BaseModel): - """Response model for get_by_usernames""" +class GetByIdResponse(BaseModel): + """Response model for get_by_id""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_posts +# Models for get_by_ids -class GetPostsResponse(BaseModel): - """Response model for get_posts""" +class GetByIdsResponse(BaseModel): + """Response model for get_by_ids""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + +# Models for get_bookmarks_by_folder_id + + +class GetBookmarksByFolderIdResponse(BaseModel): + """Response model for get_bookmarks_by_folder_id""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -307,38 +295,38 @@ class FollowListResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_by_username +# Models for get_me -class GetByUsernameResponse(BaseModel): - """Response model for get_by_username""" +class GetMeResponse(BaseModel): + """Response model for get_me""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_owned_lists +# Models for unlike_post -class GetOwnedListsResponse(BaseModel): - """Response model for get_owned_lists""" +class UnlikePostResponse(BaseModel): + """Response model for unlike_post""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_by_id +# Models for get_posts -class GetByIdResponse(BaseModel): - """Response model for get_by_id""" +class GetPostsResponse(BaseModel): + """Response model for get_posts""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for unblock_dms +# Models for unfollow_list -class UnblockDmsResponse(BaseModel): - """Response model for unblock_dms""" +class UnfollowListResponse(BaseModel): + """Response model for unfollow_list""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -352,49 +340,61 @@ class SearchResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_muting +# Models for get_blocking -class GetMutingResponse(BaseModel): - """Response model for get_muting""" +class GetBlockingResponse(BaseModel): + """Response model for get_blocking""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for mute_user +# Models for get_timeline -class MuteUserRequest(BaseModel): - """Request model for mute_user""" +class GetTimelineResponse(BaseModel): + """Response model for get_timeline""" - model_config = ConfigDict(populate_by_name=True) + model_config = ConfigDict(populate_by_name=True, extra="allow") -class MuteUserResponse(BaseModel): - """Response model for mute_user""" +# Models for unmute_user + + +class UnmuteUserResponse(BaseModel): + """Response model for unmute_user""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_bookmarks +# Models for get_pinned_lists -class GetBookmarksResponse(BaseModel): - """Response model for get_bookmarks""" +class GetPinnedListsResponse(BaseModel): + """Response model for get_pinned_lists""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for create_bookmark +# Models for pin_list -class CreateBookmarkRequest(BaseModel): - """Request model for create_bookmark""" +class PinListRequest(BaseModel): + """Request model for pin_list""" model_config = ConfigDict(populate_by_name=True) -class CreateBookmarkResponse(BaseModel): - """Response model for create_bookmark""" +class PinListResponse(BaseModel): + """Response model for pin_list""" + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + +# Models for get_followers + + +class GetFollowersResponse(BaseModel): + """Response model for get_followers""" model_config = ConfigDict(populate_by_name=True, extra="allow") diff --git a/xdk/webhooks/client.py b/xdk/webhooks/client.py index c3242f6..db3a221 100644 --- a/xdk/webhooks/client.py +++ b/xdk/webhooks/client.py @@ -21,16 +21,16 @@ if TYPE_CHECKING: from ..client import Client from .models import ( - ValidateResponse, - DeleteResponse, - GetStreamLinksResponse, - CreateStreamLinkResponse, - DeleteStreamLinkResponse, - CreateWebhookReplayJobRequest, - CreateWebhookReplayJobResponse, GetResponse, CreateRequest, CreateResponse, + CreateWebhookReplayJobRequest, + CreateWebhookReplayJobResponse, + CreateStreamLinkResponse, + DeleteStreamLinkResponse, + GetStreamLinksResponse, + ValidateResponse, + DeleteResponse, ) @@ -42,17 +42,16 @@ def __init__(self, client: Client): self.client = client - def validate(self, webhook_id: Any) -> ValidateResponse: + def get(self, webhook_config_fields: List = None) -> GetResponse: """ - Validate webhook - Triggers a CRC check for a given webhook. + Get webhook + Get a list of webhook configs associated with a client app. Args: - webhook_id: The ID of the webhook to check. + webhook_config_fields: A comma separated list of WebhookConfig fields to display. Returns: - ValidateResponse: Response data + GetResponse: Response data """ - url = self.client.base_url + "/2/webhooks/{webhook_id}" - url = url.replace("{webhook_id}", str(webhook_id)) + url = self.client.base_url + "/2/webhooks" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -62,11 +61,15 @@ def validate(self, webhook_id: Any) -> ValidateResponse: f"Bearer {self.client.access_token}" ) params = {} + if webhook_config_fields is not None: + params["webhook_config.fields"] = ",".join( + str(item) for item in webhook_config_fields + ) headers = {} # Prepare request data json_data = None # Make the request - response = self.client.session.put( + response = self.client.session.get( url, params=params, headers=headers, @@ -76,20 +79,18 @@ def validate(self, webhook_id: Any) -> ValidateResponse: # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return ValidateResponse.model_validate(response_data) + return GetResponse.model_validate(response_data) - def delete(self, webhook_id: Any) -> DeleteResponse: + def create(self, body: Optional[CreateRequest] = None) -> CreateResponse: """ - Delete webhook - Deletes an existing webhook configuration. - Args: - webhook_id: The ID of the webhook to delete. - Returns: - DeleteResponse: Response data + Create webhook + Creates a new webhook configuration. + body: Request body + Returns: + CreateResponse: Response data """ - url = self.client.base_url + "/2/webhooks/{webhook_id}" - url = url.replace("{webhook_id}", str(webhook_id)) + url = self.client.base_url + "/2/webhooks" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -100,32 +101,41 @@ def delete(self, webhook_id: Any) -> DeleteResponse: ) params = {} headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request - response = self.client.session.delete( + response = self.client.session.post( url, params=params, headers=headers, + json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return DeleteResponse.model_validate(response_data) + return CreateResponse.model_validate(response_data) - def get_stream_links( - self, - ) -> GetStreamLinksResponse: + def create_webhook_replay_job( + self, body: Optional[CreateWebhookReplayJobRequest] = None + ) -> CreateWebhookReplayJobResponse: """ - Get stream links - Get a list of webhook links associated with a filtered stream ruleset. + Create replay job for webhook + Creates a replay job to retrieve events from up to the past 24 hours for all events delivered or attempted to be delivered to the webhook. + body: Request body Returns: - GetStreamLinksResponse: Response data + CreateWebhookReplayJobResponse: Response data """ - url = self.client.base_url + "/2/tweets/search/webhooks" + url = self.client.base_url + "/2/webhooks/replay" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -136,20 +146,28 @@ def get_stream_links( ) params = {} headers = {} + headers["Content-Type"] = "application/json" # Prepare request data json_data = None + if body is not None: + json_data = ( + body.model_dump(exclude_none=True) + if hasattr(body, "model_dump") + else body + ) # Make the request - response = self.client.session.get( + response = self.client.session.post( url, params=params, headers=headers, + json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetStreamLinksResponse.model_validate(response_data) + return CreateWebhookReplayJobResponse.model_validate(response_data) def create_stream_link( @@ -253,17 +271,16 @@ def delete_stream_link(self, webhook_id: Any) -> DeleteStreamLinkResponse: return DeleteStreamLinkResponse.model_validate(response_data) - def create_webhook_replay_job( - self, body: Optional[CreateWebhookReplayJobRequest] = None - ) -> CreateWebhookReplayJobResponse: + def get_stream_links( + self, + ) -> GetStreamLinksResponse: """ - Create replay job for webhook - Creates a replay job to retrieve events from up to the past 24 hours for all events delivered or attempted to be delivered to the webhook. - body: Request body + Get stream links + Get a list of webhook links associated with a filtered stream ruleset. Returns: - CreateWebhookReplayJobResponse: Response data + GetStreamLinksResponse: Response data """ - url = self.client.base_url + "/2/webhooks/replay" + url = self.client.base_url + "/2/tweets/search/webhooks" if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -274,40 +291,33 @@ def create_webhook_replay_job( ) params = {} headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) # Make the request - response = self.client.session.post( + response = self.client.session.get( url, params=params, headers=headers, - json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return CreateWebhookReplayJobResponse.model_validate(response_data) + return GetStreamLinksResponse.model_validate(response_data) - def get(self, webhook_config_fields: List = None) -> GetResponse: + def validate(self, webhook_id: Any) -> ValidateResponse: """ - Get webhook - Get a list of webhook configs associated with a client app. + Validate webhook + Triggers a CRC check for a given webhook. Args: - webhook_config_fields: A comma separated list of WebhookConfig fields to display. + webhook_id: The ID of the webhook to check. Returns: - GetResponse: Response data + ValidateResponse: Response data """ - url = self.client.base_url + "/2/webhooks" + url = self.client.base_url + "/2/webhooks/{webhook_id}" + url = url.replace("{webhook_id}", str(webhook_id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -317,15 +327,11 @@ def get(self, webhook_config_fields: List = None) -> GetResponse: f"Bearer {self.client.access_token}" ) params = {} - if webhook_config_fields is not None: - params["webhook_config.fields"] = ",".join( - str(item) for item in webhook_config_fields - ) headers = {} # Prepare request data json_data = None # Make the request - response = self.client.session.get( + response = self.client.session.put( url, params=params, headers=headers, @@ -335,18 +341,20 @@ def get(self, webhook_config_fields: List = None) -> GetResponse: # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return GetResponse.model_validate(response_data) + return ValidateResponse.model_validate(response_data) - def create(self, body: Optional[CreateRequest] = None) -> CreateResponse: + def delete(self, webhook_id: Any) -> DeleteResponse: """ - Create webhook - Creates a new webhook configuration. - body: Request body - Returns: - CreateResponse: Response data + Delete webhook + Deletes an existing webhook configuration. + Args: + webhook_id: The ID of the webhook to delete. + Returns: + DeleteResponse: Response data """ - url = self.client.base_url + "/2/webhooks" + url = self.client.base_url + "/2/webhooks/{webhook_id}" + url = url.replace("{webhook_id}", str(webhook_id)) if self.client.bearer_token: self.client.session.headers["Authorization"] = ( f"Bearer {self.client.bearer_token}" @@ -357,25 +365,17 @@ def create(self, body: Optional[CreateRequest] = None) -> CreateResponse: ) params = {} headers = {} - headers["Content-Type"] = "application/json" # Prepare request data json_data = None - if body is not None: - json_data = ( - body.model_dump(exclude_none=True) - if hasattr(body, "model_dump") - else body - ) # Make the request - response = self.client.session.post( + response = self.client.session.delete( url, params=params, headers=headers, - json=json_data, ) # Check for errors response.raise_for_status() # Parse the response data response_data = response.json() # Convert to Pydantic model if applicable - return CreateResponse.model_validate(response_data) + return DeleteResponse.model_validate(response_data) diff --git a/xdk/webhooks/models.py b/xdk/webhooks/models.py index d2fd860..0823959 100644 --- a/xdk/webhooks/models.py +++ b/xdk/webhooks/models.py @@ -16,29 +16,41 @@ from datetime import datetime -# Models for validate +# Models for get -class ValidateResponse(BaseModel): - """Response model for validate""" +class GetResponse(BaseModel): + """Response model for get""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for delete +# Models for create -class DeleteResponse(BaseModel): - """Response model for delete""" +class CreateRequest(BaseModel): + """Request model for create""" + + model_config = ConfigDict(populate_by_name=True) + + +class CreateResponse(BaseModel): + """Response model for create""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get_stream_links +# Models for create_webhook_replay_job -class GetStreamLinksResponse(BaseModel): - """Response model for get_stream_links""" +class CreateWebhookReplayJobRequest(BaseModel): + """Request model for create_webhook_replay_job""" + + model_config = ConfigDict(populate_by_name=True) + + +class CreateWebhookReplayJobResponse(BaseModel): + """Response model for create_webhook_replay_job""" model_config = ConfigDict(populate_by_name=True, extra="allow") @@ -61,40 +73,28 @@ class DeleteStreamLinkResponse(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for create_webhook_replay_job - - -class CreateWebhookReplayJobRequest(BaseModel): - """Request model for create_webhook_replay_job""" - - model_config = ConfigDict(populate_by_name=True) +# Models for get_stream_links -class CreateWebhookReplayJobResponse(BaseModel): - """Response model for create_webhook_replay_job""" +class GetStreamLinksResponse(BaseModel): + """Response model for get_stream_links""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for get +# Models for validate -class GetResponse(BaseModel): - """Response model for get""" +class ValidateResponse(BaseModel): + """Response model for validate""" model_config = ConfigDict(populate_by_name=True, extra="allow") -# Models for create - - -class CreateRequest(BaseModel): - """Request model for create""" - - model_config = ConfigDict(populate_by_name=True) +# Models for delete -class CreateResponse(BaseModel): - """Response model for create""" +class DeleteResponse(BaseModel): + """Response model for delete""" model_config = ConfigDict(populate_by_name=True, extra="allow")