from unittest import mock

import sqlalchemy
from sqlalchemy import select

from galaxy import (
    exceptions,
    model,
)
from galaxy.app_unittest_utils.galaxy_mock import mock_url_builder
from galaxy.managers import hdas
from galaxy.managers.datasets import DatasetManager
from galaxy.managers.histories import HistoryManager
from .base import BaseTestCase

# =============================================================================
default_password = "123456"
user2_data = dict(email="user2@user2.user2", username="user2", password=default_password)
user3_data = dict(email="user3@user3.user3", username="user3", password=default_password)


# =============================================================================
class HDATestCase(BaseTestCase):
    def set_up_managers(self):
        super().set_up_managers()
        self.hda_manager = self.app[hdas.HDAManager]
        self.history_manager = self.app[HistoryManager]
        self.dataset_manager = self.app[DatasetManager]

    def _create_vanilla_hda(self, user_data=None):
        user_data = user_data or user2_data
        owner = self.user_manager.create(**user_data)
        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        return self.hda_manager.create(history=history1, dataset=dataset1)


# =============================================================================
class TestHDAManager(HDATestCase):
    def test_base(self):
        hda_model = model.HistoryDatasetAssociation
        owner = self.user_manager.create(**user2_data)
        history1 = self.history_manager.create(name="history1", user=owner)
        hda1 = self.hda_manager.create(history=history1, hid=1)
        hda2 = self.hda_manager.create(history=history1, hid=2)
        hda3 = self.hda_manager.create(history=history1, hid=3)

        self.log("should be able to query")
        hdas = self.trans.sa_session.scalars(select(hda_model)).all()
        assert self.hda_manager.list() == hdas
        assert self.hda_manager.one(filters=(hda_model.id == hda1.id)) == hda1
        assert self.hda_manager.by_id(hda1.id) == hda1
        assert self.hda_manager.by_ids([hda2.id, hda1.id]) == [hda2, hda1]

        self.log("should be able to limit and offset")
        assert self.hda_manager.list(limit=1) == hdas[0:1]
        assert self.hda_manager.list(offset=1) == hdas[1:]
        assert self.hda_manager.list(limit=1, offset=1) == hdas[1:2]

        assert self.hda_manager.list(limit=0) == []
        assert self.hda_manager.list(offset=3) == []

        self.log("should be able to order")
        assert self.hda_manager.list(order_by=sqlalchemy.desc(hda_model.create_time)) == [hda3, hda2, hda1]

    def test_create(self):
        owner = self.user_manager.create(**user2_data)

        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()

        self.log("should be able to create a new HDA with a specified history and dataset")
        hda1 = self.hda_manager.create(history=history1, dataset=dataset1)
        assert isinstance(hda1, model.HistoryDatasetAssociation)
        assert hda1 == self.trans.sa_session.get(model.HistoryDatasetAssociation, hda1.id)
        assert hda1.history == history1
        assert hda1.dataset == dataset1
        assert hda1.hid == 1

        self.log("should be able to create a new HDA with only a specified history and no dataset")
        hda2 = self.hda_manager.create(history=history1)
        assert isinstance(hda2, model.HistoryDatasetAssociation)
        assert isinstance(hda2.dataset, model.Dataset)
        assert hda2.history == history1
        assert hda2.hid == 2

        self.log("should be able to create a new HDA with no history and no dataset")
        hda3 = self.hda_manager.create(hid=None)
        assert isinstance(hda3, model.HistoryDatasetAssociation)
        assert isinstance(hda3.dataset, model.Dataset), "dataset will be auto created"
        assert hda3.history is None, "history will be None"
        assert hda3.hid is None, "should allow setting hid to None (or any other value)"

    def test_hda_annotation(self):
        owner = self.user_manager.create(**user2_data)
        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        hda1 = self.hda_manager.create(history=history1, dataset=dataset1)

        self.log("should be able to set annotation on an hda")
        annotation = "an annotation or анотація"
        self.hda_manager.annotate(hda1, annotation, user=owner)
        assert self.hda_manager.annotation(hda1) == annotation

    def test_copy_from_hda(self):
        owner = self.user_manager.create(**user2_data)
        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        hda1 = self.hda_manager.create(history=history1, dataset=dataset1)

        self.log("should be able to copy an HDA")
        hda2 = self.hda_manager.copy(hda1, history=history1)
        assert isinstance(hda2, model.HistoryDatasetAssociation)
        assert hda2 == self.trans.sa_session.get(model.HistoryDatasetAssociation, hda2.id)
        assert hda2.name == hda1.name
        assert hda2.history == hda1.history
        assert hda2.dataset == hda1.dataset
        assert hda2 != hda1

        self.log("tags should be copied between HDAs")
        tagged = self.hda_manager.create(history=history1, dataset=self.dataset_manager.create())
        tags_to_set = ["tag-one", "tag-two"]
        self.app.tag_handler.add_tags_from_list(owner, tagged, tags_to_set)
        hda2 = self.hda_manager.copy(tagged, history=history1)
        assert tagged.make_tag_string_list() == hda2.make_tag_string_list()

        self.log("annotations should be copied between HDAs")
        annotated = self.hda_manager.create(history=history1, dataset=self.dataset_manager.create())
        annotation = "( ͡° ͜ʖ ͡°)"
        self.hda_manager.annotate(annotated, annotation, user=owner)

        hda3 = self.hda_manager.copy(annotated, history=history1)
        hda3_annotation = self.hda_manager.annotation(hda3)
        assert annotation == hda3_annotation

    def test_delete(self):
        owner = self.user_manager.create(**user2_data)
        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        item1 = self.hda_manager.create(history=history1, dataset=dataset1)

        self.log("should be able to delete and undelete an hda")
        assert not item1.deleted
        assert self.hda_manager.delete(item1) == item1
        assert item1.deleted
        assert self.hda_manager.undelete(item1) == item1
        assert not item1.deleted

    def test_purge_allowed(self):
        self.trans.app.config.allow_user_dataset_purge = True

        owner = self.user_manager.create(**user2_data)
        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        item1 = self.hda_manager.create(history=history1, dataset=dataset1)

        self.log("should purge an hda if config does allow")
        assert not item1.purged
        self.hda_manager.purge(item1)
        assert item1.deleted
        assert item1.purged

    def test_purge_not_allowed(self):
        self.trans.app.config.allow_user_dataset_purge = False

        owner = self.user_manager.create(**user2_data)
        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        item1 = self.hda_manager.create(history=history1, dataset=dataset1)

        self.log("should raise an error when purging an hda if config does not allow")
        assert not item1.purged
        with self.assertRaises(exceptions.ConfigDoesNotAllowException):
            self.hda_manager.purge(item1)
        assert not item1.deleted
        assert not item1.purged

    def test_ownable(self):
        owner = self.user_manager.create(**user2_data)
        non_owner = self.user_manager.create(**user3_data)

        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        item1 = self.hda_manager.create(history=history1, dataset=dataset1)

        self.log("should be able to poll whether a given user owns an item")
        assert self.hda_manager.is_owner(item1, owner)
        assert not self.hda_manager.is_owner(item1, non_owner)

        self.log("should raise an error when checking ownership with non-owner")
        with self.assertRaises(exceptions.ItemOwnershipException):
            self.hda_manager.error_unless_owner(item1, non_owner)

        self.log("should raise an error when checking ownership with anonymous")
        with self.assertRaises(exceptions.ItemOwnershipException):
            self.hda_manager.error_unless_owner(item1, None)

        self.log("should not raise an error when checking ownership with owner")
        assert self.hda_manager.error_unless_owner(item1, owner) == item1

        self.log("should not raise an error when checking ownership with admin")
        assert self.hda_manager.error_unless_owner(item1, self.admin_user) == item1

    def test_accessible(self):
        owner = self.user_manager.create(**user2_data)
        non_owner = self.user_manager.create(**user3_data)

        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        item1 = self.hda_manager.create(history=history1, dataset=dataset1)

        self.log("(by default, dataset permissions are lax) should be accessible to all")

        for user in self.user_manager.list():
            assert self.hda_manager.is_accessible(item1, user)

        self.log("after setting a dataset to private (one user) permissions, access should be allowed for that user")
        # for this test, set restrictive access permissions
        self.dataset_manager.permissions.set_private_to_one_user(dataset1, owner)
        accessible = self.hda_manager.get_accessible(item1.id, owner, current_history=self.trans.history)
        assert accessible == item1

        self.log(
            "after setting a dataset to private (one user) permissions, access should be not allowed for other users"
        )
        with self.assertRaises(exceptions.ItemAccessibilityException):
            self.hda_manager.get_accessible(
                item1.id,
                non_owner,
                current_history=self.trans.history,
            )

        self.log(
            "a copy of a restricted dataset in another users history should be inaccessible even to "
            "the histories owner"
        )
        history2 = self.history_manager.create(name="history2", user=non_owner)
        self.trans.set_history(history2)
        item2 = self.hda_manager.copy(item1, history=history2)
        assert isinstance(item2, model.HistoryDatasetAssociation)
        with self.assertRaises(exceptions.ItemAccessibilityException):
            self.hda_manager.get_accessible(
                item2.id,
                non_owner,
                current_history=self.trans.history,
            )

        self.log("a restricted dataset cannot be accessed by anonymous users")
        anon_user = None
        self.trans.set_user(anon_user)
        history3 = self.history_manager.create(name="anon_history", user=anon_user)
        self.trans.set_history(history3)
        with self.assertRaises(exceptions.ItemAccessibilityException):
            self.hda_manager.get_accessible(
                item1.id,
                anon_user,
                current_history=self.trans.history,
            )

    def test_anon_ownership(self):
        anon_user = None
        self.trans.set_user(anon_user)

        history1 = self.history_manager.create(name="anon_history", user=anon_user)
        self.trans.set_history(history1)
        dataset1 = self.dataset_manager.create()
        item1 = self.hda_manager.create(history=history1, dataset=dataset1)

        self.log("should not raise an error when checking ownership on anonymous' own dataset")
        # need to pass the current history for comparison
        assert self.hda_manager.is_owner(item1, anon_user, current_history=self.trans.history)
        item = self.hda_manager.error_unless_owner(item1, anon_user, current_history=self.trans.history)
        assert item == item1
        item = self.hda_manager.get_owned(item1.id, anon_user, current_history=self.trans.history)
        assert item == item1

        self.log("should raise an error when checking ownership on anonymous' dataset with other user")
        non_owner = self.user_manager.create(**user3_data)
        assert not self.hda_manager.is_owner(item1, non_owner)
        with self.assertRaises(exceptions.ItemOwnershipException):
            self.hda_manager.error_unless_owner(item1, non_owner)
        with self.assertRaises(exceptions.ItemOwnershipException):
            self.hda_manager.get_owned(item1.id, non_owner)

    def test_anon_accessibility(self):
        anon_user = None
        self.trans.set_user(anon_user)

        history1 = self.history_manager.create(name="anon_history", user=anon_user)
        self.trans.set_history(history1)
        dataset1 = self.dataset_manager.create()
        item1 = self.hda_manager.create(history=history1, dataset=dataset1)

        # datasets are public by default
        assert self.hda_manager.is_accessible(item1, anon_user)
        # for this test, set restrictive access permissions
        dataset_owner = self.user_manager.create(**user3_data)
        self.dataset_manager.permissions.set_private_to_one_user(dataset1, dataset_owner)

        self.log(
            "anonymous users should not be able to access datasets within their own histories if "
            "permissions do not allow"
        )
        assert not self.hda_manager.is_accessible(item1, anon_user)
        with self.assertRaises(exceptions.ItemAccessibilityException):
            self.hda_manager.error_unless_accessible(item1, anon_user)

        self.log(
            "those users with access permissions should still be allowed access to datasets "
            "within anon users' histories"
        )
        assert self.hda_manager.is_accessible(item1, dataset_owner)

    def test_error_if_uploading(self):
        hda = self._create_vanilla_hda()

        hda.state = model.Dataset.states.OK
        self.log("should not raise an error when calling error_if_uploading and in a non-uploading state")
        assert self.hda_manager.error_if_uploading(hda) == hda

        hda.state = model.Dataset.states.UPLOAD
        self.log("should raise an error when calling error_if_uploading and in the uploading state")
        with self.assertRaises(exceptions.Conflict):
            self.hda_manager.error_if_uploading(hda)

    def test_data_conversion_status(self):
        hda = self._create_vanilla_hda()

        self.log("data conversion status should reflect state")
        assert self.hda_manager.data_conversion_status(None) == hda.conversion_messages.NO_DATA
        hda.state = model.Dataset.states.ERROR
        assert self.hda_manager.data_conversion_status(hda) == hda.conversion_messages.ERROR
        hda.state = model.Dataset.states.QUEUED
        assert self.hda_manager.data_conversion_status(hda) == hda.conversion_messages.PENDING
        hda.state = model.Dataset.states.OK
        assert self.hda_manager.data_conversion_status(hda) is None

    # def test_text_data( self ):


@mock.patch("galaxy.managers.hdas.HDASerializer.url_for", mock_url_builder)
class TestHDASerializer(HDATestCase):
    def set_up_managers(self):
        super().set_up_managers()
        self.hda_serializer = hdas.HDASerializer(self.app)

    def test_views(self):
        hda = self._create_vanilla_hda()

        self.log("should have a summary view")
        summary_view = self.hda_serializer.serialize_to_view(hda, view="summary")
        self.assertKeys(summary_view, self.hda_serializer.views["summary"])

        self.log("should have the summary view as default view")
        default_view = self.hda_serializer.serialize_to_view(hda, default_view="summary")
        self.assertKeys(default_view, self.hda_serializer.views["summary"])

        # self.log("should have a detailed view")
        # detailed_view = self.hda_serializer.serialize_to_view(hda, view="detailed")
        # self.assertKeys(detailed_view, self.hda_serializer.views["detailed"])

        # self.log("should have a extended view")
        # extended_view = self.hda_serializer.serialize_to_view(hda, view="extended")
        # self.assertKeys(extended_view, self.hda_serializer.views["extended"])

        self.log("should have a inaccessible view")
        inaccessible_view = self.hda_serializer.serialize_to_view(hda, view="inaccessible")
        self.assertKeys(inaccessible_view, self.hda_serializer.views["inaccessible"])

        # skip metadata for this test
        def is_metadata(key):
            return key == "metadata" or key.startswith("metadata_")

        self.log("should have a serializer for all serializable keys")
        for key in self.hda_serializer.serializable_keyset:
            instantiated_attribute = getattr(hda, key, None)
            assert (
                key in self.hda_serializer.serializers
                or isinstance(instantiated_attribute, self.TYPES_NEEDING_NO_SERIALIZERS)
                or is_metadata(key)
            ), f"No serializer for: {key} ({instantiated_attribute})"

    def test_views_and_keys(self):
        hda = self._create_vanilla_hda()

        self.log("should be able to use keys with views")
        serialized = self.hda_serializer.serialize_to_view(hda, view="summary", keys=["uuid"])
        self.assertKeys(serialized, self.hda_serializer.views["summary"] + ["uuid"])

        self.log("should be able to use keys on their own")
        serialized = self.hda_serializer.serialize_to_view(hda, keys=["file_path"])
        self.assertKeys(serialized, ["file_path"])

    def test_serializers(self):
        hda = self._create_vanilla_hda()
        all_keys = list(self.hda_serializer.serializable_keyset)
        serialized = self.hda_serializer.serialize(hda, all_keys, user=hda.history.user)

        self.log("everything serialized should be of the proper type")
        # base
        self.assertEncodedId(serialized["id"])
        self.assertDate(serialized["create_time"])
        self.assertDate(serialized["update_time"])

        # dataset association
        assert isinstance(serialized["dataset"], dict)
        self.assertEncodedId(serialized["dataset_id"])
        self.assertUUID(serialized["uuid"])
        assert isinstance(serialized["file_name"], str)
        assert isinstance(serialized["extra_files_path"], str)
        assert isinstance(serialized["size"], int)
        assert isinstance(serialized["file_size"], int)
        assert isinstance(serialized["nice_size"], str)
        # TODO: these should be tested w/copy
        self.assertNullableEncodedId(serialized["copied_from_history_dataset_association_id"])
        self.assertNullableEncodedId(serialized["copied_from_library_dataset_dataset_association_id"])
        self.assertNullableBasestring(serialized["info"])
        self.assertNullableBasestring(serialized["blurb"])
        self.assertNullableBasestring(serialized["peek"])
        assert isinstance(serialized["meta_files"], list)
        self.assertNullableEncodedId(serialized["parent_id"])
        assert serialized["designation"] is None
        assert isinstance(serialized["genome_build"], str)
        assert isinstance(serialized["data_type"], str)

        # hda
        self.assertEncodedId(serialized["history_id"])
        assert serialized["type_id"] == "dataset-" + serialized["id"]

        assert isinstance(serialized["resubmitted"], bool)
        assert isinstance(serialized["display_apps"], list)
        assert isinstance(serialized["display_types"], list)

        # remapped
        self.assertNullableBasestring(serialized["misc_info"])
        self.assertNullableBasestring(serialized["misc_blurb"])
        self.assertNullableBasestring(serialized["file_ext"])
        self.assertNullableBasestring(serialized["file_path"])

        # identities
        assert serialized["model_class"] == "HistoryDatasetAssociation"
        assert serialized["history_content_type"] == "dataset"
        assert serialized["hda_ldda"] == "hda"
        assert serialized["accessible"] is True
        assert serialized["api_type"] == "file"
        assert serialized["type"] == "file"

        assert isinstance(serialized["url"], str)
        assert isinstance(serialized["urls"], dict)
        assert isinstance(serialized["download_url"], str)

        self.log("serialized should jsonify well")
        self.assertIsJsonifyable(serialized)

    def test_file_name_serializers(self):
        hda = self._create_vanilla_hda()
        owner = hda.user
        keys = ["file_name"]

        self.log("file_name should be included if app configured to do so")
        # this is on by default in galaxy_mock
        assert self.app.config.expose_dataset_path
        # ... so non-admin user CAN get file_name
        serialized = self.hda_serializer.serialize(hda, keys, user=None)
        assert "file_name" in serialized
        serialized = self.hda_serializer.serialize(hda, keys, user=owner)
        assert "file_name" in serialized

        self.log("file_name should be skipped for non-admin when not exposed by config")
        self.app.config.expose_dataset_path = False
        serialized = self.hda_serializer.serialize(hda, keys, user=None)
        assert "file_name" not in serialized
        serialized = self.hda_serializer.serialize(hda, keys, user=owner)
        assert "file_name" not in serialized

        self.log("file_name should be sent for admin in either case")
        serialized = self.hda_serializer.serialize(hda, keys, user=self.admin_user)
        assert "file_name" in serialized
        self.app.config.expose_dataset_path = True
        serialized = self.hda_serializer.serialize(hda, keys, user=self.admin_user)
        assert "file_name" in serialized

    def test_serializing_inaccessible(self):
        owner = self.user_manager.create(**user2_data)
        non_owner = self.user_manager.create(**user3_data)

        history1 = self.history_manager.create(name="history1", user=owner)
        dataset1 = self.dataset_manager.create()
        item1 = self.hda_manager.create(history=history1, dataset=dataset1)

        keys_in_inaccessible_view = self.hda_serializer._view_to_keys("inaccessible")

        self.log("file_name should be included if app configured to do so")
        self.dataset_manager.permissions.set_private_to_one_user(dataset1, owner)
        # request random crap
        serialized = self.hda_serializer.serialize_to_view(item1, view="detailed", keys=["file_path"], user=non_owner)
        assert sorted(keys_in_inaccessible_view) == sorted(serialized.keys())

    # TODO: test extra_files_path as well


# =============================================================================
class TestHDADeserializer(HDATestCase):
    def set_up_managers(self):
        super().set_up_managers()
        self.hda_deserializer = hdas.HDADeserializer(self.app)

    def test_deserialize_delete(self):
        hda = self._create_vanilla_hda()

        self.log("should raise when deserializing deleted from non-bool")
        assert not hda.deleted
        with self.assertRaises(exceptions.RequestParameterInvalidException):
            self.hda_deserializer.deserialize(hda, {"deleted": None})
        assert not hda.deleted
        self.log("should be able to deserialize deleted from True")
        self.hda_deserializer.deserialize(hda, {"deleted": True})
        assert hda.deleted
        self.log("should be able to reverse by deserializing deleted from False")
        self.hda_deserializer.deserialize(hda, {"deleted": False})
        assert not hda.deleted

    def test_deserialize_purge(self):
        hda = self._create_vanilla_hda()

        self.log("should raise when deserializing purged from non-bool")
        with self.assertRaises(exceptions.RequestParameterInvalidException):
            self.hda_deserializer.deserialize(hda, {"purged": None})
        assert not hda.purged
        self.log("should be able to deserialize purged from True")
        self.hda_deserializer.deserialize(hda, {"purged": True})
        assert hda.purged
        # TODO: should this raise an error?
        self.log("should NOT be able to deserialize purged from False (will remain True)")
        self.hda_deserializer.deserialize(hda, {"purged": False})
        assert hda.purged

    def test_deserialize_visible(self):
        hda = self._create_vanilla_hda()

        self.log("should raise when deserializing from non-bool")
        assert hda.visible
        with self.assertRaises(exceptions.RequestParameterInvalidException):
            self.hda_deserializer.deserialize(hda, {"visible": "None"})
        assert hda.visible
        self.log("should be able to deserialize from False")
        self.hda_deserializer.deserialize(hda, {"visible": False})
        assert not hda.visible
        self.log("should be able to reverse by deserializing from True")
        self.hda_deserializer.deserialize(hda, {"visible": True})
        assert hda.visible

    def test_deserialize_genome_build(self):
        hda = self._create_vanilla_hda()

        assert isinstance(hda.dbkey, str)
        self.log('should deserialize to "?" from None')
        self.hda_deserializer.deserialize(hda, {"genome_build": None})
        assert hda.dbkey == "?"
        self.log("should raise when deserializing from non-string")
        with self.assertRaises(exceptions.RequestParameterInvalidException):
            self.hda_deserializer.deserialize(hda, {"genome_build": 12})
        self.log("should be able to deserialize from unicode")
        date_palm = "نخيل التمر"
        self.hda_deserializer.deserialize(hda, {"genome_build": date_palm})
        assert hda.dbkey == date_palm
        self.log("should be deserializable from empty string")
        self.hda_deserializer.deserialize(hda, {"genome_build": ""})
        assert hda.dbkey == ""

    def test_deserialize_name(self):
        hda = self._create_vanilla_hda()

        self.log("should raise when deserializing from non-string")
        with self.assertRaises(exceptions.RequestParameterInvalidException):
            self.hda_deserializer.deserialize(hda, {"name": True})
        self.log("should raise when deserializing from None")
        with self.assertRaises(exceptions.RequestParameterInvalidException):
            self.hda_deserializer.deserialize(hda, {"name": None})
        # self.log( 'should deserialize to empty string from None' )
        # self.hda_deserializer.deserialize( hda, { 'name': None } )
        # assert hda.name == ""
        self.log("should be able to deserialize from unicode")
        olive = "ελιά"
        self.hda_deserializer.deserialize(hda, {"name": olive})
        assert hda.name == olive
        self.log("should be deserializable from empty string")
        self.hda_deserializer.deserialize(hda, {"name": ""})
        assert hda.name == ""

    def test_deserialize_info(self):
        hda = self._create_vanilla_hda()

        self.log("should raise when deserializing from non-string")
        with self.assertRaises(exceptions.RequestParameterInvalidException):
            self.hda_deserializer.deserialize(hda, {"info": True})
        self.log("should raise when deserializing from None")
        with self.assertRaises(exceptions.RequestParameterInvalidException):
            self.hda_deserializer.deserialize(hda, {"info": None})
        self.log("should be able to deserialize from unicode")
        rice = "飯"
        self.hda_deserializer.deserialize(hda, {"info": rice})
        assert hda.info == rice
        self.log("should be deserializable from empty string")
        self.hda_deserializer.deserialize(hda, {"info": ""})
        assert hda.info == ""


# =============================================================================
class TestHDAFilterParser(HDATestCase):
    def set_up_managers(self):
        super().set_up_managers()
        self.filter_parser = hdas.HDAFilterParser(self.app)

    def test_parsable(self):
        self.log("the following filters should be parsable")
        # base
        self.assertORMFilter(self.filter_parser.parse_filter("id", "in", [1, 2]))
        encoded_id_string = ",".join(self.app.security.encode_id(id_) for id_ in [1, 2])
        self.assertORMFilter(self.filter_parser.parse_filter("encoded_id", "in", encoded_id_string))
        self.assertORMFilter(self.filter_parser.parse_filter("create_time", "le", "2015-03-15"))
        self.assertORMFilter(self.filter_parser.parse_filter("create_time", "ge", "2015-03-15"))
        self.assertORMFilter(self.filter_parser.parse_filter("update_time", "le", "2015-03-15"))
        self.assertORMFilter(self.filter_parser.parse_filter("update_time", "ge", "2015-03-15"))
        # purgable
        self.assertORMFilter(self.filter_parser.parse_filter("deleted", "eq", True))
        self.assertORMFilter(self.filter_parser.parse_filter("purged", "eq", True))
        # dataset asociation
        self.assertORMFilter(self.filter_parser.parse_filter("name", "eq", "wot"))
        self.assertORMFilter(self.filter_parser.parse_filter("name", "contains", "wot"))
        self.assertORMFilter(self.filter_parser.parse_filter("name", "like", "wot"))
        self.assertORMFilter(self.filter_parser.parse_filter("state", "eq", "ok"))
        self.assertORMFilter(self.filter_parser.parse_filter("state", "in", ["queued", "running"]))
        self.assertORMFilter(self.filter_parser.parse_filter("visible", "eq", True))
        # taggable
        self.assertORMFunctionFilter(self.filter_parser.parse_filter("tag", "eq", "wot"))
        self.assertORMFunctionFilter(self.filter_parser.parse_filter("tag", "has", "wot"))
        # genomebuild
        self.assertFnFilter(self.filter_parser.parse_filter("genome_build", "eq", "wot"))
        self.assertFnFilter(self.filter_parser.parse_filter("genome_build", "contains", "wot"))
        # data_type
        self.assertFnFilter(self.filter_parser.parse_filter("data_type", "eq", "wot"))
        self.assertFnFilter(self.filter_parser.parse_filter("data_type", "isinstance", "wot"))
        # annotatable
        self.assertFnFilter(self.filter_parser.parse_filter("annotation", "has", "wot"))


#     def test_genome_build_filters( self ):
#         pass

#     def test_data_type_filters( self ):
#         pass
