mirror of
https://github.com/apache/superset.git
synced 2026-04-12 04:37:49 +00:00
223 lines
7.1 KiB
Python
223 lines
7.1 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from typing import Any
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
from marshmallow import ValidationError
|
|
from pytest_mock import MockerFixture
|
|
|
|
from superset.commands.report.create import CreateReportScheduleCommand
|
|
from superset.commands.report.exceptions import (
|
|
DatabaseNotFoundValidationError,
|
|
ReportScheduleAlertRequiredDatabaseValidationError,
|
|
ReportScheduleInvalidError,
|
|
)
|
|
from superset.reports.models import ReportScheduleType
|
|
from superset.utils import json
|
|
|
|
|
|
def _make_dashboard(position: dict[str, Any]) -> MagicMock:
|
|
dashboard = MagicMock()
|
|
dashboard.position_json = json.dumps(position)
|
|
return dashboard
|
|
|
|
|
|
def test_validate_report_extra_null_extra() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {"extra": None}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 0
|
|
|
|
|
|
def test_validate_report_extra_null_dashboard() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {"extra": {"dashboard": {}}, "dashboard": None}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 0
|
|
|
|
|
|
def test_validate_report_extra_empty_active_tabs() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"extra": {"dashboard": {"activeTabs": []}},
|
|
"dashboard": _make_dashboard({"TAB-1": {}, "TAB-2": {}}),
|
|
}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 0
|
|
|
|
|
|
def test_validate_report_extra_valid_tabs() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"extra": {"dashboard": {"activeTabs": ["TAB-1"]}},
|
|
"dashboard": _make_dashboard({"TAB-1": {}, "TAB-2": {}}),
|
|
}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 0
|
|
|
|
|
|
def test_validate_report_extra_invalid_tabs() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"extra": {"dashboard": {"activeTabs": ["TAB-999"]}},
|
|
"dashboard": _make_dashboard({"TAB-1": {}}),
|
|
}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 1
|
|
assert exceptions[0].field_name == "extra"
|
|
|
|
|
|
def test_validate_report_extra_anchor_json_valid() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"extra": {"dashboard": {"anchor": '["TAB-1"]'}},
|
|
"dashboard": _make_dashboard({"TAB-1": {}}),
|
|
}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 0
|
|
|
|
|
|
def test_validate_report_extra_anchor_invalid_ids() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"extra": {"dashboard": {"anchor": '["TAB-999"]'}},
|
|
"dashboard": _make_dashboard({"TAB-1": {}}),
|
|
}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 1
|
|
assert exceptions[0].field_name == "extra"
|
|
|
|
|
|
def test_validate_report_extra_anchor_string_valid() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"extra": {"dashboard": {"anchor": "TAB-1"}},
|
|
"dashboard": _make_dashboard({"TAB-1": {}}),
|
|
}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 0
|
|
|
|
|
|
def test_validate_report_extra_anchor_string_invalid() -> None:
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"extra": {"dashboard": {"anchor": "TAB-999"}},
|
|
"dashboard": _make_dashboard({"TAB-1": {}}),
|
|
}
|
|
|
|
exceptions: list[ValidationError] = []
|
|
command._validate_report_extra(exceptions)
|
|
|
|
assert len(exceptions) == 1
|
|
assert exceptions[0].field_name == "extra"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Phase 1 gap closure: validate() — alert + database combos
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _stub_validate_deps(mocker: MockerFixture) -> None:
|
|
"""Stub out all DAO and base-class calls inside validate() so the test
|
|
can exercise a single validation branch in isolation."""
|
|
mocker.patch.object(CreateReportScheduleCommand, "_populate_recipients")
|
|
mocker.patch(
|
|
"superset.commands.report.create.ReportScheduleDAO.validate_update_uniqueness",
|
|
return_value=True,
|
|
)
|
|
mocker.patch.object(CreateReportScheduleCommand, "validate_report_frequency")
|
|
mocker.patch.object(CreateReportScheduleCommand, "validate_chart_dashboard")
|
|
mocker.patch.object(CreateReportScheduleCommand, "_validate_report_extra")
|
|
mocker.patch(
|
|
"superset.commands.report.create.ReportScheduleDAO"
|
|
".validate_unique_creation_method",
|
|
return_value=True,
|
|
)
|
|
mocker.patch.object(CreateReportScheduleCommand, "populate_owners", return_value=[])
|
|
|
|
|
|
def test_validate_alert_missing_database_key(mocker: MockerFixture) -> None:
|
|
"""Alert type without a 'database' key raises the required-database error."""
|
|
_stub_validate_deps(mocker)
|
|
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"type": ReportScheduleType.ALERT,
|
|
"name": "Test Alert",
|
|
"crontab": "* * * * *",
|
|
"creation_method": "alerts_reports",
|
|
}
|
|
|
|
with pytest.raises(ReportScheduleInvalidError) as exc:
|
|
command.validate()
|
|
|
|
assert any(
|
|
isinstance(e, ReportScheduleAlertRequiredDatabaseValidationError)
|
|
for e in exc.value._exceptions
|
|
)
|
|
|
|
|
|
def test_validate_alert_nonexistent_database(mocker: MockerFixture) -> None:
|
|
"""Alert type with a database ID that doesn't exist raises not-found."""
|
|
_stub_validate_deps(mocker)
|
|
mocker.patch(
|
|
"superset.commands.report.create.DatabaseDAO.find_by_id",
|
|
return_value=None,
|
|
)
|
|
|
|
command = CreateReportScheduleCommand({})
|
|
command._properties = {
|
|
"type": ReportScheduleType.ALERT,
|
|
"name": "Test Alert",
|
|
"crontab": "* * * * *",
|
|
"creation_method": "alerts_reports",
|
|
"database": 999,
|
|
}
|
|
|
|
with pytest.raises(ReportScheduleInvalidError) as exc:
|
|
command.validate()
|
|
|
|
assert any(
|
|
isinstance(e, DatabaseNotFoundValidationError) for e in exc.value._exceptions
|
|
)
|