def test_stateless(self): functions = StatefulFunctions() @functions.bind(typename="org.foo/greeter") def greeter(context, message): pass fun = functions.for_typename("org.foo/greeter") self.assertListEqual(fun.storage_spec.specs, [])
def test_async(self): functions = StatefulFunctions() @functions.bind(typename="org.foo/greeter", specs=[ValueSpec(name='seen_count', type=IntType)]) async def greeter(context, message): pass fun = functions.for_typename("org.foo/greeter") self.assertTrue(fun.is_async) self.assertIsNotNone(fun.storage_spec)
def test_state_spec(self): functions = StatefulFunctions() foo = ValueSpec(name='foo', type=IntType) bar = ValueSpec(name='bar', type=StringType) @functions.bind(typename="org.foo/greeter", specs=[foo, bar]) def greeter(context, message): pass fun = functions.for_typename("org.foo/greeter") self.assertListEqual(fun.storage_spec.specs, [foo, bar])
def test_integration_access_non_registered_state(self): functions = StatefulFunctions() @functions.bind( typename='org.foo/bar', states=[StateSpec('seen')]) async def fun(context, message): ignored = context['non_registered_state'] # # build the invocation # builder = InvocationBuilder() builder.with_target("org.foo", "bar", "0") seen = SeenCount() seen.seen = 100 builder.with_state("seen", seen) builder.with_invocation(Any(), None) # # assert error is raised on invoke # with self.assertRaises(StateRegistrationError): async_round_trip(functions, builder)
def test_integration(self): functions = StatefulFunctions() @functions.bind('org.foo/greeter') async def fun(context, message): any = Any() any.type_url = 'type.googleapis.com/k8s.demo.SeenCount' context.send("bar.baz/foo", "12345", any) # # build the invocation # builder = InvocationBuilder() builder.with_target("org.foo", "greeter", "0") arg = LoginEvent() arg.user_name = "user-1" builder.with_invocation(arg, ("org.foo", "greeter-java", "0")) # # invoke # result_json = async_round_trip(functions, builder) # assert outgoing message second_out_message = json_at(result_json, NTH_OUTGOING_MESSAGE(0)) self.assertEqual(second_out_message['target']['namespace'], 'bar.baz') self.assertEqual(second_out_message['target']['type'], 'foo') self.assertEqual(second_out_message['target']['id'], '12345') self.assertEqual(second_out_message['argument']['typename'], 'type.googleapis.com/k8s.demo.SeenCount')
def test_wrong_signature(self): functions = StatefulFunctions() with self.assertRaises(ValueError): @functions.bind(typename="org.foo/greeter", specs=[ValueSpec(name="bar", type=IntType)]) def foo(message): # missing context pass
def test_duplicate_state(self): functions = StatefulFunctions() with self.assertRaises(ValueError): @functions.bind(typename="org.foo/greeter", specs=[ ValueSpec(name="bar", type=IntType), ValueSpec(name="bar", type=IntType) ]) def foo(context, message): pass
def test_integration_incomplete_context(self): functions = StatefulFunctions() @functions.bind( typename='org.foo/bar', states=[ StateSpec('seen'), StateSpec('missing_state_1'), StateSpec('missing_state_2', expire_after=AfterWrite(timedelta(milliseconds=2000))) ]) async def fun(context, message): pass # # build an invocation that provides only 'seen' state # builder = InvocationBuilder() builder.with_target("org.foo", "bar", "0") seen = SeenCount() seen.seen = 100 builder.with_state("seen", seen) builder.with_invocation(Any(), None) # # invoke # result_json = async_round_trip(functions, builder) # # assert indicated missing states # missing_state_1_spec = json_at(result_json, NTH_MISSING_STATE_SPEC(0)) self.assertEqual(missing_state_1_spec['state_name'], 'missing_state_1') missing_state_2_spec = json_at(result_json, NTH_MISSING_STATE_SPEC(1)) self.assertEqual(missing_state_2_spec['state_name'], 'missing_state_2') self.assertEqual(missing_state_2_spec['expiration_spec']['mode'], 'AFTER_WRITE') self.assertEqual(missing_state_2_spec['expiration_spec']['expire_after_millis'], '2000')
# distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ################################################################################ from remote_module_verification_pb2 import Invoke, InvokeResult, InvokeCount from statefun import StatefulFunctions from statefun import StateSpec from statefun import RequestReplyHandler from statefun import kafka_egress_record import uuid functions = StatefulFunctions() @functions.bind(typename="org.apache.flink.statefun.e2e.remote/counter", states=[StateSpec('invoke_count')]) def counter(context, invoke: Invoke): """ Keeps count of the number of invocations, and forwards that count to be sent to the Kafka egress. We do the extra forwarding instead of directly sending to Kafka, so that we cover inter-function messaging in our E2E test. """ invoke_count = context.state('invoke_count').unpack(InvokeCount) if not invoke_count: invoke_count = InvokeCount() invoke_count.count = 1
def test_integration(self): functions = StatefulFunctions() @functions.bind( typename='org.foo/greeter', states=[StateSpec('seen')]) def fun(context, message): # state access seen = context.state('seen').unpack(SeenCount) seen.seen += 1 context.state('seen').pack(seen) # regular state access seenAny = context['seen'] seenAny.Unpack(seen) # sending and replying context.pack_and_reply(seen) any = Any() any.type_url = 'type.googleapis.com/k8s.demo.SeenCount' context.send("bar.baz/foo", "12345", any) # delayed messages context.send_after(timedelta(hours=1), "night/owl", "1", any) # egresses context.send_egress("foo.bar.baz/my-egress", any) context.pack_and_send_egress("foo.bar.baz/my-egress", seen) # kafka egress context.pack_and_send_egress("sdk/kafka", kafka_egress_record(topic="hello", key=u"hello world", value=seen)) context.pack_and_send_egress("sdk/kafka", kafka_egress_record(topic="hello", value=seen)) # AWS Kinesis generic egress context.pack_and_send_egress("sdk/kinesis", kinesis_egress_record( stream="hello", partition_key=u"hello world", value=seen, explicit_hash_key=u"1234")) context.pack_and_send_egress("sdk/kinesis", kinesis_egress_record( stream="hello", partition_key=u"hello world", value=seen)) # # build the invocation # builder = InvocationBuilder() builder.with_target("org.foo", "greeter", "0") seen = SeenCount() seen.seen = 100 builder.with_state("seen", seen) arg = LoginEvent() arg.user_name = "user-1" builder.with_invocation(arg, ("org.foo", "greeter-java", "0")) # # invoke # result_json = round_trip(functions, builder) # assert first outgoing message first_out_message = json_at(result_json, NTH_OUTGOING_MESSAGE(0)) self.assertEqual(first_out_message['target']['namespace'], 'org.foo') self.assertEqual(first_out_message['target']['type'], 'greeter-java') self.assertEqual(first_out_message['target']['id'], '0') self.assertEqual(first_out_message['argument']['typename'], 'type.googleapis.com/k8s.demo.SeenCount') # assert second outgoing message second_out_message = json_at(result_json, NTH_OUTGOING_MESSAGE(1)) self.assertEqual(second_out_message['target']['namespace'], 'bar.baz') self.assertEqual(second_out_message['target']['type'], 'foo') self.assertEqual(second_out_message['target']['id'], '12345') self.assertEqual(second_out_message['argument']['typename'], 'type.googleapis.com/k8s.demo.SeenCount') # assert state mutations first_mutation = json_at(result_json, NTH_STATE_MUTATION(0)) self.assertEqual(first_mutation['mutation_type'], 'MODIFY') self.assertEqual(first_mutation['state_name'], 'seen') self.assertIsNotNone(first_mutation['state_value']) # assert delayed first_delayed = json_at(result_json, NTH_DELAYED_MESSAGE(0)) self.assertEqual(int(first_delayed['delay_in_ms']), 1000 * 60 * 60) # assert egresses first_egress = json_at(result_json, NTH_EGRESS(0)) self.assertEqual(first_egress['egress_namespace'], 'foo.bar.baz') self.assertEqual(first_egress['egress_type'], 'my-egress') self.assertEqual(first_egress['argument']['typename'], 'type.googleapis.com/k8s.demo.SeenCount')