From 24de8b55e45f59d6ec17dff2e03f8fd6ab80fced Mon Sep 17 00:00:00 2001 From: Karl-Aksel Puulmann Date: Wed, 24 Feb 2021 09:32:44 +0200 Subject: [PATCH] Run tests via pytest (#3417) * Compile requirements-dev.txt with latest pip-tools * Install pytest * Avoid picking up factories as tests * New runner * Always set TEST env variable running tests Some of our tests rely on it. * Remove repetition * Fix a broken test * Cut down noise from bin/tests * Rename test factory * Fix stickiness filter * Skip a broken test This has been broken since numpy removal PR. Sadly tests were not running for this submodule * Fix import on ee * Run ee tests properly The django_db_setup fixture will be automatically run when running ee/ module tests. * Make tests run on CI * Include REDIS_URL, fix cloud * Set TEST env variable * Hack cloud tests to work * Attempt at workflow fix * Import Person model when running ee tests This module implicitly adds hooks, so this is needed when running tests * Respect reuse-db for clickhouse * Add custom markers to avoid warnings * pytest: use ch test database always Accidentally wiped by ch setup a few times without this. Oops * Remove repetition in tests * Pytest: Always run migrations Testing a state cleanup fix * Use same DB in conftest and main code * Pytest: autoset TEST setting without env variable * fix broken test Co-authored-by: eric --- .github/workflows/ci-backend.yml | 44 ++--- bin/tests | 8 +- ee/bin/docker-ch-test | 2 +- ee/clickhouse/clickhouse_test_runner.py | 41 ----- .../test_calculate_event_property_usage.py | 4 +- ee/clickhouse/test/test_process_event_ee.py | 4 +- .../views/test/test_clickhouse_element.py | 4 +- .../views/test/test_clickhouse_event.py | 4 +- .../views/test/test_clickhouse_person.py | 4 +- ee/conftest.py | 44 +++++ ee/docker-compose.ch.test.yml | 1 + posthog/api/test/test_element.py | 4 +- posthog/api/test/test_event.py | 4 +- posthog/api/test/test_organization.py | 7 +- posthog/api/test/test_person.py | 4 +- .../filters/test/test_stickiness_filter.py | 6 +- posthog/queries/sessions/sessions.py | 4 + .../queries/sessions/test/test_sessions.py | 2 + .../test/test_sessions_list_builder.py | 2 +- posthog/settings.py | 10 +- .../test_calculate_event_property_usage.py | 4 +- posthog/tasks/test/test_process_event.py | 4 +- posthog/test/test_cohort_model.py | 5 +- posthog/test/test_user_model.py | 7 +- pytest.ini | 6 + requirements-dev.in | 2 + requirements-dev.txt | 171 ++++++++++++++---- 27 files changed, 249 insertions(+), 153 deletions(-) delete mode 100644 ee/clickhouse/clickhouse_test_runner.py create mode 100644 ee/conftest.py create mode 100644 pytest.ini diff --git a/.github/workflows/ci-backend.yml b/.github/workflows/ci-backend.yml index 2fa1fde5c3f..d2548b95efd 100644 --- a/.github/workflows/ci-backend.yml +++ b/.github/workflows/ci-backend.yml @@ -2,6 +2,9 @@ name: Backend CI on: - pull_request +env: + SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only + REDIS_URL: 'redis://localhost' jobs: backend-code-quality: @@ -56,9 +59,7 @@ jobs: - name: Typecheck env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' - REDIS_URL: 'redis://localhost' run: | mypy . @@ -106,29 +107,25 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install -r requirements.txt - python -m pip install freezegun fakeredis + python -m pip install freezegun fakeredis pytest pytest-django if: steps.cache.outputs.cache-hit != 'true' - name: Check migrations env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' - REDIS_URL: 'redis://localhost' run: python manage.py makemigrations --check --dry-run - name: Run posthog tests env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' run: | mkdir -p frontend/dist touch frontend/dist/index.html touch frontend/dist/layout.html touch frontend/dist/shared_dashboard.html - python manage.py test posthog -v 2 + pytest posthog/ - name: Run EE tests env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' PRIMARY_DB: 'clickhouse' CLICKHOUSE_HOST: 'localhost' @@ -140,7 +137,7 @@ jobs: touch frontend/dist/index.html touch frontend/dist/layout.html touch frontend/dist/shared_dashboard.html - python manage.py test ee --testrunner="ee.clickhouse.clickhouse_test_runner.ClickhouseTestRunner" + pytest ee/ cloud: name: Django tests – Cloud @@ -199,16 +196,14 @@ jobs: cd deploy python -m pip install --upgrade pip python -m pip install -r requirements.txt - python -m pip install freezegun fakeredis + python -m pip install freezegun fakeredis pytest pytest-django if: steps.cache.outputs.cache-hit != 'true' # The 2-step migration process (first master, then current branch) verifies that it'll always # be possible to migrate to the new version without problems in production - name: Migrate initially at master, then remove master deploy code env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' - REDIS_URL: 'redis://localhost' run: | python deploy/manage.py migrate rm -rf deploy @@ -220,6 +215,8 @@ jobs: - name: Link posthog-production at current branch run: | + cp deploy/ee/conftest.py multi_tenancy/conftest.py + cp deploy/ee/conftest.py messaging/conftest.py cp -r multi_tenancy deploy/ cp -r messaging deploy/ cat multi_tenancy_settings.py >> deploy/posthog/settings.py @@ -227,9 +224,7 @@ jobs: - name: Check migrations env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' - REDIS_URL: 'redis://localhost' run: | cd deploy python manage.py makemigrations --check --dry-run @@ -237,22 +232,18 @@ jobs: - name: Run posthog tests env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' - REDIS_URL: 'redis://localhost' run: | cd deploy mkdir -p frontend/dist touch frontend/dist/index.html touch frontend/dist/layout.html touch frontend/dist/shared_dashboard.html - python manage.py test posthog --keepdb -v 2 --exclude-tag=skip_on_multitenancy + pytest posthog --reuse-db -m "not skip_on_multitenancy" - name: Run cloud tests (posthog-production) env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' - REDIS_URL: 'redis://localhost' PRIMARY_DB: 'clickhouse' CLICKHOUSE_HOST: 'localhost' CLICKHOUSE_DATABASE: 'posthog_test' @@ -260,20 +251,18 @@ jobs: CLICKHOUSE_VERIFY: 'False' run: | cd deploy - python manage.py test multi_tenancy messaging --keepdb -v 2 --exclude-tag=skip_on_multitenancy + pytest multi_tenancy messaging -m "not skip_on_multitenancy" - name: Run EE tests env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' - REDIS_URL: 'redis://localhost' PRIMARY_DB: 'clickhouse' CLICKHOUSE_HOST: 'localhost' CLICKHOUSE_DATABASE: 'posthog_test' CLICKHOUSE_SECURE: 'False' CLICKHOUSE_VERIFY: 'False' run: | - cd deploy - python manage.py test ee --keepdb + cd deploy/ + pytest ee foss: name: Django tests – FOSS @@ -310,7 +299,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install -r requirements.txt - python -m pip install freezegun fakeredis + python -m pip install freezegun fakeredis pytest pytest-django if: steps.cache.outputs.cache-hit != 'true' - name: Remove ee @@ -319,18 +308,15 @@ jobs: - name: Check migrations env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' - REDIS_URL: 'redis://localhost' run: python manage.py makemigrations --check --dry-run - name: Run tests env: - SECRET_KEY: '6b01eee4f945ca25045b5aab440b953461faf08693a9abbf1166dc7c6b9772da' # unsafe - for testing only DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres' run: | mkdir -p frontend/dist touch frontend/dist/index.html touch frontend/dist/layout.html touch frontend/dist/shared_dashboard.html - python manage.py test -v 2 --exclude-tag=ee + pytest -m "not ee" diff --git a/bin/tests b/bin/tests index e56da1ba9d9..b5cdc0c842e 100755 --- a/bin/tests +++ b/bin/tests @@ -1,5 +1,5 @@ #!/bin/bash -set -x +set -e if ! command -v nodemon &> /dev/null then @@ -7,5 +7,7 @@ then exit fi -REDIS_URL='redis:///' OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES python manage.py test $@ --noinput -REDIS_URL='redis:///' nodemon -w ./posthog -w ./ee --ext py --exec "OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES python manage.py test --noinput --keepdb $@; mypy posthog ee" +export REDIS_URL='redis:///' +export TEST=1 + +nodemon -w ./posthog -w ./ee --ext py --exec "OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES pytest --reuse-db $*; mypy posthog ee" diff --git a/ee/bin/docker-ch-test b/ee/bin/docker-ch-test index 5d44dc570e9..30b06256f18 100755 --- a/ee/bin/docker-ch-test +++ b/ee/bin/docker-ch-test @@ -5,4 +5,4 @@ mkdir -p frontend/dist touch frontend/dist/index.html touch frontend/dist/layout.html touch frontend/dist/shared_dashboard.html -python manage.py test ee --testrunner="ee.clickhouse.clickhouse_test_runner.ClickhouseTestRunner" \ No newline at end of file +pytest ee diff --git a/ee/clickhouse/clickhouse_test_runner.py b/ee/clickhouse/clickhouse_test_runner.py deleted file mode 100644 index c2fd0725708..00000000000 --- a/ee/clickhouse/clickhouse_test_runner.py +++ /dev/null @@ -1,41 +0,0 @@ -from django.test.runner import DiscoverRunner -from infi.clickhouse_orm import Database - -from ee.clickhouse.client import sync_execute -from posthog.settings import ( - CLICKHOUSE_DATABASE, - CLICKHOUSE_HTTP_URL, - CLICKHOUSE_PASSWORD, - CLICKHOUSE_USER, - CLICKHOUSE_VERIFY, -) - - -class ClickhouseTestRunner(DiscoverRunner): - def get_database(self) -> Database: - return Database( - CLICKHOUSE_DATABASE, - db_url=CLICKHOUSE_HTTP_URL, - username=CLICKHOUSE_USER, - password=CLICKHOUSE_PASSWORD, - verify_ssl_cert=CLICKHOUSE_VERIFY, - ) - - def setup_databases(self, **kwargs): - database = self.get_database() - try: - database.drop_database() - except: - pass - database.create_database() - database.migrate("ee.clickhouse.migrations") - # Make DELETE / UPDATE synchronous to avoid flaky tests - sync_execute("SET mutations_sync = 1") - return super().setup_databases(**kwargs) - - def teardown_databases(self, old_config, **kwargs): - try: - self.get_database().drop_database() - except: - pass - super().teardown_databases(old_config, **kwargs) diff --git a/ee/clickhouse/test/test_calculate_event_property_usage.py b/ee/clickhouse/test/test_calculate_event_property_usage.py index dfcc69564c0..ccfbb85a209 100644 --- a/ee/clickhouse/test/test_calculate_event_property_usage.py +++ b/ee/clickhouse/test/test_calculate_event_property_usage.py @@ -3,7 +3,7 @@ from uuid import uuid4 from ee.clickhouse.models.event import create_event from ee.clickhouse.util import ClickhouseTestMixin from posthog.models.event import Event -from posthog.tasks.test.test_calculate_event_property_usage import test_calculate_event_property_usage +from posthog.tasks.test.test_calculate_event_property_usage import calculate_event_property_usage_test_factory def _create_event(**kwargs) -> Event: @@ -14,6 +14,6 @@ def _create_event(**kwargs) -> Event: class CalculateEventPropertyUsage( - ClickhouseTestMixin, test_calculate_event_property_usage(_create_event), # type: ignore + ClickhouseTestMixin, calculate_event_property_usage_test_factory(_create_event), # type: ignore ): pass diff --git a/ee/clickhouse/test/test_process_event_ee.py b/ee/clickhouse/test/test_process_event_ee.py index 2bd5729d030..d290926eb04 100644 --- a/ee/clickhouse/test/test_process_event_ee.py +++ b/ee/clickhouse/test/test_process_event_ee.py @@ -13,7 +13,7 @@ from posthog.models.element import Element from posthog.models.event import Event from posthog.models.session_recording_event import SessionRecordingEvent from posthog.models.utils import UUIDT -from posthog.tasks.test.test_process_event import test_process_event_factory +from posthog.tasks.test.test_process_event import factory_test_process_event def get_session_recording_events(): @@ -57,6 +57,6 @@ def _process_event_ee( class ClickhouseProcessEvent( ClickhouseTestMixin, - test_process_event_factory(_process_event_ee, _get_events, get_session_recording_events, get_elements), # type: ignore + factory_test_process_event(_process_event_ee, _get_events, get_session_recording_events, get_elements), # type: ignore ): pass diff --git a/ee/clickhouse/views/test/test_clickhouse_element.py b/ee/clickhouse/views/test/test_clickhouse_element.py index cc89e3fcc68..a4b2b084697 100644 --- a/ee/clickhouse/views/test/test_clickhouse_element.py +++ b/ee/clickhouse/views/test/test_clickhouse_element.py @@ -2,7 +2,7 @@ from uuid import uuid4 from ee.clickhouse.models.event import create_event from ee.clickhouse.util import ClickhouseTestMixin -from posthog.api.test.test_element import test_element_factory +from posthog.api.test.test_element import factory_test_element from posthog.models import Event @@ -12,6 +12,6 @@ def _create_event(**kwargs): class TestElement( - ClickhouseTestMixin, test_element_factory(_create_event) # type: ignore + ClickhouseTestMixin, factory_test_element(_create_event) # type: ignore ): pass diff --git a/ee/clickhouse/views/test/test_clickhouse_event.py b/ee/clickhouse/views/test/test_clickhouse_event.py index 3b5a9e5d633..349d063d3a7 100644 --- a/ee/clickhouse/views/test/test_clickhouse_event.py +++ b/ee/clickhouse/views/test/test_clickhouse_event.py @@ -8,7 +8,7 @@ from freezegun import freeze_time from ee.clickhouse.models.event import create_event from ee.clickhouse.util import ClickhouseTestMixin from posthog.api.test.base import TransactionBaseTest -from posthog.api.test.test_event import test_event_api_factory +from posthog.api.test.test_event import factory_test_event_api from posthog.models import Action, ActionStep, Event, Person @@ -30,7 +30,7 @@ def _create_person(**kwargs): class ClickhouseTestEventApi( - ClickhouseTestMixin, test_event_api_factory(_create_event, _create_person, _create_action) # type: ignore + ClickhouseTestMixin, factory_test_event_api(_create_event, _create_person, _create_action) # type: ignore ): def test_live_action_events(self): pass diff --git a/ee/clickhouse/views/test/test_clickhouse_person.py b/ee/clickhouse/views/test/test_clickhouse_person.py index dac376861d8..057ee92df2b 100644 --- a/ee/clickhouse/views/test/test_clickhouse_person.py +++ b/ee/clickhouse/views/test/test_clickhouse_person.py @@ -3,7 +3,7 @@ from uuid import uuid4 from ee.clickhouse.client import sync_execute from ee.clickhouse.models.event import create_event from ee.clickhouse.util import ClickhouseTestMixin -from posthog.api.test.test_person import test_person_factory +from posthog.api.test.test_person import factory_test_person from posthog.models import Action, ActionStep, Event, Person @@ -25,6 +25,6 @@ def _create_person(**kwargs): class ClickhouseTestPersonApi( - ClickhouseTestMixin, test_person_factory(_create_event, _create_person, _get_events, Person.objects.all) # type: ignore + ClickhouseTestMixin, factory_test_person(_create_event, _create_person, _get_events, Person.objects.all) # type: ignore ): pass diff --git a/ee/conftest.py b/ee/conftest.py new file mode 100644 index 00000000000..1ecabeebf89 --- /dev/null +++ b/ee/conftest.py @@ -0,0 +1,44 @@ +import pytest +from infi.clickhouse_orm import Database + +from ee.clickhouse.client import sync_execute +from ee.clickhouse.models.person import Person +from posthog.settings import ( + CLICKHOUSE_DATABASE, + CLICKHOUSE_HTTP_URL, + CLICKHOUSE_PASSWORD, + CLICKHOUSE_USER, + CLICKHOUSE_VERIFY, +) + + +@pytest.fixture(scope="package") +def django_db_setup(django_db_setup, django_db_keepdb): + database = Database( + CLICKHOUSE_DATABASE, + db_url=CLICKHOUSE_HTTP_URL, + username=CLICKHOUSE_USER, + password=CLICKHOUSE_PASSWORD, + verify_ssl_cert=CLICKHOUSE_VERIFY, + ) + + if not django_db_keepdb: + try: + database.drop_database() + except: + pass + + if not django_db_keepdb or not database.db_exists: + database.create_database() + + database.migrate("ee.clickhouse.migrations") + # Make DELETE / UPDATE synchronous to avoid flaky tests + sync_execute("SET mutations_sync = 1") + + yield + + if not django_db_keepdb: + try: + database.drop_database() + except: + pass diff --git a/ee/docker-compose.ch.test.yml b/ee/docker-compose.ch.test.yml index 160fb144aa4..f82ad65cf0e 100644 --- a/ee/docker-compose.ch.test.yml +++ b/ee/docker-compose.ch.test.yml @@ -36,6 +36,7 @@ services: DEBUG: 'true' PRIMARY_DB: 'clickhouse' PLUGIN_SERVER_INGESTION: 'true' + TEST: 'true' depends_on: - db - redis diff --git a/posthog/api/test/test_element.py b/posthog/api/test/test_element.py index a55dbd57588..4a92ace307e 100644 --- a/posthog/api/test/test_element.py +++ b/posthog/api/test/test_element.py @@ -8,7 +8,7 @@ from posthog.models import Element, ElementGroup, Event, Organization from posthog.test.base import BaseTest -def test_element_factory(create_event: Callable) -> Callable: +def factory_test_element(create_event: Callable) -> Callable: class TestElement(BaseTest): TESTS_API = True @@ -94,5 +94,5 @@ def test_element_factory(create_event: Callable) -> Callable: return TestElement -class TestElement(test_element_factory(Event.objects.create)): # type: ignore +class TestElement(factory_test_element(Event.objects.create)): # type: ignore pass diff --git a/posthog/api/test/test_event.py b/posthog/api/test/test_event.py index fd86de8a9ca..064d09dac31 100644 --- a/posthog/api/test/test_event.py +++ b/posthog/api/test/test_event.py @@ -9,7 +9,7 @@ from posthog.test.base import TransactionBaseTest from posthog.utils import relative_date_parse -def test_event_api_factory(event_factory, person_factory, action_factory): +def factory_test_event_api(event_factory, person_factory, action_factory): class TestEvents(TransactionBaseTest): TESTS_API = True ENDPOINT = "event" @@ -372,5 +372,5 @@ def _create_action(**kwargs): return action -class TestEvent(test_event_api_factory(Event.objects.create, Person.objects.create, _create_action)): # type: ignore +class TestEvent(factory_test_event_api(Event.objects.create, Person.objects.create, _create_action)): # type: ignore pass diff --git a/posthog/api/test/test_organization.py b/posthog/api/test/test_organization.py index 48f28f2c9b0..8055b58bc78 100644 --- a/posthog/api/test/test_organization.py +++ b/posthog/api/test/test_organization.py @@ -3,6 +3,7 @@ import uuid from typing import cast from unittest.mock import patch +import pytest import pytz from django.test import tag from rest_framework import status @@ -147,7 +148,7 @@ class TestOrganizationAPI(APIBaseTest): class TestSignup(APIBaseTest): CONFIG_USER_EMAIL = None - @tag("skip_on_multitenancy") + @pytest.mark.skip_on_multitenancy @patch("posthog.api.organization.settings.EE_AVAILABLE", False) @patch("posthog.api.organization.posthoganalytics.capture") def test_api_sign_up(self, mock_capture): @@ -210,7 +211,7 @@ class TestSignup(APIBaseTest): # Assert that the password was correctly saved self.assertTrue(user.check_password("notsecure")) - @tag("skip_on_multitenancy") + @pytest.mark.skip_on_multitenancy def test_signup_disallowed_on_initiated_self_hosted(self): with self.settings(MULTI_TENANCY=False): response = self.client.post( @@ -231,7 +232,7 @@ class TestSignup(APIBaseTest): }, ) - @tag("skip_on_multitenancy") + @pytest.mark.skip_on_multitenancy @patch("posthog.api.organization.posthoganalytics.capture") @patch("posthoganalytics.identify") def test_signup_minimum_attrs(self, mock_identify, mock_capture): diff --git a/posthog/api/test/test_person.py b/posthog/api/test/test_person.py index c6f604e287e..210542fd66a 100644 --- a/posthog/api/test/test_person.py +++ b/posthog/api/test/test_person.py @@ -10,7 +10,7 @@ from posthog.tasks.process_event import process_event from posthog.test.base import APIBaseTest -def test_person_factory(event_factory, person_factory, get_events, get_people): +def factory_test_person(event_factory, person_factory, get_events, get_people): class TestPerson(APIBaseTest): def test_search(self) -> None: person_factory( @@ -334,6 +334,6 @@ def test_person_factory(event_factory, person_factory, get_events, get_people): class TestPerson( - test_person_factory(Event.objects.create, Person.objects.create, Event.objects.all, Person.objects.all) # type: ignore + factory_test_person(Event.objects.create, Person.objects.create, Event.objects.all, Person.objects.all) # type: ignore ): pass diff --git a/posthog/models/filters/test/test_stickiness_filter.py b/posthog/models/filters/test/test_stickiness_filter.py index faac4845d75..9aeaae86831 100644 --- a/posthog/models/filters/test/test_stickiness_filter.py +++ b/posthog/models/filters/test/test_stickiness_filter.py @@ -21,8 +21,8 @@ class TestStickinessFilter(BaseTest): filter.to_dict(), { "compare": True, - "date_from": "2020-01-01T20:00:00+00:00", - "date_to": "2020-02-01T20:00:00+00:00", + "date_from": "2020-01-01T20:00:00Z", + "date_to": "2020-02-01T20:00:00Z", "events": [ { "id": "$pageview", @@ -34,7 +34,7 @@ class TestStickinessFilter(BaseTest): "properties": [], } ], - "actions": [], + "insight": "TRENDS", "interval": "month", }, ) diff --git a/posthog/queries/sessions/sessions.py b/posthog/queries/sessions/sessions.py index e753ecaaec4..368ae491931 100644 --- a/posthog/queries/sessions/sessions.py +++ b/posthog/queries/sessions/sessions.py @@ -150,6 +150,10 @@ class Sessions(BaseQuery): df["date"] = (df["date"].replace(day=1) + datetime.timedelta(days=32)).replace( day=1 ) - datetime.timedelta(days=1) + for idx in range(len(date_range)): + date_range[idx] = (date_range[idx].replace(day=1) + datetime.timedelta(days=32)).replace( + day=1 + ) - datetime.timedelta(days=1) datewise_data = {d["date"]: d["count"] for d in data_array} values = [(key, datewise_data.get(key, 0)) for key in date_range] diff --git a/posthog/queries/sessions/test/test_sessions.py b/posthog/queries/sessions/test/test_sessions.py index 010a5cac3d3..73fbd1efaec 100644 --- a/posthog/queries/sessions/test/test_sessions.py +++ b/posthog/queries/sessions/test/test_sessions.py @@ -1,3 +1,5 @@ +import unittest + from freezegun import freeze_time from posthog.models import Event diff --git a/posthog/queries/sessions/test/test_sessions_list_builder.py b/posthog/queries/sessions/test/test_sessions_list_builder.py index bd94ea71f64..737542f8d43 100644 --- a/posthog/queries/sessions/test/test_sessions_list_builder.py +++ b/posthog/queries/sessions/test/test_sessions_list_builder.py @@ -90,7 +90,7 @@ class TestSessionListBuilder(BaseTest): self.assertEqual( self.builder.pagination, { - "offset": 2, + "distinct_id_offset": 2, "last_seen": { "1": (now() - relativedelta(minutes=35)).timestamp(), "2": (now() - relativedelta(minutes=45)).timestamp(), diff --git a/posthog/settings.py b/posthog/settings.py index 115987c374b..63a7880a7ca 100644 --- a/posthog/settings.py +++ b/posthog/settings.py @@ -58,7 +58,9 @@ def print_warning(warning_lines: Sequence[str]): BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DEBUG = get_from_env("DEBUG", False, type_cast=strtobool) -TEST = "test" in sys.argv or get_from_env("TEST", False, type_cast=strtobool) # type: bool +TEST = ( + "test" in sys.argv or sys.argv[0].endswith("pytest") or get_from_env("TEST", False, type_cast=strtobool) +) # type: bool SELF_CAPTURE = get_from_env("SELF_CAPTURE", DEBUG, type_cast=strtobool) SHELL_PLUS_PRINT_SQL = get_from_env("PRINT_SQL", False, type_cast=strtobool) @@ -155,12 +157,6 @@ EE_AVAILABLE = False PLUGIN_SERVER_INGESTION = get_from_env("PLUGIN_SERVER_INGESTION", False, type_cast=strtobool) -if PRIMARY_DB == RDBMS.CLICKHOUSE: - TEST_RUNNER = os.getenv("TEST_RUNNER", "ee.clickhouse.clickhouse_test_runner.ClickhouseTestRunner") -else: - TEST_RUNNER = os.getenv("TEST_RUNNER", "django.test.runner.DiscoverRunner") - - ASYNC_EVENT_ACTION_MAPPING = get_from_env("ASYNC_EVENT_ACTION_MAPPING", False, type_cast=strtobool) # Enable if ingesting with the plugin server into postgres, as it's not able to calculate the mapping on the fly diff --git a/posthog/tasks/test/test_calculate_event_property_usage.py b/posthog/tasks/test/test_calculate_event_property_usage.py index bfa0702e284..562b5b05a24 100644 --- a/posthog/tasks/test/test_calculate_event_property_usage.py +++ b/posthog/tasks/test/test_calculate_event_property_usage.py @@ -7,7 +7,7 @@ from posthog.tasks.calculate_event_property_usage import calculate_event_propert from posthog.test.base import BaseTest -def test_calculate_event_property_usage(create_event: Callable) -> Callable: +def calculate_event_property_usage_test_factory(create_event: Callable) -> Callable: class Test(BaseTest): def test_calculate_usage(self) -> None: self.team.event_names = ["$pageview", "custom event"] @@ -101,5 +101,5 @@ def test_calculate_event_property_usage(create_event: Callable) -> Callable: return Test -class Test(test_calculate_event_property_usage(Event.objects.create)): # type: ignore +class Test(calculate_event_property_usage_test_factory(Event.objects.create)): # type: ignore pass diff --git a/posthog/tasks/test/test_process_event.py b/posthog/tasks/test/test_process_event.py index ae38a39dae6..02a5b504b70 100644 --- a/posthog/tasks/test/test_process_event.py +++ b/posthog/tasks/test/test_process_event.py @@ -29,7 +29,7 @@ def get_elements(event_id: Union[int, UUID]) -> List[Element]: return [e for e in ElementGroup.objects.get(hash=event.elements_hash).element_set.all().order_by("order")] -def test_process_event_factory( +def factory_test_process_event( process_event: Callable, get_events: Callable, get_session_recording_events: Callable, get_elements: Callable ) -> Callable: class TestProcessEvent(BaseTest): @@ -889,5 +889,5 @@ def test_process_event_factory( return TestProcessEvent -class TestProcessEvent(test_process_event_factory(_process_event, Event.objects.all, SessionRecordingEvent.objects.all, get_elements)): # type: ignore +class TestProcessEvent(factory_test_process_event(_process_event, Event.objects.all, SessionRecordingEvent.objects.all, get_elements)): # type: ignore pass diff --git a/posthog/test/test_cohort_model.py b/posthog/test/test_cohort_model.py index fd1c051db6b..8a18abe7ba4 100644 --- a/posthog/test/test_cohort_model.py +++ b/posthog/test/test_cohort_model.py @@ -1,5 +1,6 @@ from unittest.mock import patch +import pytest from django.test import tag from freezegun import freeze_time @@ -73,7 +74,7 @@ class TestCohort(BaseTest): self.assertEqual(cohort.people.count(), 2) self.assertEqual(cohort.is_calculating, False) - @tag("ee") + @pytest.mark.ee @patch("ee.clickhouse.models.cohort.get_person_ids_by_cohort_id") def test_calculating_cohort_clickhouse(self, get_person_ids_by_cohort_id): person1 = Person.objects.create( @@ -93,7 +94,7 @@ class TestCohort(BaseTest): self.assertCountEqual(list(cohort.people.all()), [person1, person2]) - @tag("ee") + @pytest.mark.ee def test_clickhouse_empty_query(self): cohort2 = Cohort.objects.create( team=self.team, groups=[{"properties": {"$some_prop": "nomatchihope"}}], name="cohort1", diff --git a/posthog/test/test_user_model.py b/posthog/test/test_user_model.py index 0232cd4026e..83ef9b886a0 100644 --- a/posthog/test/test_user_model.py +++ b/posthog/test/test_user_model.py @@ -1,5 +1,6 @@ from unittest.mock import Mock, patch +import pytest from dateutil.relativedelta import relativedelta from django.test import tag from django.utils.timezone import now @@ -10,7 +11,7 @@ from posthog.test.base import BaseTest class TestUser(BaseTest): - @tag("ee") + @pytest.mark.ee @patch("posthog.models.organization.License.PLANS", {"enterprise": ["whatever"]}) @patch("ee.models.license.requests.post") def test_feature_available_self_hosted_has_license(self, patch_post): @@ -24,13 +25,13 @@ class TestUser(BaseTest): self.assertTrue(self.organization.is_feature_available("whatever")) self.assertFalse(self.organization.is_feature_available("feature-doesnt-exist")) - @tag("ee") + @pytest.mark.ee @patch("posthog.models.organization.License.PLANS", {"enterprise": ["whatever"]}) def test_feature_available_self_hosted_no_license(self): self.assertFalse(self.organization.is_feature_available("whatever")) self.assertFalse(self.organization.is_feature_available("feature-doesnt-exist")) - @tag("ee") + @pytest.mark.ee @patch("posthog.models.organization.License.PLANS", {"enterprise": ["whatever"]}) @patch("ee.models.license.requests.post") def test_feature_available_self_hosted_license_expired(self, patch_post): diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000000..724d994ace8 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,6 @@ +[pytest] +DJANGO_SETTINGS_MODULE = posthog.settings +addopts = -p no:warnings +markers = + ee + skip_on_multitenancy diff --git a/requirements-dev.in b/requirements-dev.in index 079e6a19bab..5846a5e3202 100644 --- a/requirements-dev.in +++ b/requirements-dev.in @@ -19,3 +19,5 @@ freezegun packaging black isort +pytest +pytest-django diff --git a/requirements-dev.txt b/requirements-dev.txt index 1ebdec555b9..13099fd5960 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,46 +4,137 @@ # # pip-compile requirements-dev.in # -appdirs==1.4.4 # via black -asgiref==3.2.7 # via django -attrs==19.3.0 # via black, flake8-bugbear -black==19.10b0 # via -r requirements-dev.in -click==7.1.1 # via black, pip-tools -django-debug-toolbar==2.2 # via -r requirements-dev.in -django-stubs==1.7.0 # via -r requirements-dev.in, djangorestframework-stubs -django==3.0.6 # via django-debug-toolbar, django-stubs -djangorestframework-stubs==1.1.0 # via -r requirements-dev.in -entrypoints==0.3 # via flake8 -fakeredis==1.4.4 # via -r requirements-dev.in -flake8-bugbear==20.1.4 # via -r requirements-dev.in -flake8-colors==0.1.6 # via -r requirements-dev.in -flake8-commas==2.0.0 # via -r requirements-dev.in -flake8-comprehensions==3.2.2 # via -r requirements-dev.in -flake8-import-order==0.18.1 # via -r requirements-dev.in -flake8-logging-format==0.6.0 # via -r requirements-dev.in -flake8-print==3.1.4 # via -r requirements-dev.in -flake8==3.7.9 # via -r requirements-dev.in, flake8-bugbear, flake8-colors, flake8-commas, flake8-comprehensions, flake8-print -freezegun==0.3.15 # via -r requirements-dev.in -isort==5.2.2 # via -r requirements-dev.in -mccabe==0.6.1 # via flake8 -mypy-extensions==0.4.3 # via -r requirements-dev.in, mypy -mypy==0.790 # via -r requirements-dev.in, django-stubs, djangorestframework-stubs -packaging==20.4 # via -r requirements-dev.in -pathspec==0.8.0 # via black -pip-tools==5.0.0 # via -r requirements-dev.in -pycodestyle==2.5.0 # via flake8, flake8-import-order, flake8-print -pyflakes==2.1.1 # via flake8 -pyparsing==2.4.7 # via packaging -python-dateutil==2.8.1 # via freezegun -pytz==2020.1 # via django -redis==3.5.3 # via fakeredis -regex==2020.6.8 # via black -six==1.14.0 # via fakeredis, flake8-print, freezegun, packaging, pip-tools, python-dateutil -sortedcontainers==2.3.0 # via fakeredis -sqlparse==0.3.1 # via django, django-debug-toolbar -toml==0.10.1 # via black -typed-ast==1.4.1 # via black, mypy -typing-extensions==3.7.4.2 # via django-stubs, djangorestframework-stubs, mypy +appdirs==1.4.4 + # via black +asgiref==3.2.7 + # via django +attrs==19.3.0 + # via + # black + # flake8-bugbear + # pytest +black==19.10b0 + # via -r requirements-dev.in +click==7.1.1 + # via + # black + # pip-tools +django-debug-toolbar==2.2 + # via -r requirements-dev.in +django-stubs==1.7.0 + # via + # -r requirements-dev.in + # djangorestframework-stubs +django==3.0.6 + # via + # django-debug-toolbar + # django-stubs +djangorestframework-stubs==1.1.0 + # via -r requirements-dev.in +entrypoints==0.3 + # via flake8 +fakeredis==1.4.4 + # via -r requirements-dev.in +flake8-bugbear==20.1.4 + # via -r requirements-dev.in +flake8-colors==0.1.6 + # via -r requirements-dev.in +flake8-commas==2.0.0 + # via -r requirements-dev.in +flake8-comprehensions==3.2.2 + # via -r requirements-dev.in +flake8-import-order==0.18.1 + # via -r requirements-dev.in +flake8-logging-format==0.6.0 + # via -r requirements-dev.in +flake8-print==3.1.4 + # via -r requirements-dev.in +flake8==3.7.9 + # via + # -r requirements-dev.in + # flake8-bugbear + # flake8-colors + # flake8-commas + # flake8-comprehensions + # flake8-print +freezegun==0.3.15 + # via -r requirements-dev.in +iniconfig==1.1.1 + # via pytest +isort==5.2.2 + # via -r requirements-dev.in +mccabe==0.6.1 + # via flake8 +mypy-extensions==0.4.3 + # via + # -r requirements-dev.in + # mypy +mypy==0.790 + # via + # -r requirements-dev.in + # django-stubs + # djangorestframework-stubs +packaging==20.4 + # via + # -r requirements-dev.in + # pytest +pathspec==0.8.0 + # via black +pip-tools==5.5.0 + # via -r requirements-dev.in +pluggy==0.13.1 + # via pytest +py==1.10.0 + # via pytest +pycodestyle==2.5.0 + # via + # flake8 + # flake8-import-order + # flake8-print +pyflakes==2.1.1 + # via flake8 +pyparsing==2.4.7 + # via packaging +pytest-django==4.1.0 + # via -r requirements-dev.in +pytest==6.2.2 + # via + # -r requirements-dev.in + # pytest-django +python-dateutil==2.8.1 + # via freezegun +pytz==2020.1 + # via django +redis==3.5.3 + # via fakeredis +regex==2020.6.8 + # via black +six==1.14.0 + # via + # fakeredis + # flake8-print + # freezegun + # packaging + # python-dateutil +sortedcontainers==2.3.0 + # via fakeredis +sqlparse==0.3.1 + # via + # django + # django-debug-toolbar +toml==0.10.1 + # via + # black + # pytest +typed-ast==1.4.1 + # via + # black + # mypy +typing-extensions==3.7.4.2 + # via + # django-stubs + # djangorestframework-stubs + # mypy # The following packages are considered to be unsafe in a requirements file: # pip