Repository: StackStorm/st2

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.




Discussion:
Thanks for contributing to this issue. As it has been 90 days since the last activity, we are automatically marking is as stale. If this issue is not relevant or applicable anymore (problem has been fixed in a new version or similar), please close the issue or let us know so we can close it. On the contrary, if the issue is still relevant, there is nothing you need to do, but if you have any additional details or context which would help us when working on this issue, please include it as a comment to this issue.

I too would like to change this behavior. I understand the ttl will not decrement, but if my code is already setting a ttl, raising this makes it impossible to truly test.  It should work just like encryption. You can pass in encrypt or decrypt arguments and even though there's no encryption, it doesn't raise that as an issue.

I've modified my local installed copy as such:

```python
    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.
        """


        name = self._get_full_key_name(name=name, local=local)

        instance = KeyValuePair()
        instance.id = name
        instance.name = name
        instance.value = value

        if ttl:
            self._logger.warning(
                'MockDatastoreService.set_value doesn\'t fully support "ttl" argument'
            )
            instance.ttl = ttl

        self._datastore_items[name] = instance
        return True
```

And this is the result

```python
>>> import logging
>>> logger = logging.getLogger(__name__)
>>> m = MockDatastoreService(logger, "example", "MyClass")
>>> m.set_value("foo", "bar", 900)
MockDatastoreService.set_value doesn't fully support "ttl" argument
True
>>> m.get_value("foo")
'bar'
>>> m.list_values()
[<KeyValuePair name=example.MyClass:foo,value=bar>]
>>> m.list_values()[0].ttl
900
```

Resolving patch:
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 @@ def __init__(self, logger, pack_name, class_name, api_username=None):
         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 @@ def set_value(
         """
         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 @@ def set_value(
         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


Test patch: