Skip to content

Commit

Permalink
test: dashboard lists API and view endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
amitmiran137 committed Jan 26, 2021
1 parent 58b680b commit 8714b8b
Show file tree
Hide file tree
Showing 7 changed files with 843 additions and 0 deletions.
12 changes: 12 additions & 0 deletions tests/base_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,18 @@ def get_access_requests(self, username, ds_type, ds_id):
def logout(self):
self.client.get("/logout/", follow_redirects=True)

def grant_access_to_dashboard(self, dashboard, role_name="Public"):
role = security_manager.find_role(role_name)
dashboard.roles.append(role)
db.session.merge(dashboard)
db.session.commit()

def revoke_access_to_dashboard(self, dashboard, role_name="Public"):
role = security_manager.find_role(role_name)
dashboard.roles.remove(role)
db.session.merge(dashboard)
db.session.commit()

def grant_public_access_to_table(self, table):
public_role = security_manager.find_role("Public")
perms = db.session.query(ab_models.PermissionView).all()
Expand Down
26 changes: 26 additions & 0 deletions tests/dashboards/consts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 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.


DASHBOARDS_API_URL = "api/v1/dashboard/"
GET_DASHBOARDS_LIST_VIEW = "/dashboard/list/"

ADMIN_USERNAME = "admin"
ALPHA_USERNAME = "alpha"
GAMMA_USERNAME = "gamma"

DEFAULT_DASHBOARD_SLUG_TO_TEST = "births"
97 changes: 97 additions & 0 deletions tests/dashboards/dashboard_test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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.
import logging
import random
import string
from typing import Any, Dict, List, Optional, Tuple

from sqlalchemy import func

from superset import appbuilder, db
from superset.models.dashboard import Dashboard
from tests.dashboards.consts import DEFAULT_DASHBOARD_SLUG_TO_TEST

logger = logging.getLogger(__name__)

session = appbuilder.get_session


def get_mock_positions(dashboard: Dashboard) -> Dict[str, Any]:
positions = {"DASHBOARD_VERSION_KEY": "v2"}
for i, slc in enumerate(dashboard.slices):
id_ = "DASHBOARD_CHART_TYPE-{}".format(i)
position_data = {
"type": "CHART",
"id": id_,
"children": [],
"meta": {"width": 4, "height": 50, "chartId": slc.id},
}
positions[id_] = position_data
return positions


def build_save_dash_parts(
dashboard_slug: Optional[str] = None, dashboard_to_edit: Optional[Dashboard] = None
) -> Tuple[Dashboard, Dict, Dict]:
if not dashboard_to_edit:
dashboard_slug = (
dashboard_slug if dashboard_slug else DEFAULT_DASHBOARD_SLUG_TO_TEST
)
dashboard_to_edit = get_dashboard_by_slug(dashboard_slug)

data_before_change = {
"positions": dashboard_to_edit.position,
"dashboard_title": dashboard_to_edit.dashboard_title,
}
data_after_change = {
"css": "",
"expanded_slices": {},
"positions": get_mock_positions(dashboard_to_edit),
"dashboard_title": dashboard_to_edit.dashboard_title,
}
return dashboard_to_edit, data_before_change, data_after_change


def get_all_dashboards() -> List[Dashboard]:
return db.session.query(Dashboard).all()


def get_dashboard_by_slug(dashboard_slug: str) -> Dashboard:
return db.session.query(Dashboard).filter_by(slug=dashboard_slug).first()


def count_dashboards() -> int:
return db.session.query(func.count(Dashboard.id)).first()[0]


def random_title():
return f"title{random_str()}"


def random_slug():
return f"slug{random_str()}"


def get_random_string(length):
letters = string.ascii_lowercase
result_str = "".join(random.choice(letters) for i in range(length))
print("Random string of length", length, "is:", result_str)
return result_str


def random_str():
return get_random_string(8)
Empty file.
90 changes: 90 additions & 0 deletions tests/dashboards/security/base_case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# 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 List, Optional

from flask import Response

from superset import app
from superset.models.dashboard import Dashboard
from tests.base_tests import SupersetTestCase
from tests.dashboards.consts import DASHBOARDS_API_URL, GET_DASHBOARDS_LIST_VIEW
from tests.dashboards.superset_factory_util import delete_all_inserted_objects

DASHBOARD_COUNT_IN_DASHBOARDS_LIST_VIEW_FORMAT = "Record Count:</strong> {count}"


class BaseTestDashboardSecurity(SupersetTestCase):
def tearDown(self) -> None:
self.clean_created_objects()

def assert_dashboards_list_view_response(
self,
response: Response,
expected_counts: int,
expected_dashboards: Optional[List[Dashboard]] = None,
not_expected_dashboards: Optional[List[Dashboard]] = None,
) -> None:
self.assert200(response)
response_html = response.data.decode("utf-8")
if expected_counts == 0:
assert "No records found" in response_html
else:
assert (
DASHBOARD_COUNT_IN_DASHBOARDS_LIST_VIEW_FORMAT.format(
count=str(expected_counts)
)
in response_html
)
expected_dashboards = expected_dashboards or []
for dashboard in expected_dashboards:
assert dashboard.url in response_html
not_expected_dashboards = not_expected_dashboards or []
for dashboard in not_expected_dashboards:
assert dashboard.url not in response_html

def assert_dashboards_api_response(
self,
response: Response,
expected_counts: int,
expected_dashboards: Optional[List[Dashboard]] = None,
not_expected_dashboards: Optional[List[Dashboard]] = None,
) -> None:
self.assert200(response)
response_data = response.json
self.assertEqual(response_data["count"], expected_counts)
response_dashboards_url = set(
map(lambda dash: dash["url"], response_data["result"])
)
expected_dashboards = expected_dashboards or []
for dashboard in expected_dashboards:
self.assertIn(dashboard.url, response_dashboards_url)
not_expected_dashboards = not_expected_dashboards or []
for dashboard in not_expected_dashboards:
self.assertNotIn(dashboard.url, response_dashboards_url)

def get_dashboards_list_response(self) -> Response:
return self.client.get(GET_DASHBOARDS_LIST_VIEW)

def get_dashboards_api_response(self) -> Response:
return self.client.get(DASHBOARDS_API_URL)

def clean_created_objects(self):
with app.test_request_context():
self.logout()
self.login("admin")
delete_all_inserted_objects()
self.logout()
Loading

0 comments on commit 8714b8b

Please sign in to comment.