mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-25 11:17:50 +01:00
597 lines
24 KiB
Python
597 lines
24 KiB
Python
import json
|
|
from datetime import timedelta
|
|
from typing import cast, Optional
|
|
from django.test import override_settings
|
|
from django.utils import timezone
|
|
from freezegun import freeze_time
|
|
from rest_framework import status
|
|
|
|
from ee.api.test.base import APILicensedTest
|
|
from ee.models import ExplicitTeamMembership, DashboardPrivilege
|
|
from posthog.api.test.dashboards import DashboardAPI
|
|
from posthog.models import (
|
|
Dashboard,
|
|
DashboardTile,
|
|
Insight,
|
|
OrganizationMembership,
|
|
User,
|
|
)
|
|
from posthog.test.base import FuzzyInt, snapshot_postgres_queries
|
|
from posthog.test.db_context_capturing import capture_db_queries
|
|
|
|
|
|
class TestInsightEnterpriseAPI(APILicensedTest):
|
|
def setUp(self) -> None:
|
|
super().setUp()
|
|
self.dashboard_api = DashboardAPI(self.client, self.team, self.assertEqual)
|
|
|
|
def test_insight_trends_forbidden_if_project_private_and_org_member(self) -> None:
|
|
self.organization_membership.level = OrganizationMembership.Level.MEMBER
|
|
self.organization_membership.save()
|
|
self.team.access_control = True
|
|
self.team.save()
|
|
response = self.client.get(
|
|
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}"
|
|
)
|
|
self.assertDictEqual(
|
|
self.permission_denied_response("You don't have access to the project."),
|
|
response.json(),
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
@freeze_time("2012-01-14T03:21:34.000Z")
|
|
def test_can_add_and_remove_tags(self) -> None:
|
|
insight_id, response_data = self.dashboard_api.create_insight(
|
|
{
|
|
"name": "a created dashboard",
|
|
"filters": {
|
|
"events": [{"id": "$pageview"}],
|
|
"properties": [{"key": "$browser", "value": "Mac OS X"}],
|
|
"date_from": "-90d",
|
|
},
|
|
}
|
|
)
|
|
insight_short_id = response_data["short_id"]
|
|
self.assertEqual(response_data["tags"], [])
|
|
|
|
add_tags_response = self.client.patch(
|
|
# tags are displayed in order of insertion
|
|
f"/api/projects/{self.team.id}/insights/{insight_id}",
|
|
{"tags": ["2", "1", "3"]},
|
|
)
|
|
|
|
self.assertEqual(sorted(add_tags_response.json()["tags"]), ["1", "2", "3"])
|
|
|
|
remove_tags_response = self.client.patch(f"/api/projects/{self.team.id}/insights/{insight_id}", {"tags": ["3"]})
|
|
|
|
self.assertEqual(remove_tags_response.json()["tags"], ["3"])
|
|
|
|
self.assert_insight_activity(
|
|
insight_id=insight_id,
|
|
expected=[
|
|
{
|
|
"user": {"first_name": "", "email": "user1@posthog.com"},
|
|
"activity": "created",
|
|
"scope": "Insight",
|
|
"item_id": str(insight_id),
|
|
"detail": {
|
|
"changes": None,
|
|
"trigger": None,
|
|
"type": None,
|
|
"name": "a created dashboard",
|
|
"short_id": insight_short_id,
|
|
},
|
|
"created_at": "2012-01-14T03:21:34Z",
|
|
},
|
|
{
|
|
"user": {"first_name": "", "email": "user1@posthog.com"},
|
|
"activity": "updated",
|
|
"scope": "Insight",
|
|
"item_id": str(insight_id),
|
|
"detail": {
|
|
"changes": [
|
|
{
|
|
"type": "Insight",
|
|
"action": "changed",
|
|
"field": "tags",
|
|
"before": [],
|
|
"after": ["1", "2", "3"],
|
|
}
|
|
],
|
|
"trigger": None,
|
|
"type": None,
|
|
"name": "a created dashboard",
|
|
"short_id": insight_short_id,
|
|
},
|
|
"created_at": "2012-01-14T03:21:34Z",
|
|
},
|
|
{
|
|
"user": {"first_name": "", "email": "user1@posthog.com"},
|
|
"activity": "updated",
|
|
"scope": "Insight",
|
|
"item_id": str(insight_id),
|
|
"detail": {
|
|
"changes": [
|
|
{
|
|
"type": "Insight",
|
|
"action": "changed",
|
|
"field": "tags",
|
|
"before": ["1", "2", "3"],
|
|
"after": ["3"],
|
|
}
|
|
],
|
|
"trigger": None,
|
|
"type": None,
|
|
"name": "a created dashboard",
|
|
"short_id": insight_short_id,
|
|
},
|
|
"created_at": "2012-01-14T03:21:34Z",
|
|
},
|
|
],
|
|
)
|
|
|
|
def test_update_insight_can_include_tags_when_licensed(self) -> None:
|
|
with freeze_time("2012-01-14T03:21:34.000Z") as frozen_time:
|
|
insight_id, insight = self.dashboard_api.create_insight({"name": "insight name"})
|
|
short_id = insight["short_id"]
|
|
|
|
frozen_time.tick(delta=timedelta(minutes=10))
|
|
|
|
response = self.client.patch(
|
|
f"/api/projects/{self.team.id}/insights/{insight_id}",
|
|
{"name": "insight new name", "tags": ["add", "these", "tags"]},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
response_data = response.json()
|
|
self.assertEqual(response_data["name"], "insight new name")
|
|
self.assertEqual(sorted(response_data["tags"]), sorted(["add", "these", "tags"]))
|
|
self.assertEqual(response_data["created_by"]["distinct_id"], self.user.distinct_id)
|
|
self.assertEqual(
|
|
response_data["effective_restriction_level"],
|
|
Dashboard.RestrictionLevel.EVERYONE_IN_PROJECT_CAN_EDIT,
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_privilege_level"],
|
|
Dashboard.PrivilegeLevel.CAN_EDIT,
|
|
)
|
|
|
|
response = self.client.get(f"/api/projects/{self.team.id}/insights/{insight_id}")
|
|
|
|
self.assertEqual(response.json()["name"], "insight new name")
|
|
|
|
self.assert_insight_activity(
|
|
insight_id,
|
|
[
|
|
{
|
|
"user": {"first_name": "", "email": "user1@posthog.com"},
|
|
"activity": "updated",
|
|
"scope": "Insight",
|
|
"item_id": str(insight_id),
|
|
"detail": {
|
|
"changes": [
|
|
{
|
|
"type": "Insight",
|
|
"action": "changed",
|
|
"field": "tags",
|
|
"before": [],
|
|
"after": ["add", "tags", "these"],
|
|
},
|
|
{
|
|
"type": "Insight",
|
|
"action": "changed",
|
|
"field": "name",
|
|
"before": "insight name",
|
|
"after": "insight new name",
|
|
},
|
|
],
|
|
"trigger": None,
|
|
"type": None,
|
|
"name": "insight new name",
|
|
"short_id": short_id,
|
|
},
|
|
"created_at": "2012-01-14T03:31:34Z",
|
|
},
|
|
{
|
|
"user": {"first_name": "", "email": "user1@posthog.com"},
|
|
"activity": "created",
|
|
"scope": "Insight",
|
|
"item_id": str(insight_id),
|
|
"detail": {
|
|
"changes": None,
|
|
"trigger": None,
|
|
"type": None,
|
|
"name": "insight name",
|
|
"short_id": short_id,
|
|
},
|
|
"created_at": "2012-01-14T03:21:34Z",
|
|
},
|
|
],
|
|
)
|
|
|
|
def test_non_admin_user_with_privilege_can_add_an_insight_to_a_restricted_dashboard(
|
|
self,
|
|
) -> None:
|
|
# create insight and dashboard separately with default user
|
|
dashboard_restricted: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
restriction_level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
|
|
insight_id, response_data = self.dashboard_api.create_insight(data={"name": "starts un-restricted dashboard"})
|
|
|
|
user_with_permissions = User.objects.create_and_join(
|
|
organization=self.organization,
|
|
email="with_access_user@posthog.com",
|
|
password=None,
|
|
)
|
|
|
|
DashboardPrivilege.objects.create(
|
|
dashboard=dashboard_restricted,
|
|
user=user_with_permissions,
|
|
level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
|
|
self.client.force_login(user_with_permissions)
|
|
|
|
response = self.client.patch(
|
|
f"/api/projects/{self.team.id}/insights/{insight_id}",
|
|
{"dashboards": [dashboard_restricted.id]},
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
def test_an_insight_on_both_restricted_dashboard_does_not_restrict_with_explicit_privilege(
|
|
self,
|
|
) -> None:
|
|
dashboard_restricted: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
restriction_level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
|
|
DashboardPrivilege.objects.create(
|
|
dashboard=dashboard_restricted,
|
|
user=self.user,
|
|
level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
|
|
_, response_data = self.dashboard_api.create_insight(
|
|
data={
|
|
"name": "on a restricted and unrestricted dashboard",
|
|
"dashboards": [dashboard_restricted.pk],
|
|
}
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_restriction_level"],
|
|
Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_privilege_level"],
|
|
Dashboard.PrivilegeLevel.CAN_EDIT,
|
|
)
|
|
|
|
def test_insight_trends_allowed_if_project_private_and_org_member_and_project_member(
|
|
self,
|
|
) -> None:
|
|
self.organization_membership.level = OrganizationMembership.Level.MEMBER
|
|
self.organization_membership.save()
|
|
self.team.access_control = True
|
|
self.team.save()
|
|
ExplicitTeamMembership.objects.create(
|
|
team=self.team,
|
|
parent_membership=self.organization_membership,
|
|
level=ExplicitTeamMembership.Level.MEMBER,
|
|
)
|
|
response = self.client.get(
|
|
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}"
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
def test_cannot_update_restricted_insight_as_other_user_who_is_project_member(self):
|
|
creator = User.objects.create_and_join(self.organization, "y@x.com", None)
|
|
self.organization_membership.level = OrganizationMembership.Level.MEMBER
|
|
self.organization_membership.save()
|
|
original_name = "Edit-restricted dashboard"
|
|
dashboard: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
name=original_name,
|
|
created_by=creator,
|
|
restriction_level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
insight: Insight = Insight.objects.create(team=self.team, name="XYZ", created_by=self.user)
|
|
DashboardTile.objects.create(dashboard=dashboard, insight=insight)
|
|
|
|
response = self.client.patch(f"/api/projects/{self.team.id}/insights/{insight.id}", {"name": "ABC"})
|
|
response_data = response.json()
|
|
dashboard.refresh_from_db()
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
self.assertEqual(
|
|
response_data,
|
|
self.permission_denied_response(
|
|
"This insight is on a dashboard that can only be edited by its owner, team members invited to editing the dashboard, and project admins."
|
|
),
|
|
)
|
|
self.assertEqual(dashboard.name, original_name)
|
|
|
|
def test_event_definition_no_duplicate_tags(self):
|
|
from ee.models.license import License, LicenseManager
|
|
|
|
super(LicenseManager, cast(LicenseManager, License.objects)).create(
|
|
key="key_123",
|
|
plan="enterprise",
|
|
valid_until=timezone.datetime(2038, 1, 19, 3, 14, 7),
|
|
)
|
|
dashboard = Dashboard.objects.create(team=self.team, name="Edit-restricted dashboard")
|
|
insight = Insight.objects.create(team=self.team, name="XYZ", created_by=self.user)
|
|
DashboardTile.objects.create(dashboard=dashboard, insight=insight)
|
|
|
|
response = self.client.patch(
|
|
f"/api/projects/{self.team.id}/insights/{insight.id}",
|
|
{"tags": ["a", "b", "a"]},
|
|
)
|
|
|
|
self.assertListEqual(sorted(response.json()["tags"]), ["a", "b"])
|
|
|
|
def test_searching_insights_includes_tags_and_description(self) -> None:
|
|
insight_one_id, _ = self.dashboard_api.create_insight(
|
|
{
|
|
"name": "needle in a haystack",
|
|
"filters": {"events": [{"id": "$pageview"}]},
|
|
}
|
|
)
|
|
insight_two_id, _ = self.dashboard_api.create_insight(
|
|
{"name": "not matching", "filters": {"events": [{"id": "$pageview"}]}}
|
|
)
|
|
|
|
insight_three_id, _ = self.dashboard_api.create_insight(
|
|
{
|
|
"name": "not matching name",
|
|
"filters": {"events": [{"id": "$pageview"}]},
|
|
"tags": ["needle"],
|
|
}
|
|
)
|
|
|
|
insight_four_id, _ = self.dashboard_api.create_insight(
|
|
{
|
|
"name": "not matching name",
|
|
"description": "another needle",
|
|
"filters": {"events": [{"id": "$pageview"}]},
|
|
"tags": ["not matching"],
|
|
}
|
|
)
|
|
|
|
matching = self.client.get(f"/api/projects/{self.team.id}/insights/?search=needle")
|
|
self.assertEqual(matching.status_code, status.HTTP_200_OK)
|
|
matched_insights = [insight["id"] for insight in matching.json()["results"]]
|
|
assert sorted(matched_insights) == [
|
|
insight_one_id,
|
|
insight_three_id,
|
|
insight_four_id,
|
|
]
|
|
|
|
def test_cannot_update_an_insight_if_on_restricted_dashboard(self) -> None:
|
|
dashboard_restricted: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
restriction_level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
|
|
insight_id, response_data = self.dashboard_api.create_insight(
|
|
data={
|
|
"name": "on a restricted and unrestricted dashboard",
|
|
"dashboards": [dashboard_restricted.pk],
|
|
}
|
|
)
|
|
assert [t["dashboard_id"] for t in response_data["dashboard_tiles"]] == [dashboard_restricted.pk]
|
|
|
|
response = self.client.patch(
|
|
f"/api/projects/{self.team.id}/insights/{insight_id}",
|
|
{"name": "changing when restricted"},
|
|
)
|
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
|
|
def test_non_admin_user_cannot_add_an_insight_to_a_restricted_dashboard(
|
|
self,
|
|
) -> None:
|
|
# create insight and dashboard separately with default user
|
|
dashboard_restricted_id, _ = self.dashboard_api.create_dashboard(
|
|
{"restriction_level": Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT}
|
|
)
|
|
|
|
insight_id, response_data = self.dashboard_api.create_insight(data={"name": "starts un-restricted dashboard"})
|
|
|
|
# user with no permissions on the dashboard cannot add insight to it
|
|
user_without_permissions = User.objects.create_and_join(
|
|
organization=self.organization,
|
|
email="no_access_user@posthog.com",
|
|
password=None,
|
|
)
|
|
self.client.force_login(user_without_permissions)
|
|
|
|
response = self.client.patch(
|
|
f"/api/projects/{self.team.id}/insights/{insight_id}",
|
|
{"dashboards": [dashboard_restricted_id]},
|
|
)
|
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
|
|
|
self.client.force_login(self.user)
|
|
|
|
response = self.client.patch(
|
|
f"/api/projects/{self.team.id}/insights/{insight_id}",
|
|
{"dashboards": [dashboard_restricted_id]},
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
def test_admin_user_can_add_an_insight_to_a_restricted_dashboard(self) -> None:
|
|
# create insight and dashboard separately with default user
|
|
dashboard_restricted: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
restriction_level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
|
|
insight_id, response_data = self.dashboard_api.create_insight(data={"name": "starts un-restricted dashboard"})
|
|
|
|
# an admin user has implicit permissions on the dashboard and can add the insight to it
|
|
admin = User.objects.create_and_join(
|
|
organization=self.organization,
|
|
email="team2@posthog.com",
|
|
password=None,
|
|
level=OrganizationMembership.Level.ADMIN,
|
|
)
|
|
self.client.force_login(admin)
|
|
|
|
response = self.client.patch(
|
|
f"/api/projects/{self.team.id}/insights/{insight_id}",
|
|
{"dashboards": [dashboard_restricted.id]},
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
def test_an_insight_on_no_dashboard_has_no_restrictions(self) -> None:
|
|
_, response_data = self.dashboard_api.create_insight(data={"name": "not on a dashboard"})
|
|
self.assertEqual(
|
|
response_data["effective_restriction_level"],
|
|
Dashboard.RestrictionLevel.EVERYONE_IN_PROJECT_CAN_EDIT,
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_privilege_level"],
|
|
Dashboard.PrivilegeLevel.CAN_EDIT,
|
|
)
|
|
|
|
def test_an_insight_on_unrestricted_dashboard_has_no_restrictions(self) -> None:
|
|
dashboard: Dashboard = Dashboard.objects.create(team=self.team)
|
|
_, response_data = self.dashboard_api.create_insight(
|
|
data={"name": "on an unrestricted dashboard", "dashboards": [dashboard.pk]}
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_restriction_level"],
|
|
Dashboard.RestrictionLevel.EVERYONE_IN_PROJECT_CAN_EDIT,
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_privilege_level"],
|
|
Dashboard.PrivilegeLevel.CAN_EDIT,
|
|
)
|
|
|
|
def test_an_insight_on_restricted_dashboard_has_restrictions_cannot_edit_without_explicit_privilege(
|
|
self,
|
|
) -> None:
|
|
dashboard: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
restriction_level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
_, response_data = self.dashboard_api.create_insight(
|
|
data={"name": "on a restricted dashboard", "dashboards": [dashboard.pk]}
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_restriction_level"],
|
|
Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_privilege_level"],
|
|
Dashboard.PrivilegeLevel.CAN_VIEW,
|
|
)
|
|
|
|
def test_an_insight_on_both_restricted_and_unrestricted_dashboard_has_no_restrictions(
|
|
self,
|
|
) -> None:
|
|
dashboard_restricted: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
restriction_level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
dashboard_unrestricted: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
restriction_level=Dashboard.RestrictionLevel.EVERYONE_IN_PROJECT_CAN_EDIT,
|
|
)
|
|
_, response_data = self.dashboard_api.create_insight(
|
|
data={
|
|
"name": "on a restricted and unrestricted dashboard",
|
|
"dashboards": [dashboard_restricted.pk, dashboard_unrestricted.pk],
|
|
}
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_restriction_level"],
|
|
Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_privilege_level"],
|
|
Dashboard.PrivilegeLevel.CAN_EDIT,
|
|
)
|
|
|
|
def test_an_insight_on_restricted_dashboard_does_not_restrict_admin(self) -> None:
|
|
dashboard_restricted: Dashboard = Dashboard.objects.create(
|
|
team=self.team,
|
|
restriction_level=Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
|
|
admin = User.objects.create_and_join(
|
|
organization=self.organization,
|
|
email="y@x.com",
|
|
password=None,
|
|
level=OrganizationMembership.Level.ADMIN,
|
|
)
|
|
self.client.force_login(admin)
|
|
_, response_data = self.dashboard_api.create_insight(
|
|
data={
|
|
"name": "on a restricted and unrestricted dashboard",
|
|
"dashboards": [dashboard_restricted.pk],
|
|
}
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_restriction_level"],
|
|
Dashboard.RestrictionLevel.ONLY_COLLABORATORS_CAN_EDIT,
|
|
)
|
|
self.assertEqual(
|
|
response_data["effective_privilege_level"],
|
|
Dashboard.PrivilegeLevel.CAN_EDIT,
|
|
)
|
|
|
|
# :KLUDGE: avoid making extra queries that are explicitly not cached in tests. Avoids false N+1-s.
|
|
@override_settings(PERSON_ON_EVENTS_OVERRIDE=False, PERSON_ON_EVENTS_V2_OVERRIDE=False)
|
|
@snapshot_postgres_queries
|
|
def test_listing_insights_does_not_nplus1(self) -> None:
|
|
query_counts: list[int] = []
|
|
queries = []
|
|
|
|
for i in range(5):
|
|
user = User.objects.create(email=f"testuser{i}@posthog.com")
|
|
OrganizationMembership.objects.create(user=user, organization=self.organization)
|
|
dashboard = Dashboard.objects.create(name=f"Dashboard {i}", team=self.team)
|
|
|
|
self.dashboard_api.create_insight(
|
|
data={
|
|
"short_id": f"insight{i}",
|
|
"dashboards": [dashboard.pk],
|
|
"filters": {"events": [{"id": "$pageview"}]},
|
|
}
|
|
)
|
|
|
|
self.assertEqual(Insight.objects.count(), i + 1)
|
|
|
|
with capture_db_queries() as capture_query_context:
|
|
response = self.client.get(f"/api/projects/{self.team.id}/insights?basic=true")
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(len(response.json()["results"]), i + 1)
|
|
|
|
query_count_for_create_and_read = len(capture_query_context.captured_queries)
|
|
queries.append(capture_query_context.captured_queries)
|
|
query_counts.append(query_count_for_create_and_read)
|
|
|
|
# adding more insights doesn't change the query count
|
|
self.assertEqual(
|
|
[
|
|
FuzzyInt(11, 12),
|
|
FuzzyInt(11, 12),
|
|
FuzzyInt(11, 12),
|
|
FuzzyInt(11, 12),
|
|
FuzzyInt(11, 12),
|
|
],
|
|
query_counts,
|
|
f"received query counts\n\n{query_counts}",
|
|
)
|
|
|
|
def assert_insight_activity(self, insight_id: Optional[int], expected: list[dict]):
|
|
activity_response = self.dashboard_api.get_insight_activity(insight_id)
|
|
|
|
activity: list[dict] = activity_response["results"]
|
|
|
|
self.maxDiff = None
|
|
assert activity == expected
|