#1784 - added wrapper class

Added documentation
This commit is contained in:
Petr Kalis 2021-07-14 14:58:36 +02:00
parent 7fd3abc91f
commit f977cba564
3 changed files with 256 additions and 202 deletions

View file

@ -1 +1,42 @@
Folder for libs and tooling for automatic testing. Automatic testing
-----------------
Folder for libs and tooling for automatic testing.
- db_handler.py - class for preparation of test DB
- dumps DB(s) to BSON (mongodump)
- loads dump(s) to new DB (mongorestore)
- loads sql file(s) to DB (mongoimport)
- deletes test DB
- file_handler.py - class to download test data from GDrive
- downloads data from (list) of files from GDrive
- checks md5 if file ok
- unzips if zip
- testing_wrapper.py - base class to use for testing
- all env var necessary for running (OPENPYPE_MONGO ...)
- implements reusable fixtures to:
- load test data (uses `file_handler`)
- prepare DB (uses `db_handler`)
- modify temporarily env vars for testing
Should be used as a skeleton to create new test cases.
Test data
---------
Each class implementing `TestCase` can provide test file(s) by adding them to
TEST_FILES ('GDRIVE_FILE_ID', 'ACTUAL_FILE_NAME', 'MD5HASH')
GDRIVE_FILE_ID can be pulled from shareable link from Google Drive app.
Currently it is expected that test file will be zip file with structure:
- expected - expected files (not implemented yet)
- input
- data - test data (workfiles, images etc)
- dumps - folder for BSOn dumps from (`mongodump`)
- env_vars
env_vars.json - dictionary with environment variables {key:value}
- sql - sql files to load with `mongoimport` (human readable)

View file

@ -0,0 +1,105 @@
import os
import sys
import six
import json
import pytest
import tempfile
import shutil
from bson.objectid import ObjectId
from tests.lib.db_handler import DBHandler
from tests.lib.file_handler import RemoteFileHandler
class TestCase():
TEST_OPENPYPE_MONGO = "mongodb://localhost:27017"
TEST_DB_NAME = "test_db"
TEST_PROJECT_NAME = "test_project"
TEST_OPENPYPE_NAME = "test_openpype"
REPRESENTATION_ID = "60e578d0c987036c6a7b741d"
TEST_FILES = [
("1eCwPljuJeOI8A3aisfOIBKKjcmIycTEt", "test_site_operations.zip", "")
]
@pytest.fixture(scope='session')
def monkeypatch_session(self):
"""Monkeypatch couldn't be used with module or session fixtures."""
from _pytest.monkeypatch import MonkeyPatch
m = MonkeyPatch()
yield m
m.undo()
@pytest.fixture(scope="module")
def download_test_data(self):
tmpdir = tempfile.mkdtemp()
for test_file in self.TEST_FILES:
file_id, file_name, md5 = test_file
f_name, ext = os.path.splitext(file_name)
RemoteFileHandler.download_file_from_google_drive(file_id,
str(tmpdir),
file_name)
if ext.lstrip('.') in RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS:
RemoteFileHandler.unzip(os.path.join(tmpdir, file_name))
yield tmpdir
shutil.rmtree(tmpdir)
@pytest.fixture(scope="module")
def env_var(self, monkeypatch_session, download_test_data):
"""Sets temporary env vars from json file."""
env_url = os.path.join(download_test_data, "input",
"env_vars", "env_var.json")
if not os.path.exists(env_url):
raise ValueError("Env variable file {} doesn't exist".format(env_url))
env_dict = {}
try:
with open(env_url) as json_file:
env_dict = json.load(json_file)
except ValueError:
print("{} doesn't contain valid JSON")
six.reraise(*sys.exc_info())
for key, value in env_dict.items():
all_vars = globals()
all_vars.update(vars(TestCase)) # TODO check
value = value.format(**all_vars)
print("Setting {}:{}".format(key, value))
monkeypatch_session.setenv(key, value)
@pytest.fixture(scope="module")
def db_setup(self, download_test_data, env_var, monkeypatch_session):
"""Restore prepared MongoDB dumps into selected DB."""
backup_dir = os.path.join(download_test_data, "input", "dumps")
uri = os.environ.get("OPENPYPE_MONGO") or "mongodb://localhost:27017"
db_handler = DBHandler(uri)
db_handler.setup_from_dump(self.TEST_DB_NAME, backup_dir, True,
db_name_out=self.TEST_DB_NAME)
db_handler.setup_from_dump("openpype", backup_dir, True,
db_name_out=self.TEST_OPENPYPE_NAME)
yield db_handler
db_handler.teardown(self.TEST_DB_NAME)
db_handler.teardown(self.TEST_OPENPYPE_NAME)
@pytest.fixture(scope="module")
def db(self, db_setup):
"""Provide test database connection.
Database prepared from dumps with 'db_setup' fixture.
"""
from avalon.api import AvalonMongoDB
db = AvalonMongoDB()
yield db

View file

@ -11,213 +11,121 @@
removes temporary folder removes temporary folder
removes temporary databases (?) removes temporary databases (?)
""" """
import os
import sys
import six
import json
import pytest import pytest
import tempfile
import shutil from tests.lib.testing_wrapper import TestCase
from bson.objectid import ObjectId from bson.objectid import ObjectId
from tests.lib.db_handler import DBHandler
from tests.lib.file_handler import RemoteFileHandler
TEST_OPENPYPE_MONGO = "mongodb://localhost:27017" class TestSiteOperation(TestCase):
TEST_DB_NAME = "test_db"
TEST_PROJECT_NAME = "test_project"
TEST_OPENPYPE_NAME = "test_openpype"
REPRESENTATION_ID = "60e578d0c987036c6a7b741d"
TEST_FILES = [ @pytest.fixture(scope="module")
("1eCwPljuJeOI8A3aisfOIBKKjcmIycTEt", "test_site_operations.zip", "") def setup_sync_server_module(self, db):
] """Get sync_server_module from ModulesManager"""
from openpype.modules import ModulesManager
@pytest.fixture(scope='session') manager = ModulesManager()
def monkeypatch_session(): sync_server = manager.modules_by_name["sync_server"]
"""Monkeypatch couldn't be used with module or session fixtures.""" yield sync_server
from _pytest.monkeypatch import MonkeyPatch
m = MonkeyPatch()
yield m @pytest.mark.usefixtures("db")
m.undo() def test_project_created(self, db):
assert ['test_project'] == db.database.collection_names(False)
@pytest.fixture(scope="module")
def download_test_data(): @pytest.mark.usefixtures("db")
tmpdir = tempfile.mkdtemp() def test_objects_imported(self, db):
for test_file in TEST_FILES: count_obj = len(list(db.database[self.TEST_PROJECT_NAME].find({})))
file_id, file_name, md5 = test_file assert 15 == count_obj
f_name, ext = os.path.splitext(file_name)
@pytest.mark.usefixtures("setup_sync_server_module")
RemoteFileHandler.download_file_from_google_drive(file_id, def test_add_site(self, db, setup_sync_server_module):
str(tmpdir), """Adds 'test_site', checks that added, checks that doesn't duplicate."""
file_name) query = {
"_id": ObjectId(self.REPRESENTATION_ID)
if ext.lstrip('.') in RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS: }
RemoteFileHandler.unzip(os.path.join(tmpdir, file_name))
ret = db.database[self.TEST_PROJECT_NAME].find(query)
yield tmpdir
shutil.rmtree(tmpdir) assert 1 == len(list(ret)), \
"Single {} must be in DB".format(self.REPRESENTATION_ID)
@pytest.fixture(scope="module") setup_sync_server_module.add_site(self.TEST_PROJECT_NAME, self.REPRESENTATION_ID,
def env_var(monkeypatch_session, download_test_data):
"""Sets temporary env vars from json file."""
env_url = os.path.join(download_test_data, "input",
"env_vars", "env_var.json")
if not os.path.exists(env_url):
raise ValueError("Env variable file {} doesn't exist".format(env_url))
env_dict = {}
try:
with open(env_url) as json_file:
env_dict = json.load(json_file)
except ValueError:
print("{} doesn't contain valid JSON")
six.reraise(*sys.exc_info())
for key, value in env_dict.items():
value = value.format(**globals())
print("Setting {}:{}".format(key, value))
monkeypatch_session.setenv(key, value)
@pytest.fixture(scope="module")
def db_setup(download_test_data, env_var, monkeypatch_session):
"""Restore prepared MongoDB dumps into selected DB."""
backup_dir = os.path.join(download_test_data, "input", "dumps")
uri = os.environ.get("OPENPYPE_MONGO") or "mongodb://localhost:27017"
db_handler = DBHandler(uri)
db_handler.setup_from_dump(TEST_DB_NAME, backup_dir, True,
db_name_out=TEST_DB_NAME)
db_handler.setup_from_dump("openpype", backup_dir, True,
db_name_out=TEST_OPENPYPE_NAME)
yield db_handler
db_handler.teardown(TEST_DB_NAME)
db_handler.teardown(TEST_OPENPYPE_NAME)
@pytest.fixture(scope="module")
def db(db_setup):
"""Provide test database connection.
Database prepared from dumps with 'db_setup' fixture.
"""
from avalon.api import AvalonMongoDB
db = AvalonMongoDB()
yield db
@pytest.fixture(scope="module")
def setup_sync_server_module(db):
"""Get sync_server_module from ModulesManager"""
from openpype.modules import ModulesManager
manager = ModulesManager()
sync_server = manager.modules_by_name["sync_server"]
yield sync_server
@pytest.mark.usefixtures("db")
def test_project_created(db):
assert ['test_project'] == db.database.collection_names(False)
@pytest.mark.usefixtures("db")
def test_objects_imported(db):
count_obj = len(list(db.database[TEST_PROJECT_NAME].find({})))
assert 15 == count_obj
@pytest.mark.usefixtures("setup_sync_server_module")
def test_add_site(db, setup_sync_server_module):
"""Adds 'test_site', checks that added, checks that doesn't duplicate."""
query = {
"_id": ObjectId(REPRESENTATION_ID)
}
ret = db.database[TEST_PROJECT_NAME].find(query)
assert 1 == len(list(ret)), \
"Single {} must be in DB".format(REPRESENTATION_ID)
setup_sync_server_module.add_site(TEST_PROJECT_NAME, REPRESENTATION_ID,
site_name='test_site')
ret = list(db.database[TEST_PROJECT_NAME].find(query))
assert 1 == len(ret), \
"Single {} must be in DB".format(REPRESENTATION_ID)
ret = ret.pop()
site_names = [site["name"] for site in ret["files"][0]["sites"]]
assert 'test_site' in site_names, "Site name wasn't added"
@pytest.mark.usefixtures("setup_sync_server_module")
def test_add_site_again(db, setup_sync_server_module):
"""Depends on test_add_site, must throw exception."""
with pytest.raises(ValueError):
setup_sync_server_module.add_site(TEST_PROJECT_NAME, REPRESENTATION_ID,
site_name='test_site') site_name='test_site')
ret = list(db.database[self.TEST_PROJECT_NAME].find(query))
@pytest.mark.usefixtures("setup_sync_server_module")
def test_add_site_again_force(db, setup_sync_server_module): assert 1 == len(ret), \
"""Depends on test_add_site, must not throw exception.""" "Single {} must be in DB".format(self.REPRESENTATION_ID)
setup_sync_server_module.add_site(TEST_PROJECT_NAME, REPRESENTATION_ID,
site_name='test_site', force=True) ret = ret.pop()
site_names = [site["name"] for site in ret["files"][0]["sites"]]
query = { assert 'test_site' in site_names, "Site name wasn't added"
"_id": ObjectId(REPRESENTATION_ID)
}
@pytest.mark.usefixtures("setup_sync_server_module")
ret = list(db.database[TEST_PROJECT_NAME].find(query)) def test_add_site_again(self, db, setup_sync_server_module):
"""Depends on test_add_site, must throw exception."""
assert 1 == len(ret), \ with pytest.raises(ValueError):
"Single {} must be in DB".format(REPRESENTATION_ID) setup_sync_server_module.add_site(self.TEST_PROJECT_NAME, self.REPRESENTATION_ID,
site_name='test_site')
@pytest.mark.usefixtures("setup_sync_server_module")
def test_remove_site(db, setup_sync_server_module): @pytest.mark.usefixtures("setup_sync_server_module")
"""Depends on test_add_site, must remove 'test_site'.""" def test_add_site_again_force(self, db, setup_sync_server_module):
setup_sync_server_module.remove_site(TEST_PROJECT_NAME, REPRESENTATION_ID, """Depends on test_add_site, must not throw exception."""
site_name='test_site') setup_sync_server_module.add_site(self.TEST_PROJECT_NAME, self.REPRESENTATION_ID,
site_name='test_site', force=True)
query = {
"_id": ObjectId(REPRESENTATION_ID) query = {
} "_id": ObjectId(self.REPRESENTATION_ID)
}
ret = list(db.database[TEST_PROJECT_NAME].find(query))
ret = list(db.database[self.TEST_PROJECT_NAME].find(query))
assert 1 == len(ret), \
"Single {} must be in DB".format(REPRESENTATION_ID) assert 1 == len(ret), \
"Single {} must be in DB".format(self.REPRESENTATION_ID)
ret = ret.pop()
site_names = [site["name"] for site in ret["files"][0]["sites"]]
@pytest.mark.usefixtures("setup_sync_server_module")
assert 'test_site' not in site_names, "Site name wasn't removed" def test_remove_site(self, db, setup_sync_server_module):
"""Depends on test_add_site, must remove 'test_site'."""
setup_sync_server_module.remove_site(self.TEST_PROJECT_NAME, self.REPRESENTATION_ID,
@pytest.mark.usefixtures("setup_sync_server_module")
def test_remove_site_again(db, setup_sync_server_module):
"""Depends on test_add_site, must trow exception"""
with pytest.raises(ValueError):
setup_sync_server_module.remove_site(TEST_PROJECT_NAME,
REPRESENTATION_ID,
site_name='test_site') site_name='test_site')
query = {
"_id": ObjectId(self.REPRESENTATION_ID)
}
ret = list(db.database[self.TEST_PROJECT_NAME].find(query))
assert 1 == len(ret), \
"Single {} must be in DB".format(self.REPRESENTATION_ID)
ret = ret.pop()
site_names = [site["name"] for site in ret["files"][0]["sites"]]
assert 'test_site' not in site_names, "Site name wasn't removed"
@pytest.mark.usefixtures("setup_sync_server_module")
def test_remove_site_again(self, db, setup_sync_server_module):
"""Depends on test_add_site, must trow exception"""
with pytest.raises(ValueError):
setup_sync_server_module.remove_site(self.TEST_PROJECT_NAME,
self.REPRESENTATION_ID,
site_name='test_site')
query = {
"_id": ObjectId(self.REPRESENTATION_ID)
}
ret = list(db.database[self.TEST_PROJECT_NAME].find(query))
assert 1 == len(ret), \
"Single {} must be in DB".format(self.REPRESENTATION_ID)
query = {
"_id": ObjectId(REPRESENTATION_ID)
}
ret = list(db.database[TEST_PROJECT_NAME].find(query)) test_case = TestSiteOperation()
assert 1 == len(ret), \
"Single {} must be in DB".format(REPRESENTATION_ID)