prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  An exception is raised when calling MockDatastoreService.set_value() with a ttl
  ## SUMMARY
  
  When using `MockDatastoreService.set_value()` for unit testing (e.g. via `BaseActionTestCase`), a `ValueError` exception will be raised if `ttl` argument is specified.
  
  
  ### STACKSTORM VERSION
  
  `st2 3.1.0, on Python 3.6.8`
  
  ### OS, environment, install method
  
  Custom install, but not relevant to this case, see below.
  
  ## Steps to reproduce the problem
  
  See [code](https://github.com/StackStorm/st2/blob/6d1809a4bb577e117baa00f249757284db9c6e76/st2tests/st2tests/mocks/datastore.py#L108) for `MockDatastoreService.set_value()`. If `ttl` argument is specified, `ValueError` will explicitly be raised.
  
  ```python
  class MockDatastoreService(BaseDatastoreService):
      # ...
      def set_value(self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False):
          """
          Store a value in a dictionary which is local to this class.
          """
          if ttl:
              raise ValueError('MockDatastoreService.set_value doesn\'t support "ttl" argument')
          # ...
  ```
  
  
  ## Expected Results
  
  Both `action_service.set_value()` and  `sensor_service.set_value()` support `ttl` argument. Although I understand that this argument cannot be honored in a mock situation, `MockDatastoreService`  should be consistent with the behavior of the class it is mocking - perhaps fire a warning instead of an Exception?
  
  ## Actual Results
  
  An exception is returned while running tests:
  ```python
  ValueError: MockDatastoreService.set_value doesn't support "ttl" argument
  ```
  It is not possible to test actions or sensors that use `xxx.set_value()` with a `ttl` argument.
  
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `st2tests/st2tests/mocks/datastore.py`
  Content:
  ```
  1 # Copyright 2020 The StackStorm Authors.
  2 # Copyright 2019 Extreme Networks, Inc.
  3 #
  4 # Licensed under the Apache License, Version 2.0 (the "License");
  5 # you may not use this file except in compliance with the License.
  6 # You may obtain a copy of the License at
  7 #
  8 #     http://www.apache.org/licenses/LICENSE-2.0
  9 #
  10 # Unless required by applicable law or agreed to in writing, software
  11 # distributed under the License is distributed on an "AS IS" BASIS,
  12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 # See the License for the specific language governing permissions and
  14 # limitations under the License.
  15 
  16 """
  17 Mock classes for use in pack testing.
  18 """
  19 
  20 from __future__ import absolute_import
  21 from st2common.constants.keyvalue import SYSTEM_SCOPE
  22 from st2common.services.datastore import BaseDatastoreService
  23 from st2client.models.keyvalue import KeyValuePair
  24 
  25 __all__ = ["MockDatastoreService"]
  26 
  27 
  28 class MockDatastoreService(BaseDatastoreService):
  29     """
  30     Mock DatastoreService for use in testing.
  31     """
  32 
  33     def __init__(self, logger, pack_name, class_name, api_username=None):
  34         self._pack_name = pack_name
  35         self._class_name = class_name
  36         self._username = api_username or "admin"
  37 
  38         # Holds mock KeyValuePair objects
  39         # Key is a KeyValuePair name and value is the KeyValuePair object
  40         self._datastore_items = {}
  41 
  42     ##################################
  43     # General methods
  44     ##################################
  45 
  46     def get_user_info(self):
  47         """
  48         Retrieve information about the current user which is authenticated against StackStorm and
  49         used to perform other datastore operations via the API.
  50 
  51         :rtype: ``dict``
  52         """
  53         result = {
  54             "username": self._username,
  55             "rbac": {"is_admin": True, "enabled": True, "roles": ["admin"]},
  56             "authentication": {"method": "authentication token", "location": "header"},
  57         }
  58 
  59         return result
  60 
  61     ##################################
  62     # Methods for datastore management
  63     ##################################
  64 
  65     def list_values(self, local=True, prefix=None):
  66         """
  67         Return a list of all values stored in a dictionary which is local to this class.
  68         """
  69         key_prefix = self._get_full_key_prefix(local=local, prefix=prefix)
  70 
  71         if not key_prefix:
  72             return list(self._datastore_items.values())
  73 
  74         result = []
  75         for name, kvp in self._datastore_items.items():
  76             if name.startswith(key_prefix):
  77                 result.append(kvp)
  78 
  79         return result
  80 
  81     def get_value(self, name, local=True, scope=SYSTEM_SCOPE, decrypt=False):
  82         """
  83         Return a particular value stored in a dictionary which is local to this class.
  84         """
  85         name = self._get_full_key_name(name=name, local=local)
  86 
  87         if name not in self._datastore_items:
  88             return None
  89 
  90         kvp = self._datastore_items[name]
  91         return kvp.value
  92 
  93     def set_value(
  94         self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False
  95     ):
  96         """
  97         Store a value in a dictionary which is local to this class.
  98         """
  99         if ttl:
  100             raise ValueError(
  101                 'MockDatastoreService.set_value doesn\'t support "ttl" argument'
  102             )
  103 
  104         name = self._get_full_key_name(name=name, local=local)
  105 
  106         instance = KeyValuePair()
  107         instance.id = name
  108         instance.name = name
  109         instance.value = value
  110 
  111         self._datastore_items[name] = instance
  112         return True
  113 
  114     def delete_value(self, name, local=True, scope=SYSTEM_SCOPE):
  115         """
  116         Delete a value from a dictionary which is local to this class.
  117         """
  118         name = self._get_full_key_name(name=name, local=local)
  119 
  120         if name not in self._datastore_items:
  121             return False
  122 
  123         del self._datastore_items[name]
  124         return True
  125
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/st2tests/st2tests/mocks/datastore.py b/st2tests/st2tests/mocks/datastore.py
  --- a/st2tests/st2tests/mocks/datastore.py
  +++ b/st2tests/st2tests/mocks/datastore.py
  @@ -34,6 +34,7 @@
           self._pack_name = pack_name
           self._class_name = class_name
           self._username = api_username or "admin"
  +        self._logger = logger
   
           # Holds mock KeyValuePair objects
           # Key is a KeyValuePair name and value is the KeyValuePair object
  @@ -96,10 +97,6 @@
           """
           Store a value in a dictionary which is local to this class.
           """
  -        if ttl:
  -            raise ValueError(
  -                'MockDatastoreService.set_value doesn\'t support "ttl" argument'
  -            )
   
           name = self._get_full_key_name(name=name, local=local)
   
  @@ -107,6 +104,11 @@
           instance.id = name
           instance.name = name
           instance.value = value
  +        if ttl:
  +            self._logger.warning(
  +                "MockDatastoreService is not able to expire keys based on ttl."
  +            )
  +            instance.ttl = ttl
   
           self._datastore_items[name] = instance
           return True
  
verification_info: {"golden_diff": "diff --git a/st2tests/st2tests/mocks/datastore.py b/st2tests/st2tests/mocks/datastore.py\n--- a/st2tests/st2tests/mocks/datastore.py\n+++ b/st2tests/st2tests/mocks/datastore.py\n@@ -34,6 +34,7 @@\n         self._pack_name = pack_name\n         self._class_name = class_name\n         self._username = api_username or \"admin\"\n+        self._logger = logger\n \n         # Holds mock KeyValuePair objects\n         # Key is a KeyValuePair name and value is the KeyValuePair object\n@@ -96,10 +97,6 @@\n         \"\"\"\n         Store a value in a dictionary which is local to this class.\n         \"\"\"\n-        if ttl:\n-            raise ValueError(\n-                'MockDatastoreService.set_value doesn\\'t support \"ttl\" argument'\n-            )\n \n         name = self._get_full_key_name(name=name, local=local)\n \n@@ -107,6 +104,11 @@\n         instance.id = name\n         instance.name = name\n         instance.value = value\n+        if ttl:\n+            self._logger.warning(\n+                \"MockDatastoreService is not able to expire keys based on ttl.\"\n+            )\n+            instance.ttl = ttl\n \n         self._datastore_items[name] = instance\n         return True\n", "issue": "An exception is raised when calling MockDatastoreService.set_value() with a ttl\n## SUMMARY\r\n\r\nWhen using `MockDatastoreService.set_value()` for unit testing (e.g. via `BaseActionTestCase`), a `ValueError` exception will be raised if `ttl` argument is specified.\r\n\r\n\r\n### STACKSTORM VERSION\r\n\r\n`st2 3.1.0, on Python 3.6.8`\r\n\r\n### OS, environment, install method\r\n\r\nCustom install, but not relevant to this case, see below.\r\n\r\n## Steps to reproduce the problem\r\n\r\nSee [code](https://github.com/StackStorm/st2/blob/6d1809a4bb577e117baa00f249757284db9c6e76/st2tests/st2tests/mocks/datastore.py#L108) for `MockDatastoreService.set_value()`. If `ttl` argument is specified, `ValueError` will explicitly be raised.\r\n\r\n```python\r\nclass MockDatastoreService(BaseDatastoreService):\r\n    # ...\r\n    def set_value(self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False):\r\n        \"\"\"\r\n        Store a value in a dictionary which is local to this class.\r\n        \"\"\"\r\n        if ttl:\r\n            raise ValueError('MockDatastoreService.set_value doesn\\'t support \"ttl\" argument')\r\n        # ...\r\n```\r\n\r\n\r\n## Expected Results\r\n\r\nBoth `action_service.set_value()` and  `sensor_service.set_value()` support `ttl` argument. Although I understand that this argument cannot be honored in a mock situation, `MockDatastoreService`  should be consistent with the behavior of the class it is mocking - perhaps fire a warning instead of an Exception?\r\n\r\n## Actual Results\r\n\r\nAn exception is returned while running tests:\r\n```python\r\nValueError: MockDatastoreService.set_value doesn't support \"ttl\" argument\r\n```\r\nIt is not possible to test actions or sensors that use `xxx.set_value()` with a `ttl` argument.\r\n\r\n\n", "before_files": [{"content": "# Copyright 2020 The StackStorm Authors.\n# Copyright 2019 Extreme Networks, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nMock classes for use in pack testing.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom st2common.constants.keyvalue import SYSTEM_SCOPE\nfrom st2common.services.datastore import BaseDatastoreService\nfrom st2client.models.keyvalue import KeyValuePair\n\n__all__ = [\"MockDatastoreService\"]\n\n\nclass MockDatastoreService(BaseDatastoreService):\n    \"\"\"\n    Mock DatastoreService for use in testing.\n    \"\"\"\n\n    def __init__(self, logger, pack_name, class_name, api_username=None):\n        self._pack_name = pack_name\n        self._class_name = class_name\n        self._username = api_username or \"admin\"\n\n        # Holds mock KeyValuePair objects\n        # Key is a KeyValuePair name and value is the KeyValuePair object\n        self._datastore_items = {}\n\n    ##################################\n    # General methods\n    ##################################\n\n    def get_user_info(self):\n        \"\"\"\n        Retrieve information about the current user which is authenticated against StackStorm and\n        used to perform other datastore operations via the API.\n\n        :rtype: ``dict``\n        \"\"\"\n        result = {\n            \"username\": self._username,\n            \"rbac\": {\"is_admin\": True, \"enabled\": True, \"roles\": [\"admin\"]},\n            \"authentication\": {\"method\": \"authentication token\", \"location\": \"header\"},\n        }\n\n        return result\n\n    ##################################\n    # Methods for datastore management\n    ##################################\n\n    def list_values(self, local=True, prefix=None):\n        \"\"\"\n        Return a list of all values stored in a dictionary which is local to this class.\n        \"\"\"\n        key_prefix = self._get_full_key_prefix(local=local, prefix=prefix)\n\n        if not key_prefix:\n            return list(self._datastore_items.values())\n\n        result = []\n        for name, kvp in self._datastore_items.items():\n            if name.startswith(key_prefix):\n                result.append(kvp)\n\n        return result\n\n    def get_value(self, name, local=True, scope=SYSTEM_SCOPE, decrypt=False):\n        \"\"\"\n        Return a particular value stored in a dictionary which is local to this class.\n        \"\"\"\n        name = self._get_full_key_name(name=name, local=local)\n\n        if name not in self._datastore_items:\n            return None\n\n        kvp = self._datastore_items[name]\n        return kvp.value\n\n    def set_value(\n        self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False\n    ):\n        \"\"\"\n        Store a value in a dictionary which is local to this class.\n        \"\"\"\n        if ttl:\n            raise ValueError(\n                'MockDatastoreService.set_value doesn\\'t support \"ttl\" argument'\n            )\n\n        name = self._get_full_key_name(name=name, local=local)\n\n        instance = KeyValuePair()\n        instance.id = name\n        instance.name = name\n        instance.value = value\n\n        self._datastore_items[name] = instance\n        return True\n\n    def delete_value(self, name, local=True, scope=SYSTEM_SCOPE):\n        \"\"\"\n        Delete a value from a dictionary which is local to this class.\n        \"\"\"\n        name = self._get_full_key_name(name=name, local=local)\n\n        if name not in self._datastore_items:\n            return False\n\n        del self._datastore_items[name]\n        return True\n", "path": "st2tests/st2tests/mocks/datastore.py"}], "after_files": [{"content": "# Copyright 2020 The StackStorm Authors.\n# Copyright 2019 Extreme Networks, Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nMock classes for use in pack testing.\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom st2common.constants.keyvalue import SYSTEM_SCOPE\nfrom st2common.services.datastore import BaseDatastoreService\nfrom st2client.models.keyvalue import KeyValuePair\n\n__all__ = [\"MockDatastoreService\"]\n\n\nclass MockDatastoreService(BaseDatastoreService):\n    \"\"\"\n    Mock DatastoreService for use in testing.\n    \"\"\"\n\n    def __init__(self, logger, pack_name, class_name, api_username=None):\n        self._pack_name = pack_name\n        self._class_name = class_name\n        self._username = api_username or \"admin\"\n        self._logger = logger\n\n        # Holds mock KeyValuePair objects\n        # Key is a KeyValuePair name and value is the KeyValuePair object\n        self._datastore_items = {}\n\n    ##################################\n    # General methods\n    ##################################\n\n    def get_user_info(self):\n        \"\"\"\n        Retrieve information about the current user which is authenticated against StackStorm and\n        used to perform other datastore operations via the API.\n\n        :rtype: ``dict``\n        \"\"\"\n        result = {\n            \"username\": self._username,\n            \"rbac\": {\"is_admin\": True, \"enabled\": True, \"roles\": [\"admin\"]},\n            \"authentication\": {\"method\": \"authentication token\", \"location\": \"header\"},\n        }\n\n        return result\n\n    ##################################\n    # Methods for datastore management\n    ##################################\n\n    def list_values(self, local=True, prefix=None):\n        \"\"\"\n        Return a list of all values stored in a dictionary which is local to this class.\n        \"\"\"\n        key_prefix = self._get_full_key_prefix(local=local, prefix=prefix)\n\n        if not key_prefix:\n            return list(self._datastore_items.values())\n\n        result = []\n        for name, kvp in self._datastore_items.items():\n            if name.startswith(key_prefix):\n                result.append(kvp)\n\n        return result\n\n    def get_value(self, name, local=True, scope=SYSTEM_SCOPE, decrypt=False):\n        \"\"\"\n        Return a particular value stored in a dictionary which is local to this class.\n        \"\"\"\n        name = self._get_full_key_name(name=name, local=local)\n\n        if name not in self._datastore_items:\n            return None\n\n        kvp = self._datastore_items[name]\n        return kvp.value\n\n    def set_value(\n        self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False\n    ):\n        \"\"\"\n        Store a value in a dictionary which is local to this class.\n        \"\"\"\n\n        name = self._get_full_key_name(name=name, local=local)\n\n        instance = KeyValuePair()\n        instance.id = name\n        instance.name = name\n        instance.value = value\n        if ttl:\n            self._logger.warning(\n                \"MockDatastoreService is not able to expire keys based on ttl.\"\n            )\n            instance.ttl = ttl\n\n        self._datastore_items[name] = instance\n        return True\n\n    def delete_value(self, name, local=True, scope=SYSTEM_SCOPE):\n        \"\"\"\n        Delete a value from a dictionary which is local to this class.\n        \"\"\"\n        name = self._get_full_key_name(name=name, local=local)\n\n        if name not in self._datastore_items:\n            return False\n\n        del self._datastore_items[name]\n        return True\n", "path": "st2tests/st2tests/mocks/datastore.py"}]}