def test_dispatched_event(httpserver):
    from krules_core import messages

    message_dispatcher_factory.override(
        providers.Singleton(lambda: CloudEventsDispatcher(
            httpserver.url, "pytest", test=True)))
    router = message_router_factory()
    subject = subject_factory("test-subject")
    subject.set_ext("ext1", "val1")
    subject.set_ext("ext2", 2)
    _id, code, sent_headers = router.route("test-message", subject,
                                           {"key1": "hello"})
    assert (200 <= code < 300)
    assert (sent_headers.get("Ce-Id") == _id)
    assert (sent_headers.get("Ce-Source") == "pytest")
    assert (sent_headers.get("Ce-Subject") == "test-subject")
    assert (sent_headers.get("Ce-Type") == "test-message")
    assert (sent_headers.get("Ce-Originid") == _id)
    assert (sent_headers.get("Ce-Ext1") == "val1")
    assert (sent_headers.get("Ce-Ext2") == "2")

    # with event info
    subject = subject_factory("test-subject", event_info={"Originid": 1234})
    _, _, sent_headers = router.route("test-message", subject,
                                      {"key1": "hello"})
    assert (sent_headers.get("Ce-Id") != sent_headers.get("Ce-Originid"))
    assert (sent_headers.get("Ce-Originid") == '1234')

    # property name
    _, _, sent_headers = router.route(messages.SUBJECT_PROPERTY_CHANGED,
                                      subject,
                                      {PayloadConst.PROPERTY_NAME: "foo"})
    assert (sent_headers.get("Ce-Propertyname") == 'foo')
def test_props():
    pod = subject_factory(f"k8s:/api/v1/namespaces/{NAMESPACE}/pods/{POD_NAME}", use_cache_default=True)

    assert pod.ext_name == pod.get_ext("name") == POD_NAME

    with pytest.raises(AttributeError):
        pod.get("p1")

    pod.p1 = 1
    pod.ext_p2 = "p2"

    pod.store()

    pod = subject_factory(f"k8s:/api/v1/namespaces/{NAMESPACE}/pods/{POD_NAME}", use_cache_default=True)

    assert pod.p1 == 1
    pod.p1 = lambda p: p+1
    assert pod.p1 == 2

    pod_nc = subject_factory(f"k8s:/api/v1/namespaces/{NAMESPACE}/pods/{POD_NAME}", use_cache_default=False)
    assert pod_nc.p1 == 1
    pod.store()
    assert pod_nc.p1 == 2
    del pod.p1
    pod.store()
    with pytest.raises(AttributeError):
        pod_nc.get("p1")
Example #3
0
def test_dispatched_event(fake_receiver):
    from krules_core import event_types

    event_dispatcher_factory.override(
        providers.Singleton(lambda: CloudEventsDispatcher(
            fake_receiver.url, "pytest", test=True)))
    router = event_router_factory()
    subject = subject_factory("test-subject")
    subject.set_ext("ext1", "val1")
    subject.set_ext("ext2", "2")
    _id, code, sent_headers = router.route("test-type", subject,
                                           {"key1": "hello"})

    assert (200 <= code < 300)
    assert (sent_headers.get("ce-id") == _id)
    assert (sent_headers.get("ce-source") == "pytest")
    assert (sent_headers.get("ce-subject") == "test-subject")
    assert (sent_headers.get("ce-type") == "test-type")
    assert (sent_headers.get("ce-Originid") == _id)
    assert (sent_headers.get("ce-ext1") == "val1")
    assert (sent_headers.get("ce-ext2") == "2")

    # with event info
    subject = subject_factory("test-subject", event_info={"originid": 1234})
    _, _, sent_headers = router.route("test-type", subject, {"key1": "hello"})
    assert (sent_headers.get("id") != sent_headers.get("ce-Originid"))
    assert (sent_headers.get("ce-Originid") == '1234')

    # property name
    _, _, sent_headers = router.route(event_types.SUBJECT_PROPERTY_CHANGED,
                                      subject,
                                      {PayloadConst.PROPERTY_NAME: "foo"})
    assert (sent_headers.get("ce-propertyname") == 'foo')
Example #4
0
def test_property_proxy(subject_no_cache):

    from krules_core.providers import subject_factory, subject_storage_factory
    global _test_events
    _test_events = []

    subject_no_cache.flush()

    with pytest.raises(AttributeError):
        _ = subject_no_cache.foo

    subject_no_cache.foo = 1
    assert subject_no_cache.foo == 1
    assert len(_test_events) == 1

    if subject_storage_factory(subject_no_cache.name).is_persistent():
        assert subject_factory(subject_no_cache.name).get("foo") == 1

    assert subject_no_cache.m_foo == 1
    assert subject_no_cache.foo == 1
    subject_no_cache.m_foo = 2
    assert len(_test_events) == 1

    subject_no_cache.ext_foo = 3
    assert subject_no_cache.ext_foo == 3
    assert subject_no_cache.foo == 2
    assert len(_test_events) == 1
    if subject_storage_factory(subject_no_cache.name).is_persistent():
        assert subject_factory(subject_no_cache.name).get_ext("foo") == 3

    subject_no_cache.foo = lambda foo: foo * foo
    assert len(_test_events) == 2

    assert subject_no_cache.foo == 4

    subject_no_cache.foo.incr()
    assert len(_test_events) == 3
    assert subject_no_cache.foo == 5
    subject_no_cache.m_foo.incr(2)
    assert len(_test_events) == 3
    assert subject_no_cache.foo == 7
    subject_no_cache.foo.decr()
    assert subject_no_cache.foo == 6
    with pytest.raises(TypeError):
        subject_no_cache.ext_foo.incr()
    with pytest.raises(TypeError):
        subject_no_cache.ext_foo.decr()

    del subject_no_cache.foo
    assert len(_test_events) == 5
    with pytest.raises(AttributeError):
        _ = subject_no_cache.foo
    subject_no_cache.foo = 1
    del subject_no_cache.m_foo
    assert len(_test_events) == 6
    with pytest.raises(AttributeError):
        _ = subject_no_cache.foo
    del subject_no_cache.ext_foo
    with pytest.raises(AttributeError):
        _ = subject_no_cache.ext_foo
Example #5
0
 def execute(self,
             message=None,
             subject=None,
             payload=None,
             hash=None,
             when=lambda _: datetime.now(),
             replace=False):
     if message is None:
         message = self.message
     if subject is None:
         subject = self.subject
     if payload is None:
         payload = self.payload
     if str(self.subject) != str(subject):
         subject = subject_factory(str(subject),
                                   event_info=self.subject.event_info())
     if callable(when):
         when = when(self)
     if type(when) is not str:
         when = when.isoformat()
     new_payload = {
         "message": message,
         "subject": str(subject),
         "payload": payload,
         "when": when,
         "replace": replace
     }
     message_router_factory().route(
         "schedule-message",
         subject,
         new_payload,
         dispatch_policy=DispatchPolicyConst.DIRECT)
Example #6
0
def subject_no_cache():
    from krules_core.providers import subject_factory

    global counter
    counter += 1
    return subject_factory('test-subject-{0}'.format(counter),
                           use_cache_default=False).flush()
def test_with_self():
    class WithSelfSet(RuleFunctionBase):
        def execute(self, arg1, arg2, arg3):

            self.payload["arg1"] = arg1
            self.payload["arg2"] = arg2
            self.payload["arg3"] = arg3

    RuleFactory.create(
        "test-with-self",
        subscribe_to="test-argprocessors-self",
        data={
            processing: [
                WithSelfSet(lambda self: self.payload["value_from"],
                            arg2=lambda self: self.subject.get("value_from"),
                            arg3=lambda p: "I'll never be called")
            ]
        })

    payload = {"value_from": 1}
    subject = subject_factory("test-1")

    subject.set("value_from", 2)

    event_router_factory().route("test-argprocessors-self", subject, payload)
    proc_events_rx_factory.subscribe(lambda x: x[
        rulename] == "test-with-self" and _assert(x[processing][0]["args"][
            0] == 1 and x[processing][0]["kwargs"]["arg2"] == 2 and isinstance(
                x[processing][0]["kwargs"]["arg3"], str)))

    assert payload["arg1"] == 1
    assert payload["arg2"] == 2
    assert inspect.isfunction(payload["arg3"])
    def execute(self):
        '''
        take the list of networks interfaces name set by multus in "k8s.v1.cni.cncf.io/networks"
        annotation and for each name add into the subject the pair name-status, which status is
        the status set by multus in "k8s.v1.cni.cncf.io/networks-status" annotation.
        (status mainly contain the ip of the network interface)
        '''

        interfaces = self.payload["metadata"]["annotations"][
            "k8s.v1.cni.cncf.io/networks"]
        statuses = self.payload["metadata"]["annotations"][
            "k8s.v1.cni.cncf.io/networks-status"]
        statuses = json.loads(statuses)
        interfaces = interfaces.split(', ')

        subject = subject_factory(
            f"service:{self.payload['metadata']['name']}")
        if "multus-app" in self.payload["metadata"]["labels"]:
            subject.set_ext("multusapp",
                            self.payload["metadata"]["labels"]["multus-app"])

        for interface in interfaces:
            for status in statuses:
                if status.get("name") == interface:
                    subject.set(status.get("name"), status, use_cache=False)
                    break
Example #9
0
def publish_proc_events_filtered(result, jp_expr, expt_value, debug=False):
    if jp_expr is not None:
        if not isinstance(jp_expr, list):
            jp_expr = [jp_expr]
        for expr in jp_expr:
            if callable(expt_value):
                _pass = expt_value(jp.match1(f"$[?({expr})]", [result]))
            else:
                _pass = (jp.match1(f"$[?({expr})]", [result]) == expt_value)
            if not _pass:
                return

    data = result

    event_info = data["event_info"]
    result_subject = subject_factory(data[RuleConst.RULENAME],
                                     event_info=event_info)

    if debug and result["type"] != RULE_PROC_EVENT:
        dispatch_policy = DispatchPolicyConst.NEVER
    else:
        dispatch_policy = DispatchPolicyConst.DIRECT
    event_router_factory().route(RULE_PROC_EVENT,
                                 result_subject,
                                 data,
                                 dispatch_policy=dispatch_policy)
Example #10
0
    def route(self,
              type,
              subject,
              payload,
              dispatch_policy=DispatchPolicyConst.DEFAULT):

        if isinstance(subject, str):
            # NOTE: this should have already happened if we want to take care or event info
            from krules_core.providers import subject_factory
            subject = subject_factory(subject)

        from ..providers import event_dispatcher_factory

        _callables = self._callables.get(type, None)

        #        try:
        if not dispatch_policy == DispatchPolicyConst.DIRECT:
            if _callables is not None:
                for _callable in _callables:
                    _callable(type, subject, payload)
        #        finally:
        #            subject.store()

        # TODO: unit test (policies)
        if dispatch_policy != DispatchPolicyConst.NEVER and _callables is None \
                and dispatch_policy == DispatchPolicyConst.DEFAULT \
                or dispatch_policy == DispatchPolicyConst.ALWAYS \
                or dispatch_policy == DispatchPolicyConst.DIRECT:
            logger.debug("dispatch {} to {} with payload {}".format(
                type, subject, payload))
            return event_dispatcher_factory().dispatch(type, subject, payload)
Example #11
0
def main():
    start_time = datetime.now()
    try:
        dispatch_policy = os.environ.get("DISPATCH_POLICY",
                                         DispatchPolicyConst.NEVER)

        m = marshaller.NewDefaultHTTPMarshaller()
        event = m.FromRequest(v1.Event(), request.headers,
                              io.BytesIO(request.data), lambda x: json.load(x))
        event_info = event.Properties()
        event_info.update(event_info.pop("extensions"))
        event_data = event_info.pop("data")

        app.logger.debug("RCVR: {}".format(event_data))
        event_type = event_info.get("type")
        subject = event_info.get("subject", "sys-0")
        if event_info["source"] == os.environ.get("K_SERVICE",
                                                  os.environ.get("SOURCE")):
            return Response(status=201)

        event_info["originid"] = event_info.get("originid",
                                                event_info.get("id"))

        app.logger.debug("subject: {}".format(subject))
        app.logger.debug("event_data: {}".format(event_data))

        subject = subject_factory(name=subject,
                                  event_info=event_info,
                                  event_data=event_data)

        event_data["_event_info"] = event_info  # TODO: KRUL-155

        try:
            app.router.route(event_type,
                             subject,
                             event_data,
                             dispatch_policy=dispatch_policy)
        finally:
            pass

        exec_time = (datetime.now() - start_time).total_seconds()
        app.logger.info(
            "Event",
            extra={
                'props': {
                    'event_info': event_info,
                    'type': event_type,
                    'subject': subject.name,
                    'exec_time': exec_time,
                    # 'headers': list(headers.keys())
                }
            })
        return Response(status=200)

    except Exception as ex:
        app.logger.error(ex, exc_info=True)
        return Response(status=201)
Example #12
0
def publish_proc_events_all(result):

    data = result
    # data = json.loads(json.dumps(result, cls=_JSONEncoder).encode("utf-8"))
    event_info = data.get("event_info", {})
    result_subject = subject_factory(data[RuleConst.RULENAME], event_info=event_info)

    event_router_factory().route(
        RULE_PROC_EVENT, result_subject, data, dispatch_policy=DispatchPolicyConst.DIRECT
    )
def test_inferred_properties():
    global POD_NAME, NAMESPACE

    pod = subject_factory(f"k8s:/api/v1/namespaces/{NAMESPACE}/pods/{POD_NAME}/status", event_data={})

    assert pod.ext_name == pod.get_ext("name") == POD_NAME
    assert pod.ext_namespace == pod.get_ext("namespace") == NAMESPACE
    #assert pod.ext_group == pod.get_ext("group") == ""
    assert pod.ext_apiversion == pod.get_ext("apiversion") == "v1"
    assert pod.ext_resourcetype == pod.get_ext("resourcetype") == "pods"
    assert pod.ext_subresource == pod.get_ext("subresource") == "status"
Example #14
0
def set_running_fws_in_payload(obj, payload):
    """
    Set ip address of the LB_FW_NETWORK interface of the FW passed as first parameter
    in the payload passed as second parameter.
    """
    subject = subject_factory(f"service:{obj['metadata']['name']}")
    try:
        if subject.get_ext("multusapp"):
            payload["fw_ips"] = payload.get("fw_ips", [])
            payload["fw_ips"].append(subject.get(LB_FW_NETWORK)["ips"][0])
    except AttributeError as ex:
        print(str(ex))
Example #15
0
def test_cache_policy(subject):
    from krules_core.providers import subject_factory, subject_storage_factory

    # default policy: use cache
    subject.flush()
    subject.set("p1", None)
    subject.set_ext("p2", None)

    subject_fresh = subject_factory(subject.name)
    with pytest.raises(AttributeError):
        subject_fresh.get("p1")
    with pytest.raises(AttributeError):
        subject_fresh.get_ext("p2")

    subject = subject_factory(subject.name, use_cache_default=False)
    subject.flush()
    subject.set("p1", None)
    subject.set_ext("p2", None)

    if subject_storage_factory(subject.name).is_persistent():
        subject_fresh = subject_factory(subject.name)
        subject_fresh.get("p1")
        subject_fresh.get_ext("p2")
Example #16
0
def publish_proc_events_errors(result):

    if not result.get("got_errors", False):
        return

    data = result
    # data = json.loads(json.dumps(result, cls=_JSONEncoder).encode("utf-8"))

    event_info = data["event_info"]
    result_subject = subject_factory(data[RuleConst.RULENAME], event_info=event_info)

    event_router_factory().route(
        RULE_PROC_EVENT, result_subject, data, dispatch_policy=DispatchPolicyConst.DIRECT
    )
def k8s_subject(obj=None, resource_path=None, prefix="k8s:"):
    """
    Returns a k8s subject instance providing a kubernetes resource
    :param obj:
    :param resource_path:
    :param prefix:
    :return:
    """
    if hasattr(obj, 'obj'):
        obj = obj.obj
    if obj is None:
        obj = {}
    if resource_path is None:
        resource_path = obj["metadata"]["selfLink"]
    return subject_factory(f"{prefix}{resource_path}", event_data=obj)
    def dispatch(self, event_type, subject, payload):

        if isinstance(subject, str):
            subject = subject_factory(subject)
        _event_info = subject.event_info()

        _id = str(uuid.uuid4())
        logging.debug("new event id: {}".format(_id))

        event = v1.Event()
        event.SetContentType('application/json')
        event.SetEventID(_id)
        event.SetSource(self._source)
        event.SetSubject(str(subject))
        event.SetEventTime(
            datetime.utcnow().replace(tzinfo=pytz.UTC).isoformat())
        event.SetEventType(event_type)

        # set extended properties
        ext_props = subject.get_ext_props()
        property_name = payload.get(PayloadConst.PROPERTY_NAME, None)
        if property_name is not None:
            ext_props.update({"propertyname": property_name})
        event.SetExtensions(ext_props)
        event.Set('Originid', str(_event_info.get("originid", _id)))
        event.SetData(payload)

        m = marshaller.NewHTTPMarshaller(
            [binary.NewBinaryHTTPCloudEventConverter()])

        headers, body = m.ToRequest(event, converters.TypeBinary,
                                    lambda x: json.dumps(x, cls=_JSONEncoder))
        # headers['Ce-Originid'] = str(_event_info.get("Originid", _id))

        if callable(self._dispatch_url):
            dispatch_url = self._dispatch_url(subject, event_type)
        else:
            dispatch_url = self._dispatch_url

        response = requests.post(dispatch_url, headers=headers, data=body)

        response.raise_for_status()

        if self._test:
            return _id, response.status_code, headers
        return _id
Example #19
0
def publish_proc_events_filtered(result, jp_expr, expt_value):

    if callable(expt_value):
        _pass = expt_value(jp.match1(jp_expr, result))
    else:
        _pass = (jp.match1(jp_expr, result) == expt_value)
    if not _pass:
        return

    data = result
    # data = json.loads(json.dumps(result, cls=_JSONEncoder).encode("utf-8"))

    event_info = data["event_info"]
    result_subject = subject_factory(data[RuleConst.RULENAME], event_info=event_info)

    event_router_factory().route(
        RULE_PROC_EVENT, result_subject, data, dispatch_policy=DispatchPolicyConst.DIRECT
    )
Example #20
0
def fake_receiver_app(environ, start_response):
    """Simplest possible WSGI application"""
    request = Request(environ)
    m = marshaller.NewDefaultHTTPMarshaller()
    event = m.FromRequest(v1.Event(), request.headers,
                          io.BytesIO(request.data), lambda x: json.load(x))
    event_info = event.Properties()
    event_info.update(event_info.pop("extensions"))
    subject = subject_factory(event_info.get("subject", "sys-0"))

    assert "originid" in event_info
    assert "subject" in event_info
    assert "data" in event_info

    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)

    return ['Ok']
def test_with_payload_and_subject():
    class WithPayloadSet(RuleFunctionBase):
        def execute(self, arg1, arg2, arg3):

            self.payload["arg1"] = arg1
            self.payload["arg2"] = arg2
            self.payload["arg3"] = arg3

    RuleFactory.create(
        "test-with-payload-and-subject",
        subscribe_to="test-argprocessors-payload-and-subject",
        data={
            processing: [
                WithPayloadSet(lambda payload: payload["value_from"],
                               arg2=lambda subject: subject.get("value_from"),
                               arg3=lambda p: "I'll never be called")
            ]
        })

    _payload = {"value_from": 1}
    _subject = subject_factory("test-1")

    _subject.set("value_from", 2)

    event_router_factory().route("test-argprocessors-payload-and-subject",
                                 _subject, _payload)

    proc_events_rx_factory().subscribe(
        lambda x: x[rulename] == "test-with-payload-and-subject" and _assert(x[
            processing][0]["args"][0] == 1 and x[processing][0][
                "kwargs"]["arg2"] == 2 and hasattr(
                    x[processing][0]["kwargs"]["arg3"], "__call__")))

    assert _payload["arg1"] == 1
    assert _payload["arg2"] == 2
    assert inspect.isfunction(_payload["arg3"])
Example #22
0
def main():
    start_time = datetime.now()
    try:
        dispatch_policy = os.environ.get("DISPATCH_POLICY",
                                         DispatchPolicyConst.NEVER)

        m = marshaller.NewDefaultHTTPMarshaller()
        event = m.FromRequest(v1.Event(), request.headers,
                              io.BytesIO(request.data), lambda x: json.load(x))
        event_info = event.Properties()
        event_info.update(event_info.pop("extensions"))
        event_data = event_info.pop("data")

        app.logger.debug("RCVR: {}".format(event_data))
        type = event_info.get("type")
        subject = event_info.get("subject", "sys-0")

        g.subjects = []
        # TODO: important!!
        # need to find a way to avoid a return of messages from the same service
        # (for example when resending it again after intercepted in the first time)
        # this workaround only works when in a knative service or at least when SOURCE environment
        # variable is set

        if event_info["source"] == os.environ.get("K_SERVICE",
                                                  os.environ.get("SOURCE")):
            return Response(status=201)

        event_info["originid"] = event_info.get("originid",
                                                event_info.get("id"))

        logger.debug("subject: {}".format(subject))
        logger.debug("event_data: {}".format(event_data))

        from dependency_injector import providers

        subject = subject_factory(name=subject,
                                  event_info=event_info,
                                  event_data=event_data)

        event_data["_event_info"] = event_info  # TODO: KRUL-155

        try:
            event_router_factory().route(type,
                                         subject,
                                         event_data,
                                         dispatch_policy=dispatch_policy)
        finally:
            for sub in g.subjects:
                sub.store()

        exec_time = (datetime.now() - start_time).total_seconds()
        logger.info(
            "Event",
            extra={
                'props': {
                    'event_info': event_info,
                    'type': type,
                    'subject': subject.name,
                    'exec_time': exec_time,
                    #'headers': list(headers.keys())
                }
            })
        return Response(status=200)

    except Exception as ex:
        app.logger.error(ex, exc_info=True)
        return Response(status=201)
Example #23
0
        rulename: "on-csv-upload-import-devices",
        subscribe_to: "google.storage.object.finalize",
        ruledata: {
            filters: [
                SubjectMatch("onboarding/import/(?P<deviceclass>.+)/(?P<filename>.+)", payload_dest="path_info"),
                CheckPayloadPropertyValue("contentType", "text/csv"),
            ],
            processing: [
                ProcessCSV_AsDict(
                    driver=GoogleStorageDriver,
                    bucket=_(lambda _self: _self.payload["bucket"]),
                    path=_(lambda _self: _self.payload["name"]),
                    func=lambda device_data, _self: (
                        message_router_factory().route(
                            "onboard-device",
                            subject_factory(device_data.pop("deviceid"), event_info=_self.subject.event_info()),
                            {
                                "data": device_data,
                                "class": _self.payload["path_info"]["deviceclass"]
                            }),
                    )
                )
            ],
        }
    },

    """
    Catch csv upload errors, reject file
    """,
    {
        rulename: 'on-csv-upload-import-devices-error',
Example #24
0
def subject():
    global counter
    counter += 1

    return subject_factory('test-subject-{0}'.format(counter)).flush()
Example #25
0
def k8s_subject(obj):
    return subject_factory("k8s:{}".format(obj["metadata"]["selfLink"]), event_data=obj)
Example #26
0
def test_factory():

    from krules_core.providers import subject_factory
    test_subject = subject_factory("test-subject")
    assert test_subject.name == "test-subject"
Example #27
0
def subject():
    from krules_core.providers import subject_factory

    global counter
    counter += 1
    return subject_factory('test-subject-{0}'.format(counter)).flush()
Example #28
0
                        upsert=True)
                ])
            ]
        },
    },
    """
    Do schedules
    """,
    {
        rulename: "on-tick-do-schedules",
        subscribe_to: "krules.heartbeat",
        ruledata: {
            processing: [
                SetPayloadProperty("_ids", []),
                WithDatabase(DATABASE),
                WithCollection(COLLECTION, indexes=INDEXES),
                MongoDBFind(
                    lambda self: {"_when": {
                        "$lt": datetime.now()
                    }},  # query
                    lambda x, self: (  # foreach
                        message_router_factory().route(
                            x["message"], subject_factory(x["subject"]), x[
                                "payload"]), self.payload["_ids"].append(
                                    str(x["_id"]))),
                ),
                MongoDBDeleteByIds(payload_from="_ids")
            ]
        }
    },
]
Example #29
0
def test_set_get_del(subject):
    from krules_core.providers import subject_factory, subject_storage_factory

    subject.flush()

    # store nothing
    subject.store()

    global _test_events
    _test_events = []

    # USE CACHE (default)
    subject.set("my-prop", 1)
    assert subject.get("my-prop") == 1

    # not yet stored
    subject_copy = subject_factory(subject.name)
    with pytest.raises(AttributeError):
        subject_copy.get("my-prop")
    subject.store()
    subject_copy = subject_factory(subject.name)
    if subject_storage_factory(subject_copy.name).is_persistent():
        assert subject_copy.get("my-prop") == 1

    # callables
    new_value, old_value = subject.set("my-prop", lambda x: x + 2)
    assert new_value == 3 and old_value == 1
    new_value, old_value = subject.set("my-prop-2",
                                       lambda x: x is None and 1 or x + 2)
    assert new_value == 1 and old_value is None

    # events
    assert len(_test_events) == 3
    from krules_core import event_types
    #   type
    assert _test_events[0][0] == _test_events[1][0] == _test_events[2][
        0] == event_types.SUBJECT_PROPERTY_CHANGED
    #   subject
    assert _test_events[0][1].name == _test_events[1][1].name == _test_events[
        2][1].name == subject.name
    #   payload
    payload = _test_events[0][2]
    assert payload[PayloadConst.PROPERTY_NAME] == "my-prop" and payload[PayloadConst.OLD_VALUE] is None \
        and payload[PayloadConst.VALUE] == 1
    payload = _test_events[1][2]
    assert payload[PayloadConst.PROPERTY_NAME] == "my-prop" and payload[PayloadConst.OLD_VALUE] == 1 \
        and payload[PayloadConst.VALUE] == 3
    payload = _test_events[2][2]
    assert payload[PayloadConst.PROPERTY_NAME] == "my-prop-2" and payload[PayloadConst.OLD_VALUE] is None \
        and payload[PayloadConst.VALUE] == 1

    # old the same with extended properties except that they are mute
    _test_events = []
    subject.set_ext("my-prop", 1)
    assert subject.get_ext("my-prop") == 1

    # not yet stored
    subject_copy = subject_factory(subject.name)
    with pytest.raises(AttributeError):
        subject_copy.get_ext("my-prop")
    subject.store()
    if subject_storage_factory(subject.name).is_persistent():
        subject_copy = subject_factory(subject.name)
        assert subject_copy.get_ext("my-prop") == 1

    # callables
    new_value, old_value = subject.set_ext("my-prop", lambda x: x + 2)
    assert new_value == 3 and old_value == 1
    new_value, old_value = subject.set_ext("my-prop-2",
                                           lambda x: x is None and 1 or x + 2)
    assert new_value == 1 and old_value is None

    # events
    assert len(_test_events) == 0

    # muted
    val, _ = subject.set("my-prop", "silent", muted=True)
    assert val == "silent" and len(_test_events) == 0

    # NO CACHE
    subject.set("my-prop", 0, use_cache=False)  # not calling store
    if subject_storage_factory(subject.name).is_persistent():
        subject = subject_factory(subject.name)
        val = subject.get("my-prop", use_cache=False)
        assert val == 0
    #  with callables
    val, old_val = subject.set("my-prop", lambda x: x + 5, use_cache=False)
    assert old_val == 0 and val == 5
    #  events produced
    assert len(_test_events) == 2

    # same with exts
    _test_events = []
    subject.set_ext("my-prop", 0, use_cache=False)  # not calling store
    if subject_storage_factory(subject.name).is_persistent():
        subject = subject_factory(subject.name)
        val = subject.get_ext("my-prop", use_cache=False)
        assert val == 0
    #  with callables
    val, old_val = subject.set_ext("my-prop", lambda x: x + 5, use_cache=False)
    assert old_val == 0 and val == 5
    #  events NOT produced
    assert len(_test_events) == 0

    #  with cached
    if subject_storage_factory(subject.name).is_persistent():
        subject = subject_factory(subject.name)
        val = subject.get("my-prop")
        assert val == 5  # prop cached
        subject.set("my-prop", 1, use_cache=False)
        val = subject.get("my-prop")  # from cache
        assert val == 1
        subject.store()
        #  update cache
        subject = subject_factory(subject.name)
        val = subject.get("my-prop")
        assert val == 1  # prop cached
        subject.set("my-prop", 8, use_cache=True)
        val = subject.get("my-prop", use_cache=False)  # update cache
        assert val == 1
        val = subject.get("my-prop", use_cache=True)
        assert val == 1
        subject.store()

    # deletes
    _test_events = []
    #   cache not loaded yet
    subject.delete("my-prop", use_cache=False)
    assert len(_test_events) == 1 and \
        _test_events[0][0] == event_types.SUBJECT_PROPERTY_DELETED and \
        _test_events[0][1].name == subject.name and \
        _test_events[0][2][PayloadConst.PROPERTY_NAME] == "my-prop"
    with pytest.raises(AttributeError):
        subject.get("my-prop")
    with pytest.raises(AttributeError):
        subject.delete("my-prop")
    # add prop bypassing cache
    subject.set("my-prop", 0, use_cache=False)
    subject.delete("my-prop", use_cache=True)
    subject.store()
    with pytest.raises(AttributeError):
        subject.get("my-prop")
    # add prop in cache remove directly
    subject.set("my-prop", 0, use_cache=True)
    subject.delete("my-prop", use_cache=False)
    subject.store()
    with pytest.raises(AttributeError):
        subject.get("my-prop")
    # all in cache
    subject.set("my-prop", 0, use_cache=True)
    subject.delete("my-prop", use_cache=True)
    subject.store()
    with pytest.raises(AttributeError):
        subject.get("my-prop")
    # no cache
    subject.set("my-prop", 0, use_cache=False)
    subject.delete("my-prop", use_cache=False)
    subject.store()
    with pytest.raises(AttributeError):
        subject.get("my-prop")
 def execute(self, prop, expected_value):
     if not subject_storage_factory(
             self.subject.name).is_concurrency_safe():
         return True  # skip test
     subject = subject_factory(self.subject.name)
     return subject.get(prop, cached=False) == expected_value