diff --git a/.github/workflows/pytest.yml b/.github/workflows/coverage.yml similarity index 78% rename from .github/workflows/pytest.yml rename to .github/workflows/coverage.yml index 19c1996..10e7ca5 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/coverage.yml @@ -28,5 +28,7 @@ jobs: cache: 'poetry' - name: Install dependencies run: poetry install - - name: Run pytest - run: poetry run pytest + - name: Run pytest via coverage + run: poetry run coverage run --source=caracara_filters -m pytest -s + - name: Get Coverage Report + run: poetry run coverage report diff --git a/caracara_filters/dialects/__init__.py b/caracara_filters/dialects/__init__.py index 82309a3..2ac1a9b 100644 --- a/caracara_filters/dialects/__init__.py +++ b/caracara_filters/dialects/__init__.py @@ -10,14 +10,17 @@ 'rebase_filters_on_default', 'DIALECTS', 'HOST_FILTERS', + 'RTR_FILTERS', ] from caracara_filters.dialects._base import default_filter from caracara_filters.dialects._merge import rebase_filters_on_default from caracara_filters.dialects._base import BASE_FILTERS from caracara_filters.dialects.hosts import HOST_FILTERS +from caracara_filters.dialects.rtr import RTR_FILTERS DIALECTS = { "base": BASE_FILTERS, "hosts": HOST_FILTERS, + "rtr": RTR_FILTERS, } diff --git a/caracara_filters/dialects/rtr.py b/caracara_filters/dialects/rtr.py new file mode 100644 index 0000000..7354f19 --- /dev/null +++ b/caracara_filters/dialects/rtr.py @@ -0,0 +1,66 @@ +"""Caracara Filters: RTR Dialect. + +This module contains filters that are specific to the RTR API. +""" +from functools import partial +from typing import Any, Dict + +from caracara_filters.dialects._base import default_filter +from caracara_filters.dialects._base import rebase_filters_on_default +from caracara_filters.validators import options_validator + + +RTR_COMMANDS = [ + "cat", + "cd", + "clear", + "cp", + "csrutil", + "cswindiag", + "encrypt", + "env", + "eventlog", + "filehash", + "get", + "getsid", + "history", + "ifconfig", + "ipconfig", + "kill", + "ls", + "map", + "memdump", + "mkdir", + "mount", + "mv", + "netstat", + "ps", + "put", + "put-and-run", + "reg", + "restart", + "rm", + "run", + "runscript", + "shutdown", + "tar", + "umount", + "unmap", + "update", + "users", + "xmemdump", + "zip", +] + +rtr_base_command_filter = { + "fql": "base_command", + "validator": partial(options_validator, RTR_COMMANDS, case_sensitive=False), + "help": "Filter RTR audit logs by base command.", +} + +RTR_FILTERS: Dict[str, Dict[str, Any]] = { + "basecommand": rtr_base_command_filter, + "command": rtr_base_command_filter, +} + +rebase_filters_on_default(default_filter, RTR_FILTERS) diff --git a/caracara_filters/fql.py b/caracara_filters/fql.py index 1d5f9f4..b441071 100644 --- a/caracara_filters/fql.py +++ b/caracara_filters/fql.py @@ -55,24 +55,25 @@ def __init__(self, dialect: str = 'base'): self.dialect: str = dialect self.filters: Dict[str, FilterArgs] = {} - def add_filter(self, new_filter: FilterArgs): + def add_filter(self, new_filter: FilterArgs) -> str: """Add a new filter to the FQLGenerator object.""" filter_id = str(uuid4()) self.filters[filter_id] = new_filter + return filter_id def remove_filter(self, filter_id: str): """Remove a filter from the current FQL Generator object by filter ID.""" if filter_id in self.filters: del self.filters[filter_id] else: - raise ValueError(f"The filter with ID {filter_id} does not exist in this object.") + raise KeyError(f"The filter with ID {filter_id} does not exist in this object.") def create_new_filter( self, filter_name: str, initial_value: Any, initial_operator: Optional[str] = None, - ) -> None: + ) -> str: """Create a new FQL filter and store it, alongside its arguments, inside this object.""" # For compatability reasons, we must send all filter names to lower case. filter_name = filter_name.lower() @@ -135,7 +136,7 @@ def create_new_filter( value=transformed_value, operator=initial_operator ) - self.add_filter(filter_args) + return self.add_filter(filter_args) def create_new_filter_from_kv_string(self, key_string: str, value) -> str: """ diff --git a/caracara_filters/validators/options.py b/caracara_filters/validators/options.py index 354f35e..8eb984e 100644 --- a/caracara_filters/validators/options.py +++ b/caracara_filters/validators/options.py @@ -6,7 +6,7 @@ from typing import Any, List -def options_validator(options: List[Any], chosen_option: Any) -> bool: +def options_validator(options: List[Any], chosen_option: Any, case_sensitive: bool = True) -> bool: """Check if an option passed to the filter is within a pre-set list of options. If a list of choices is passed in, we bail out if any of those items are not in the list. @@ -14,11 +14,41 @@ def options_validator(options: List[Any], chosen_option: Any) -> bool: Technically, we should probably test whether this item is multivariate here. However, we perform that check in fql.py within the FQLGenerator function. + + Scenarios to cover here: + - Case sensitive and a list of strings + - Case sensitive and a list of non-strings + - Case sensitive and a string + - Non-case sensitive and a list of strings + - Non-case sensitive and a list of non-strings + - Non-case sensitive and a string """ + if isinstance(chosen_option, str): + return chosen_option in options if case_sensitive \ + else chosen_option.lower() in options + if isinstance(chosen_option, list): for chosen_option_item in chosen_option: - if chosen_option_item not in options: + if ( + isinstance(chosen_option_item, str) and + case_sensitive and + chosen_option_item not in options + ): + return False + + if ( + isinstance(chosen_option_item, str) and + not case_sensitive and + chosen_option_item.lower() not in options + ): return False + + if ( + not isinstance(chosen_option_item, str) and + chosen_option_item not in options + ): + return False + return True return chosen_option in options diff --git a/poetry.lock b/poetry.lock index 9afdb57..4b61b01 100644 --- a/poetry.lock +++ b/poetry.lock @@ -31,6 +31,78 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coverage" +version = "7.2.7" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, + {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, + {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, + {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, + {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, + {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, + {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, + {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, + {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, + {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, + {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, + {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, + {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, + {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, + {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, + {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, + {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, + {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, +] + +[package.extras] +toml = ["tomli"] + [[package]] name = "dill" version = "0.3.7" @@ -553,4 +625,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7.2" -content-hash = "5559d0c13d705f1b6f8e3bb7fe00610bf4ce71b02c9a099005887517b3c1d8cf" +content-hash = "420a99bba0d39eea0df8ca0da4246ab7042c9674e2a5d42d14660e9c4680614a" diff --git a/pyproject.toml b/pyproject.toml index a0aa9cf..f60afcd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "caracara-filters" -version = "0.1.0" +version = "0.1.1" description = "FQL generation engine for Caracara" authors = ["Chris Hammond "] license = "MIT" @@ -32,9 +32,10 @@ classifiers = [ python = "^3.7.2" [tool.poetry.group.dev.dependencies] +coverage = "^7.0" flake8 = "^5.0" -pydocstyle = "^6.3.0" freezegun = "^1.2.2" +pydocstyle = "^6.3.0" pylint = "^2.17.5" pytest = "^7.4.0" diff --git a/tests/test_host_filters.py b/tests/test_host_filters.py index 98fc2b1..babcc08 100644 --- a/tests/test_host_filters.py +++ b/tests/test_host_filters.py @@ -106,3 +106,58 @@ def test_multi_kv_string(): fql_generator.create_new_filter_from_kv_string("LastSeen__GTE", "-29m") fql = fql_generator.get_fql() assert fql == "platform_name: ['Windows','Linux']+last_seen: >='2019-12-31T23:31:13Z'" + + +def test_non_multivariate_list_exception(): + fql_generator = FQLGenerator(dialect='hosts') + with pytest.raises(TypeError): + fql_generator.create_new_filter("LastSeen", ['-30m', '-60h']) + + +def test_incorrect_list_type_exception(): + fql_generator = FQLGenerator(dialect='hosts') + with pytest.raises(TypeError): + fql_generator.create_new_filter("hostname", [0, 'some host']) + + +def test_validation_failure(): + fql_generator = FQLGenerator(dialect='hosts') + with pytest.raises(ValueError): + fql_generator.create_new_filter("LastSeen", '^123') + + +def test_incorrect_containment_option(): + fql_generator = FQLGenerator(dialect='hosts') + with pytest.raises(ValueError): + fql_generator.create_new_filter("contained", "some containment option") + + +def test_incorrect_relative_timestamp_format(): + fql_generator = FQLGenerator(dialect='hosts') + with pytest.raises(ValueError): + fql_generator.create_new_filter("firstseen", "-80x") + + +@freeze_time("2023-08-15 01:02:03") +def test_last_seen_day(): + fql_generator = FQLGenerator(dialect='hosts') + fql_generator.create_new_filter("firstseen", "-2d") + fql = fql_generator.get_fql() + assert fql == "first_seen: >='2023-08-13T01:02:03Z'" + + +@freeze_time("2023-08-15 01:02:03") +def test_first_seen_add_time(): + # This is a ridiculous scenario, but we support it anyway. + fql_generator = FQLGenerator(dialect='hosts') + fql_generator.create_new_filter("firstseen", "+2d") + fql = fql_generator.get_fql() + assert fql == "first_seen: >='2023-08-17T01:02:03Z'" + + +@freeze_time("2023-08-15 01:02:03") +def test_last_seen_relative_seconds(): + fql_generator = FQLGenerator(dialect='hosts') + fql_generator.create_new_filter("lastseen", "-63s") + fql = fql_generator.get_fql() + assert fql == "last_seen: >='2023-08-15T01:01:00Z'" diff --git a/tests/test_meta.py b/tests/test_meta.py new file mode 100644 index 0000000..7ea1538 --- /dev/null +++ b/tests/test_meta.py @@ -0,0 +1,49 @@ +import pytest + +from caracara_filters import FQLGenerator + + +def test_non_existent_dialect(): + with pytest.raises(ValueError): + FQLGenerator(dialect='not a module') + + +def test_filter_delete_real_id(): + fql_generator = FQLGenerator(dialect='base') + filter_id = fql_generator.create_new_filter("name", "testtest") + fql = fql_generator.get_fql() + assert fql == "name: 'testtest'" + assert filter_id is not None + fql_generator.remove_filter(filter_id) + assert fql_generator.filters == {} + + +def test_filter_delete_bad_id(): + fql_generator = FQLGenerator() + with pytest.raises(KeyError): + fql_generator.remove_filter("non-existent-filter-id") + + +def test_bad_data_type(): + fql_generator = FQLGenerator(dialect='base') + with pytest.raises(TypeError): + fql_generator.create_new_filter("name", 123) + + +def test_nullable_filter(): + # TODO: write a test here once we have a nullable filter defined + # (and validation logic to handle this) + pass + + +def test_bool_filter(): + # TODO: write a test once we have a boolean filter defined + pass + + +def test_str_dunder(): + fql_generator = FQLGenerator(dialect='base') + fql_generator.create_new_filter("name", "testname") + fql = fql_generator.get_fql() + assert fql == str(fql_generator) + assert fql == "name: 'testname'" diff --git a/tests/test_rtr_filters.py b/tests/test_rtr_filters.py new file mode 100644 index 0000000..adede84 --- /dev/null +++ b/tests/test_rtr_filters.py @@ -0,0 +1,23 @@ +import pytest + +from caracara_filters import FQLGenerator + + +def test_base_command_fql(): + fql_generator = FQLGenerator(dialect='rtr') + fql_generator.create_new_filter("command", "put") + fql = fql_generator.get_fql() + assert fql == "base_command: 'put'" + + +def test_mixed_case_base_command_fql(): + fql_generator = FQLGenerator(dialect='rtr') + fql_generator.create_new_filter("command", "GeT") + fql = fql_generator.get_fql() + assert fql == "base_command: 'GeT'" + + +def test_invalid_operator_base_command(): + fql_generator = FQLGenerator(dialect='rtr') + with pytest.raises(ValueError): + fql_generator.create_new_filter("command", "ls", "GTE")