# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['pytest_cache_assert', 'pytest_cache_assert._check_assert']

package_data = \
{'': ['*']}

install_requires = \
['attrs-strict>=0.2',
 'beartype>=0.10.0',
 'dictdiffer>=0.9',
 'pendulum>=2.1.2',
 'punq>=0.6.2',
 'pytest>=5.0.0']

entry_points = \
{'pytest11': ['pytest_cache_assert = pytest_cache_assert.plugin']}

setup_kwargs = {
    'name': 'pytest-cache-assert',
    'version': '1.3.5',
    'description': 'Cache assertion data to simplify regression testing of complex serializable data',
    'long_description': '# pytest_cache_assert\n\nCache assertion data to simplify regression testing of complex serializable data\n\n## Installation\n\n`poetry add pytest_assert_check --dev`\n\n## Quick Start\n\nThe primary use case of this package is regression testing of large, serializable dictionaries, such as from an API under development.\n\nYou may have parameterized test cases where you need to assert that the created dictionary stays the same, but you don’t want to manually generate the expected fields and values to compare. Instead you can capture a snapshot of the serialized data and cache the result then use the cached data to check for consistency in repeated test runs. The cached files should be checked into version control, which can be very useful as documentation\n\nThis package can minimize test case logic, while improving test thoroughness\n\nThis project was heavily inspired by the excellent [pytest-recording](https://github.com/kiwicom/pytest-recording)\n\n### Alternatives\n\n- [pytest-recording](https://github.com/kiwicom/pytest-recording): this is the package I use and highly recommend for recording and replaying **external** API communication so that API requests only need to be made once for unit testing (i.e. recording API responses from Github\'s API called from a test suite)\n- [pytest-snapshot](https://pypi.org/project/pytest-snapshot/): I only found this package after already releasing a 1.0.0 version of `pytest_assert_cache`. This package can be more configurable with a user-specified serializer and might be a good alternative. See their documentation for more info\n- [snapshottest](https://github.com/syrusakbary/snapshottest): This was another find after releasing a 1.0.0 version and would probably be **a good alterantive for most users**\n  - `pytest-snapshot` is much more configurable, has many more users, and is a better name\n    - I really like the ability to quickly regenerate the cached files with [--snapshot-update](https://github.com/syrusakbary/snapshottest/blob/master/snapshottest/pytest.py)\n    - [There is some interesting discussion on how best to handle fields that change between tests](https://github.com/syrusakbary/snapshottest/issues/21)\n- [dirty-equals](https://github.com/samuelcolvin/dirty-equals): broadly check values (i.e. `assert result == {\'counter\': IsPositiveInt, ...}`, etc.) rather than accessing and checking each field individual, which makes test easier to write and output errors easier to review\n\n### Basic Example\n\nYou\'ve created a new project called `package_a` with one file `package_a/source_file.py` and test `tests/test_file.py`\n\n```py\n"""package_a/source_file.py"""\n\nimport sys\nfrom datetime import datetime\nfrom typing import Any, Dict, List, Optional\n\nfrom beartype import beartype\nfrom pydantic import BaseModel\n\n\nclass User(BaseModel):  # noqa: H601\n    """Example from pydantic documentation."""\n\n    id: int  # noqa: A003,VNE003\n    name = \'John Doe\'\n    signup_ts: Optional[datetime] = None\n    friends: List[int] = []\n\n\n@beartype\ndef create_data(name: str) -> Dict[str, Any]:\n    """Arbitrary function that returns a dictionary.\n\n    This demonstration uses pydantic, but any dictionary can be tested!\n\n    """\n    return User(id=sys.maxsize, name=name).dict()\n```\n\n```py\n"""tests/test_file.py"""\n\nimport pytest\n\nfrom package_a.source_file import create_data\n\n\n@pytest.mark.parametrize(\'name\', [\'Test Name 1\', \'Test Name 2\'])\ndef test_create_data(name, assert_against_cache):\n    """Basic test of create_data()."""\n    result = create_data(name=name)\n\n    # One could manually create the expected dictionary\n    cache = {\'id\': 9223372036854775807, \'signup_ts\': None, \'friends\': [], \'name\': name}\n    assert result == cache\n    # ----------------------------------------------------------------------------------\n    # Or utilize the pytest_cache_assert fixture to compare against the last cached version\n    assert_against_cache(result)\n```\n\n`pytest_cache_assert` will automatically create: `tests/cache-assert/source_file/test_file/test_create_data-[Test Name 1].json` (and `test_create_data[Test Name 2].json`) for each of the parameters when first run by caching the `result`. Below is the example for `test_create_data-[Test Name 1].json`\n\n```json\n{\n  "_info": [\n    {\n      "func_args": {\n        "name": "Test Name 1"\n      },\n      "test_file": "test_readme.py",\n      "test_name": "test_create_data"\n    }\n  ],\n  "_json": {\n    "friends": [],\n    "id": 9223372036854775807,\n    "name": "Test Name 1",\n    "signup_ts": null\n  }\n}\n```\n\nThe cached JSON files must be checked into version control and if needed, can be manually edited or deleted so that they will be regenerated when the test suite is next run\n\n### More Examples\n\nIn your cached dictionary, you may have variable values with more complex logic to verify, such as dates, UUIDs, etc. These can be selectively ignored, matched-if-null, or some other user-specified check:\n\n```py\n"""tests/test_main.py."""\n\nfrom uuid import uuid4\nfrom datetime import datetime, timedelta\n\nfrom pytest_cache_assert import KeyRule, check_suppress, check_type, Wildcards\n\n\ndef test_assert_against_cache_key_rules(assert_against_cache):\n    """Demonstrate use of `key_rules`."""\n    now = datetime.now()\n    cached_data = {\n      \'date\': str(now),\n      {\'nested\': {\'uuid\': str(uuid4())}},\n      {\'ignored\': {\'a\': 1, \'b\': 2}},\n    }\n    test_data = {\n      \'date\': str(now + timedelta(hours=3)),\n      {\'nested\': {\'uuid\': str(uuid4())}},\n      {\'ignored\': {\'recursively\': {\'a\': {\'b\': {\'c\': 1}}}}},\n    }\n\n    key_rules = [\n      # Suppress keys \'ignored.a\' and \'ignored.b\' with the SINGLE wildcard,\n      #   which aren\'t present in the test-data and would otherwise error\n      KeyRule(pattern=[\'ignored\', Wildcards.SINGLE], func=check_suppress),\n      # The pattern can also recursively apply to data below\n      KeyRule(\n        pattern=[\'ignored\', \'recursively\', Wildcards.RECURSIVELY],\n        func=check_suppress,\n      ),\n      # Instead of suppressing, the type can be coerced from the string and verified\n      #   This is useful for datetime or UUID\'s where the string will be different,\n      #   but both values are the same type\n      KeyRule(pattern=[\'date\'], func=check_type),\n      KeyRule(pattern=[\'nested\', \'uuid\'], func=check_type),\n      # Custom functions can also be specified to check a datetime format, etc.\n      #   The function must accept the keyword arguments \'old\' and \'new\'\n    ]\n\n    # In this example, the cache file has been deleted, so first call will recreate it\n    assert_against_cache(cached_data)\n    # Then this line demonstrates that key_rules will suppress the errors\n    assert_against_cache(test_data, key_rules=key_rules)\n\n    # While without key rules, an AssertionError is raised\n    with pytest.raises(AssertionError):\n        assert_against_cache(test_data)\n```\n\nOr you may want to write your own custom checks against the serialized data, such as with Cerberus or another library. This is possible with the `validator` callable. The default validator is a no-op and that may be replaced with any custom function that raises an Exception on error.\n\n```py\n"""tests/test_main.py."""\n\nimport re\n\nimport pytest\nfrom beartype import beartype\nfrom cerberus import Validator\nfrom cerberus.schema import SchemaError\n\n\n@beartype\ndef cerberus_validator(test_data) -> None:\n    """Cerberus custom validator example."""\n    validator = Validator({\'result\': {\'type\': \'int\'}})\n    assert validator.validate(test_data)\n\n\ndef test_assert_against_cache_validator(assert_against_cache):\n    """Test the validator."""\n    expected = re.escape("{\'result\': [{\'type\': [\'Unsupported types: int\']}]}")\n\n    with pytest.raises(SchemaError, match=expected):\n        assert_against_cache({\'result\': False}, validator=cerberus_validator)  # act\n```\n\n### Even More Example\n\nFor more examples, see [Scripts](https://github.com/kyleking/pytest_cache_assert/scripts) or [Tests](https://github.com/kyleking/pytest_cache_assert/tests)\n\n## Global Configuration Options\n\n- See `AssertConfig` in `plugin.py` for configuration options and more information\n  - `cache_dir_rel_path`: set a custom relative path from the `tests/` directory. Default is `assert-cache/`\n  - `extra_ser_rules`: additional serialization rules that can be used generically on arbitrary data\n\n```py\nimport pytest\n\nfrom pytest_cache_assert.plugin import AssertConfig\n\n\n@pytest.fixture(scope=\'module\')\ndef cache_assert_config():\n    return AssertConfig(cache_dir_rel_path=\'custom/cache/dir\')\n```\n\n## Roadmap\n\nSee the `Open Issues` and `Milestones` for current status and [./docs/CODE_TAG_SUMMARY.md](./docs/CODE_TAG_SUMMARY.md) for annotations in the source code.\n\nFor release history, see the [./docs/CHANGELOG.md](./docs/CHANGELOG.md)\n\n### Planned Global Configuration Options\n\nThese are ideas for future options that are not currently implemented, but could be if there is enough interest:\n\n- PLANNED: Consider a record mode that will always-write to regenerate the cache while working on development\n  - The other edge case where a `mode` might be helpful is when file names or test names are changed and the cache metadata has too many duplicates and needs to be refreshed. Maybe a `rewrite_metadata` setting would be useful with options: `Always`, `Once` (Default), or `Never`\n  - Note that errors where the same test is appending to the metadata are problems with the code and should not necessarily need configuration. The only exception would be hypothesis testing where the inputs could be variable. In this case, a function argument to turn off metadata would be useful (rather than a global config)\n    - FIXME: Don\'t store variable datetime in the func_args!\n- PLANNED: [Provide CLI arguments like `pytest-recording`](https://github.com/kiwicom/pytest-recording/blob/484bb887dd43fcaf44149160d57b58a7215e2c8a/src/pytest_recording/plugin.py#L37-L70) (`request.config.getoption("--record-mode") or "none"`) for one-time changes to configuration\n- PLANNED: Consider filters to prevent secrets from being cached: `filter_headers=[[\'authorization\', \'id\'], [\'authorization\', \'cookies\']]`\n\n### Other Planned Features\n\n- PLANNED: Consider inline corrections to cached data like this [feature from Jest](https://jestjs.io/docs/snapshot-testing#inline-snapshots)\n  - Show the diff between the cached data and the test data? Would need to look for a package that can show the comparison between two dictionaries in terminal\n    - [ydiff](https://github.com/ymattw/ydiff) might be a great choice, but I would need to support git (and ask if anyone needs SVN support) to write the change and compare. Probably better to more directly ask if the user wants the test case (shown by name and maybe a brief list of changes) to be replaced or not\n- PLANNED: [Add tips from Jest on best practices](https://jestjs.io/docs/snapshot-testing#best-practices) -- treat snapshots as code, etc.\n\n## Contributing\n\nSee the Developer Guide, Contribution Guidelines, etc\n\n- [./docs/DEVELOPER_GUIDE.md](./docs/DEVELOPER_GUIDE.md)\n- [./docs/STYLE_GUIDE.md](./docs/STYLE_GUIDE.md)\n- [./docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md)\n- [./docs/CODE_OF_CONDUCT.md](./docs/CODE_OF_CONDUCT.md)\n- [./docs/SECURITY.md](./docs/SECURITY.md)\n\n## License\n\n[LICENSE](https://github.com/kyleking/pytest_cache_assert/LICENSE)\n',
    'author': 'Kyle King',
    'author_email': 'dev.act.kyle@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/kyleking/pytest_cache_assert',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.7.6,<4.0.0',
}


setup(**setup_kwargs)
