7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
331 lines
10 KiB
Python
331 lines
10 KiB
Python
"""
|
|
Extended tests for news/flask_api.py
|
|
|
|
Tests cover:
|
|
- safe_error_message() helper
|
|
- get_user_id() helper
|
|
- Flask route handlers with mocking
|
|
- Error handling and response codes
|
|
- Request validation
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import patch
|
|
from flask import Flask
|
|
|
|
|
|
@pytest.fixture
|
|
def flask_app():
|
|
"""Create a Flask app for testing."""
|
|
app = Flask(__name__)
|
|
app.config["TESTING"] = True
|
|
return app
|
|
|
|
|
|
class TestSafeErrorMessage:
|
|
"""Tests for safe_error_message() helper."""
|
|
|
|
def test_returns_string(self):
|
|
"""Returns a string."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(Exception("test"))
|
|
|
|
assert isinstance(result, str)
|
|
|
|
def test_handles_value_error(self):
|
|
"""Returns 'Invalid input provided' for ValueError."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(ValueError("bad value"))
|
|
|
|
assert result == "Invalid input provided"
|
|
|
|
def test_handles_key_error(self):
|
|
"""Returns 'Required data missing' for KeyError."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(KeyError("missing_key"))
|
|
|
|
assert result == "Required data missing"
|
|
|
|
def test_handles_type_error(self):
|
|
"""Returns 'Invalid data format' for TypeError."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(TypeError("wrong type"))
|
|
|
|
assert result == "Invalid data format"
|
|
|
|
def test_handles_generic_exception(self):
|
|
"""Returns generic message for other exceptions."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(Exception("unknown error"))
|
|
|
|
assert "error occurred" in result.lower()
|
|
|
|
def test_includes_context_in_message(self):
|
|
"""Includes context in error message."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(Exception("error"), context="testing")
|
|
|
|
assert "testing" in result
|
|
|
|
def test_handles_none_exception(self):
|
|
"""Handles None-like exception gracefully."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(Exception(""))
|
|
|
|
assert isinstance(result, str)
|
|
|
|
def test_empty_context(self):
|
|
"""Handles empty context string."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(Exception("error"), context="")
|
|
|
|
assert isinstance(result, str)
|
|
|
|
|
|
class TestGetUserId:
|
|
"""Tests for get_user_id() helper."""
|
|
|
|
def test_function_exists(self):
|
|
"""get_user_id function exists."""
|
|
from local_deep_research.news.flask_api import get_user_id
|
|
|
|
assert callable(get_user_id)
|
|
|
|
def test_returns_none_when_no_current_user(self):
|
|
"""Returns None when current_user returns None."""
|
|
# Create app context for the import inside get_user_id
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
with patch(
|
|
"local_deep_research.web.auth.decorators.current_user"
|
|
) as mock_current:
|
|
mock_current.return_value = None
|
|
|
|
from local_deep_research.news.flask_api import get_user_id
|
|
|
|
result = get_user_id()
|
|
|
|
assert result is None
|
|
|
|
def test_returns_username_when_user_exists(self):
|
|
"""Returns username when current_user returns a username."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
with patch(
|
|
"local_deep_research.web.auth.decorators.current_user"
|
|
) as mock_current:
|
|
mock_current.return_value = "testuser"
|
|
|
|
from local_deep_research.news.flask_api import get_user_id
|
|
|
|
result = get_user_id()
|
|
|
|
assert result == "testuser"
|
|
|
|
|
|
class TestNewsBlueprintExists:
|
|
"""Tests for news_api_bp Blueprint."""
|
|
|
|
def test_blueprint_name(self):
|
|
"""Blueprint name is 'news_api'."""
|
|
from local_deep_research.news.flask_api import news_api_bp
|
|
|
|
assert news_api_bp.name == "news_api"
|
|
|
|
def test_blueprint_url_prefix(self):
|
|
"""Blueprint has /api url_prefix."""
|
|
from local_deep_research.news.flask_api import news_api_bp
|
|
|
|
assert news_api_bp.url_prefix == "/api"
|
|
|
|
|
|
class TestFeedRouteValidation:
|
|
"""Tests for /feed route validation."""
|
|
|
|
def test_feed_route_function_exists(self):
|
|
"""get_news_feed function exists."""
|
|
from local_deep_research.news.flask_api import get_news_feed
|
|
|
|
assert callable(get_news_feed)
|
|
|
|
|
|
class TestErrorHandlers:
|
|
"""Tests for error handler functions."""
|
|
|
|
def test_bad_request_handler_exists(self):
|
|
"""400 error handler is defined."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import bad_request
|
|
|
|
result = bad_request(Exception("test"))
|
|
|
|
assert result[1] == 400
|
|
|
|
def test_not_found_handler_exists(self):
|
|
"""404 error handler is defined."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import not_found
|
|
|
|
result = not_found(Exception("test"))
|
|
|
|
assert result[1] == 404
|
|
|
|
def test_internal_error_handler_exists(self):
|
|
"""500 error handler is defined."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import internal_error
|
|
|
|
result = internal_error(Exception("test"))
|
|
|
|
assert result[1] == 500
|
|
|
|
def test_bad_request_returns_json(self):
|
|
"""400 error handler returns JSON."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import bad_request
|
|
|
|
result = bad_request(Exception("test"))
|
|
|
|
# Result is tuple (response, status_code)
|
|
assert "error" in result[0].get_json()
|
|
|
|
def test_not_found_returns_json(self):
|
|
"""404 error handler returns JSON."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import not_found
|
|
|
|
result = not_found(Exception("test"))
|
|
|
|
assert "error" in result[0].get_json()
|
|
|
|
def test_internal_error_returns_json(self):
|
|
"""500 error handler returns JSON."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import internal_error
|
|
|
|
result = internal_error(Exception("test"))
|
|
|
|
assert "error" in result[0].get_json()
|
|
|
|
|
|
class TestSafeErrorMessageExtended:
|
|
"""Extended tests for safe_error_message() helper."""
|
|
|
|
def test_attribute_error_returns_generic(self):
|
|
"""Returns generic message for AttributeError."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(AttributeError("missing attr"))
|
|
|
|
assert "error occurred" in result.lower()
|
|
|
|
def test_index_error_returns_generic(self):
|
|
"""Returns generic message for IndexError."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(IndexError("list index out of range"))
|
|
|
|
assert "error occurred" in result.lower()
|
|
|
|
def test_runtime_error_returns_generic(self):
|
|
"""Returns generic message for RuntimeError."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(RuntimeError("runtime issue"))
|
|
|
|
assert "error occurred" in result.lower()
|
|
|
|
def test_context_with_special_characters(self):
|
|
"""Handles context with special characters."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(Exception("error"), context="test <>&\"'")
|
|
|
|
assert isinstance(result, str)
|
|
|
|
def test_long_context_string(self):
|
|
"""Handles long context string."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
long_context = "x" * 1000
|
|
result = safe_error_message(Exception("error"), context=long_context)
|
|
|
|
assert isinstance(result, str)
|
|
|
|
def test_unicode_context(self):
|
|
"""Handles unicode in context."""
|
|
from local_deep_research.news.flask_api import safe_error_message
|
|
|
|
result = safe_error_message(Exception("error"), context="日本語")
|
|
|
|
assert isinstance(result, str)
|
|
|
|
|
|
class TestBlueprintConfiguration:
|
|
"""Tests for Blueprint configuration."""
|
|
|
|
def test_blueprint_has_routes_registered(self):
|
|
"""Blueprint has routes registered."""
|
|
from local_deep_research.news.flask_api import news_api_bp
|
|
|
|
# Blueprint should have deferred functions
|
|
assert len(news_api_bp.deferred_functions) > 0
|
|
|
|
def test_blueprint_is_flask_blueprint(self):
|
|
"""Blueprint is a Flask Blueprint instance."""
|
|
from local_deep_research.news.flask_api import news_api_bp
|
|
from flask import Blueprint
|
|
|
|
assert isinstance(news_api_bp, Blueprint)
|
|
|
|
|
|
class TestErrorHandlerMessages:
|
|
"""Tests for error handler message content."""
|
|
|
|
def test_bad_request_message_content(self):
|
|
"""400 handler returns 'Bad request' message."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import bad_request
|
|
|
|
result = bad_request(Exception("test"))
|
|
json_data = result[0].get_json()
|
|
|
|
assert json_data["error"] == "Bad request"
|
|
|
|
def test_not_found_message_content(self):
|
|
"""404 handler returns 'Resource not found' message."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import not_found
|
|
|
|
result = not_found(Exception("test"))
|
|
json_data = result[0].get_json()
|
|
|
|
assert json_data["error"] == "Resource not found"
|
|
|
|
def test_internal_error_message_content(self):
|
|
"""500 handler returns 'Internal server error' message."""
|
|
app = Flask(__name__)
|
|
with app.app_context():
|
|
from local_deep_research.news.flask_api import internal_error
|
|
|
|
result = internal_error(Exception("test"))
|
|
json_data = result[0].get_json()
|
|
|
|
assert json_data["error"] == "Internal server error"
|