def init(self, **kwargs):
     self.observer = CallTrace('observer')
     self.cache = TimedMessageCache(**kwargs)
     self.dna = be((Observable(),
         (self.cache,
             (self.observer,)
         )
     ))
 def init(self, **kwargs):
     self.observer = CallTrace('observer')
     self.cache = TimedMessageCache(**kwargs)
     self.dna = be((Observable(),
         (self.cache,
             (self.observer,)
         )
     ))
class TimedMessageCacheTest(SeecrTestCase):
    def setUp(self):
        SeecrTestCase.setUp(self)
        self.init(cacheTimeout=0.1)

    def init(self, **kwargs):
        self.observer = CallTrace('observer')
        self.cache = TimedMessageCache(**kwargs)
        self.dna = be((Observable(),
            (self.cache,
                (self.observer,)
            )
        ))

    def testTransparentForAll(self):
        def someMessage(*args, **kwargs):
            yield 'text'
        self.observer.methods['someMessage'] = someMessage
        result = asString(self.dna.all.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('text', result)
        result = asString(self.dna.all.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('text', result)
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testTransparentForDo(self):
        self.observer.methods['someMessage'] = lambda *args, **kwargs: None
        self.dna.do.someMessage('arg', kwarg='kwarg')
        self.dna.do.someMessage('arg', kwarg='kwarg')
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testCacheAny(self):
        def someMessage(*args, **kwargs):
            return 'result'
            yield
        self.observer.methods['someMessage'] = someMessage
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        self.assertEqual(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())
        result = retval(self.dna.any.someMessage('arg', kwarg='otherkwarg'))
        self.assertEqual(['someMessage', 'someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testClearCache(self):
        def someMessage(*args, **kwargs):
            return 'result'
            yield
        self.observer.methods['someMessage'] = someMessage
        retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual(['someMessage'], self.observer.calledMethodNames())
        self.cache.clear()
        retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testKeepValueInCaseOfError(self):
        self.init(cacheTimeout=0.1, returnCachedValueInCaseOfException=True)
        def someMessageResult(*args, **kwargs):
            return 'result'
            yield
        def someMessageError(*args, **kwargs):
            raise RuntimeError("could be any exception")
            yield
        self.observer.methods['someMessage'] = someMessageResult
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        self.assertEqual(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.observer.methods['someMessage'] = someMessageError
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testTimeoutExceptionIsRaiseIfNoBackoffTimeoutSet(self):
        self.init(cacheTimeout=0.1)
        def someMessageTimeout(*args, **kwargs):
            raise TimeoutException()
            yield
        self.observer.methods['someMessage'] = someMessageTimeout
        self.assertRaises(TimeoutException, lambda: retval(self.dna.any.someMessage('arg', kwarg='kwarg')))

    def testTimeoutExceptionNotHandledSpecially(self):
        self.init(cacheTimeout=0.1, returnCachedValueInCaseOfException=True)
        def someMessageResult(*args, **kwargs):
            return 'result'
            yield
        def someMessageTimeout(*args, **kwargs):
            raise TimeoutException()
            yield
        self.observer.methods['someMessage'] = someMessageResult
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.observer.methods['someMessage'] = someMessageTimeout
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testTimeoutExceptionTriggersBackoff(self):
        self.init(cacheTimeout=0.1, returnCachedValueInCaseOfException=True, backoffTimeout=0.1)
        def someMessageResult(*args, **kwargs):
            return 'result'
            yield
        def someMessageTimeout(*args, **kwargs):
            raise TimeoutException()
            yield
        self.observer.methods['someMessage'] = someMessageResult
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.observer.methods['someMessage'] = someMessageTimeout
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        # should be in backoff mode and not even try!
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.observer.methods['someMessage'] = someMessageResult
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEqual('result', result)
        self.assertEqual(['someMessage', 'someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testTimeoutExceptionWithoutCachedResult(self):
        self.init(cacheTimeout=0.1, returnCachedValueInCaseOfException=True, backoffTimeout=0.1)
        def someMessageTimeout(*args, **kwargs):
            raise TimeoutException()
            yield
        self.observer.methods['someMessage'] = someMessageTimeout
        self.assertRaises(BackoffException, lambda: retval(self.dna.any.someMessage('arg', kwarg='kwarg')))
        self.assertEqual(['someMessage'], self.observer.calledMethodNames())
        self.assertRaises(BackoffException, lambda: retval(self.dna.any.someMessage('arg', kwarg='kwarg')))
        self.assertEqual(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.assertRaises(BackoffException, lambda: retval(self.dna.any.someMessage('arg', kwarg='kwarg')))
        self.assertEqual(['someMessage', 'someMessage'], self.observer.calledMethodNames())
class TimedMessageCacheTest(SeecrTestCase):
    def setUp(self):
        SeecrTestCase.setUp(self)
        self.init(cacheTimeout=0.1)

    def init(self, **kwargs):
        self.observer = CallTrace('observer')
        self.cache = TimedMessageCache(**kwargs)
        self.dna = be((Observable(),
            (self.cache,
                (self.observer,)
            )
        ))

    def testTransparentForAll(self):
        def someMessage(*args, **kwargs):
            yield 'text'
        self.observer.methods['someMessage'] = someMessage
        result = asString(self.dna.all.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('text', result)
        result = asString(self.dna.all.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('text', result)
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testTransparentForDo(self):
        self.observer.methods['someMessage'] = lambda *args, **kwargs: None
        self.dna.do.someMessage('arg', kwarg='kwarg')
        self.dna.do.someMessage('arg', kwarg='kwarg')
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testCacheAny(self):
        def someMessage(*args, **kwargs):
            raise StopIteration('result')
            yield
        self.observer.methods['someMessage'] = someMessage
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        self.assertEquals(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())
        result = retval(self.dna.any.someMessage('arg', kwarg='otherkwarg'))
        self.assertEquals(['someMessage', 'someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testClearCache(self):
        def someMessage(*args, **kwargs):
            raise StopIteration('result')
            yield
        self.observer.methods['someMessage'] = someMessage
        retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals(['someMessage'], self.observer.calledMethodNames())
        self.cache.clear()
        retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testKeepValueInCaseOfError(self):
        self.init(cacheTimeout=0.1, returnCachedValueInCaseOfException=True)
        def someMessageResult(*args, **kwargs):
            raise StopIteration('result')
            yield
        def someMessageError(*args, **kwargs):
            raise RuntimeError("could be any exception")
            yield
        self.observer.methods['someMessage'] = someMessageResult
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        self.assertEquals(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.observer.methods['someMessage'] = someMessageError
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testTimeoutExceptionIsRaiseIfNoBackoffTimeoutSet(self):
        self.init(cacheTimeout=0.1)
        def someMessageTimeout(*args, **kwargs):
            raise TimeoutException()
            yield
        self.observer.methods['someMessage'] = someMessageTimeout
        self.assertRaises(TimeoutException, lambda: retval(self.dna.any.someMessage('arg', kwarg='kwarg')))

    def testTimeoutExceptionNotHandledSpecially(self):
        self.init(cacheTimeout=0.1, returnCachedValueInCaseOfException=True)
        def someMessageResult(*args, **kwargs):
            raise StopIteration('result')
            yield
        def someMessageTimeout(*args, **kwargs):
            raise TimeoutException()
            yield
        self.observer.methods['someMessage'] = someMessageResult
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.observer.methods['someMessage'] = someMessageTimeout
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testTimeoutExceptionTriggersBackoff(self):
        self.init(cacheTimeout=0.1, returnCachedValueInCaseOfException=True, backoffTimeout=0.1)
        def someMessageResult(*args, **kwargs):
            raise StopIteration('result')
            yield
        def someMessageTimeout(*args, **kwargs):
            raise TimeoutException()
            yield
        self.observer.methods['someMessage'] = someMessageResult
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.observer.methods['someMessage'] = someMessageTimeout
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        # should be in backoff mode and not even try!
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.observer.methods['someMessage'] = someMessageResult
        result = retval(self.dna.any.someMessage('arg', kwarg='kwarg'))
        self.assertEquals('result', result)
        self.assertEquals(['someMessage', 'someMessage', 'someMessage'], self.observer.calledMethodNames())

    def testTimeoutExceptionWithoutCachedResult(self):
        self.init(cacheTimeout=0.1, returnCachedValueInCaseOfException=True, backoffTimeout=0.1)
        def someMessageTimeout(*args, **kwargs):
            raise TimeoutException()
            yield
        self.observer.methods['someMessage'] = someMessageTimeout
        self.assertRaises(BackoffException, lambda: retval(self.dna.any.someMessage('arg', kwarg='kwarg')))
        self.assertEquals(['someMessage'], self.observer.calledMethodNames())
        self.assertRaises(BackoffException, lambda: retval(self.dna.any.someMessage('arg', kwarg='kwarg')))
        self.assertEquals(['someMessage'], self.observer.calledMethodNames())
        sleep(0.11)

        self.assertRaises(BackoffException, lambda: retval(self.dna.any.someMessage('arg', kwarg='kwarg')))
        self.assertEquals(['someMessage', 'someMessage'], self.observer.calledMethodNames())