feat: add extract_errors to Postgres (#13997)

* feat: add extract_errors to Postgres

* Add unit tests

* Fix lint

* Fix unit tests
This commit is contained in:
Beto Dealmeida
2021-04-08 13:24:54 -07:00
committed by GitHub
parent 784d29b57c
commit c60a93db9c
11 changed files with 248 additions and 228 deletions

View File

@@ -17,6 +17,7 @@
# isort:skip_file
# pylint: disable=invalid-name, no-self-use, too-many-public-methods, too-many-arguments
"""Unit tests for Superset"""
import dataclasses
import json
from io import BytesIO
from unittest import mock
@@ -27,10 +28,12 @@ import pytest
import yaml
from sqlalchemy.engine.url import make_url
from sqlalchemy.exc import DBAPIError
from sqlalchemy.sql import func
from superset import db, security_manager
from superset.connectors.sqla.models import SqlaTable
from superset.errors import SupersetError
from superset.models.core import Database
from superset.models.reports import ReportSchedule, ReportScheduleType
from superset.utils.core import get_example_database, get_main_database
@@ -895,16 +898,44 @@ class TestDatabaseApi(SupersetTestCase):
app.config["PREVENT_UNSAFE_DB_CONNECTIONS"] = False
@mock.patch("superset.databases.commands.test_connection.is_hostname_valid",)
def test_test_connection_failed_invalid_hostname(self, mock_is_hostname_valid):
@mock.patch(
"superset.databases.commands.test_connection.DatabaseDAO.build_db_for_connection_test",
)
@mock.patch("superset.databases.commands.test_connection.event_logger",)
def test_test_connection_failed_invalid_hostname(
self, mock_event_logger, mock_build_db
):
"""
Database API: Test test connection failed due to invalid hostname
"""
mock_is_hostname_valid.return_value = False
msg = 'psql: error: could not translate host name "locahost" to address: nodename nor servname provided, or not known'
mock_build_db.return_value.set_sqlalchemy_uri.side_effect = DBAPIError(
msg, None, None
)
mock_build_db.return_value.db_engine_spec.__name__ = "Some name"
superset_error = SupersetError(
message='Unable to resolve hostname "locahost".',
error_type="TEST_CONNECTION_INVALID_HOSTNAME_ERROR",
level="error",
extra={
"hostname": "locahost",
"issue_codes": [
{
"code": 1007,
"message": (
"Issue 1007 - The hostname provided can't be resolved."
),
}
],
},
)
mock_build_db.return_value.db_engine_spec.extract_errors.return_value = [
superset_error
]
self.login("admin")
data = {
"sqlalchemy_uri": "postgres://username:password@invalidhostname:12345/db",
"sqlalchemy_uri": "postgres://username:password@locahost:12345/db",
"database_name": "examples",
"impersonate_user": False,
"server_cert": None,
@@ -912,121 +943,10 @@ class TestDatabaseApi(SupersetTestCase):
url = "api/v1/database/test_connection"
rv = self.post_assert_metric(url, data, "test_connection")
assert rv.status_code == 400
assert rv.status_code == 422
assert rv.headers["Content-Type"] == "application/json; charset=utf-8"
response = json.loads(rv.data.decode("utf-8"))
expected_response = {
"errors": [
{
"message": 'Unable to resolve hostname "invalidhostname".',
"error_type": "TEST_CONNECTION_INVALID_HOSTNAME_ERROR",
"level": "error",
"extra": {
"hostname": "invalidhostname",
"issue_codes": [
{
"code": 1007,
"message": "Issue 1007 - The hostname provided can't be resolved.",
}
],
},
}
]
}
assert response == expected_response
@mock.patch("superset.databases.commands.test_connection.is_hostname_valid")
@mock.patch("superset.databases.commands.test_connection.is_port_open")
@mock.patch("superset.databases.commands.test_connection.is_host_up")
def test_test_connection_failed_closed_port(
self, mock_is_host_up, mock_is_port_open, mock_is_hostname_valid
):
"""
Database API: Test test connection failed due to closed port.
"""
mock_is_hostname_valid.return_value = True
mock_is_port_open.return_value = False
mock_is_host_up.return_value = True
self.login("admin")
data = {
"sqlalchemy_uri": "postgres://username:password@localhost:12345/db",
"database_name": "examples",
"impersonate_user": False,
"server_cert": None,
}
url = "api/v1/database/test_connection"
rv = self.post_assert_metric(url, data, "test_connection")
assert rv.status_code == 400
assert rv.headers["Content-Type"] == "application/json; charset=utf-8"
response = json.loads(rv.data.decode("utf-8"))
expected_response = {
"errors": [
{
"message": "The host localhost is up, but the port 12345 is closed.",
"error_type": "TEST_CONNECTION_PORT_CLOSED_ERROR",
"level": "error",
"extra": {
"hostname": "localhost",
"port": 12345,
"issue_codes": [
{
"code": 1008,
"message": "Issue 1008 - The port is closed.",
}
],
},
}
]
}
assert response == expected_response
@mock.patch("superset.databases.commands.test_connection.is_hostname_valid")
@mock.patch("superset.databases.commands.test_connection.is_port_open")
@mock.patch("superset.databases.commands.test_connection.is_host_up")
def test_test_connection_failed_host_down(
self, mock_is_host_up, mock_is_port_open, mock_is_hostname_valid
):
"""
Database API: Test test connection failed due to host being down.
"""
mock_is_hostname_valid.return_value = True
mock_is_port_open.return_value = False
mock_is_host_up.return_value = False
self.login("admin")
data = {
"sqlalchemy_uri": "postgres://username:password@localhost:12345/db",
"database_name": "examples",
"impersonate_user": False,
"server_cert": None,
}
url = "api/v1/database/test_connection"
rv = self.post_assert_metric(url, data, "test_connection")
assert rv.status_code == 400
assert rv.headers["Content-Type"] == "application/json; charset=utf-8"
response = json.loads(rv.data.decode("utf-8"))
expected_response = {
"errors": [
{
"message": "The host localhost might be down, ond can't be reached on port 12345.",
"error_type": "TEST_CONNECTION_HOST_DOWN_ERROR",
"level": "error",
"extra": {
"hostname": "localhost",
"port": 12345,
"issue_codes": [
{
"code": 1009,
"message": "Issue 1009 - The host might be down, and can't be reached on the provided port.",
}
],
},
}
]
}
expected_response = {"errors": [dataclasses.asdict(superset_error)]}
assert response == expected_response
@pytest.mark.usefixtures(