Skip to content

Commit

Permalink
test(backup): Create first model test
Browse files Browse the repository at this point in the history
This creates a second test file for tests of specific models. The
eventual end-goal here is to test all models that have `__include_in_export =
True` set. This is the first change supporting that work, focused mostly
on adding a single proof-of-concept test (for organizations), along with
the scaffolding that will make adding more such tests easier.

Issue: getsentry/team-ospo/#156
  • Loading branch information
azaslavsky committed Jul 11, 2023
1 parent 8839b6d commit 0dec276
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 20 deletions.
28 changes: 28 additions & 0 deletions tests/sentry/backup/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from __future__ import annotations

from pathlib import Path

from click.testing import CliRunner

from sentry.runner.commands.backup import ComparatorFindings, export
from sentry.utils import json


class ValidationError(Exception):
def __init__(self, info: ComparatorFindings):
super().__init__(info.pretty())
self.info = info


def tmp_export_to_file(tmp_path: Path) -> json.JSONData:
"""Helper function that exports the current state of the database to the specified file."""

tmp_json_file_path = str(tmp_path)
rv = CliRunner().invoke(
export, [tmp_json_file_path], obj={"silent": True, "indent": 2, "exclude": None}
)
assert rv.exit_code == 0, rv.output

with open(tmp_json_file_path) as tmp_file:
output = json.load(tmp_file)
return output
26 changes: 6 additions & 20 deletions tests/sentry/backup/test_correctness.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,26 @@
from freezegun import freeze_time

from sentry.db.postgres.roles import in_test_psql_role_override
from sentry.runner.commands.backup import ComparatorFindings, export, import_, validate
from sentry.runner.commands.backup import import_, validate
from sentry.testutils.factories import get_fixture_path
from sentry.utils import json
from sentry.utils.pytest.fixtures import django_db_all


class ValidationError(Exception):
def __init__(self, info: ComparatorFindings):
super().__init__(info.pretty())
self.info = info
from tests.sentry.backup import ValidationError, tmp_export_to_file


def import_export_then_validate(tmp_path: Path, fixture_file_name: str) -> None:
"""Test helper that validates that the originally imported data correctly matches actual
outputted export data."""
"""Test helper that validates that data imported from a fixture `.json` file correctly matches
the actual outputted export data."""

fixture_file_path = get_fixture_path("backup", fixture_file_name)
with open(fixture_file_path) as backup_file:
input = json.load(backup_file)
expect = json.load(backup_file)

with in_test_psql_role_override("postgres"):
rv = CliRunner().invoke(import_, [str(fixture_file_path)])
assert rv.exit_code == 0, rv.output

tmp_json_file_path = str(tmp_path.joinpath("tmp_test_file.json"))
rv = CliRunner().invoke(
export, [tmp_json_file_path], obj={"silent": True, "indent": 2, "exclude": None}
)
assert rv.exit_code == 0, rv.output

with open(tmp_json_file_path) as tmp_file:
output = json.load(tmp_file)

res = validate(input, output)
res = validate(expect, tmp_export_to_file(tmp_path.joinpath("tmp_test_file.json")))
if res.findings:
raise ValidationError(res)

Expand Down
63 changes: 63 additions & 0 deletions tests/sentry/backup/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from __future__ import annotations

import atexit
import tempfile
from pathlib import Path

from click.testing import CliRunner
from django.core.management import call_command

from sentry.db.postgres.roles import in_test_psql_role_override
from sentry.runner.commands.backup import import_, validate
from sentry.testutils import TransactionTestCase
from tests.sentry.backup import ValidationError, tmp_export_to_file


class ModelBackupTests(TransactionTestCase):
"""Test the JSON-ification of models marked `__include_in_export__ = True`. Each test here
creates a fresh database, performs some writes to it, then exports that data into a temporary
file (called the "expected" JSON). It then imports the "expected" JSON and re-exports it into
the "actual" JSON file, and diffs the two to ensure that they match per the specified
comparators."""

def setUp(self):
# Create a temporary directory for JSON exports.
self.tmp_dir = tempfile.TemporaryDirectory()
atexit.register(self.tmp_dir.cleanup)

# Reset the Django database.
call_command("flush", verbosity=0, interactive=False)

# Generate temporary filenames for the expected and actual JSON files.
self.tmp_expect = Path(self.tmp_dir.name) / f"{self._testMethodName}.expect.json"
self.tmp_actual = Path(self.tmp_dir.name) / f"{self._testMethodName}.actual.json"

def tearDown(self):
self.tmp_dir.cleanup()

def import_export_then_validate(self):
"""Test helper that validates that data imported from a temporary `.json` file correctly
matches the actual outputted export data."""

# Export the current state of the database into the "expected" temporary file, then parse it
# into a JSON object for comparison.
expect = tmp_export_to_file(self.tmp_expect)

# Reset the Django database.
call_command("flush", verbosity=0, interactive=False)

# Write the contents of the "expected" JSON file into the now clean database.
with in_test_psql_role_override("postgres"):
rv = CliRunner().invoke(import_, [str(self.tmp_expect)])
assert rv.exit_code == 0, rv.output

# Validate that the "expected" and "actual" JSON matches.
actual = tmp_export_to_file(self.tmp_actual)
res = validate(expect, actual)
if res.findings:
raise ValidationError(res)

def test_organization(self):
user = self.create_user()
self.create_organization(owner=user)
self.import_export_then_validate()

0 comments on commit 0dec276

Please sign in to comment.