From 28a8b1c7e2a04279ebe7c45e013a7a553239ce9b Mon Sep 17 00:00:00 2001 From: Stan Misiurev Date: Tue, 22 Jan 2019 17:13:08 -0500 Subject: [PATCH] tests --- django_grpc/__init__.py | 2 +- django_grpc/management/commands/grpcserver.py | 23 +-- django_grpc/utils.py | 27 +++- requirements_test.txt | 2 + setup.cfg | 2 +- tests/conftest.py | 17 +++ tests/manage.py | 10 ++ tests/sampleapp/__init__.py | 0 tests/sampleapp/helloworld_pb2.py | 134 ++++++++++++++++++ tests/sampleapp/helloworld_pb2_grpc.py | 46 ++++++ tests/sampleapp/servicer.py | 7 + tests/sampleapp/utils.py | 8 ++ tests/settings.py | 10 ++ tests/test_models.py | 25 ---- tests/test_server.py | 20 +++ tests/test_utils.py | 14 ++ tox.ini | 9 +- 17 files changed, 309 insertions(+), 47 deletions(-) create mode 100644 tests/conftest.py create mode 100755 tests/manage.py create mode 100644 tests/sampleapp/__init__.py create mode 100644 tests/sampleapp/helloworld_pb2.py create mode 100644 tests/sampleapp/helloworld_pb2_grpc.py create mode 100644 tests/sampleapp/servicer.py create mode 100644 tests/sampleapp/utils.py delete mode 100644 tests/test_models.py create mode 100644 tests/test_server.py create mode 100644 tests/test_utils.py diff --git a/django_grpc/__init__.py b/django_grpc/__init__.py index 8ce9b36..7525d19 100644 --- a/django_grpc/__init__.py +++ b/django_grpc/__init__.py @@ -1 +1 @@ -__version__ = '0.1.3' +__version__ = '0.1.4' diff --git a/django_grpc/management/commands/grpcserver.py b/django_grpc/management/commands/grpcserver.py index f524491..42a5fdb 100644 --- a/django_grpc/management/commands/grpcserver.py +++ b/django_grpc/management/commands/grpcserver.py @@ -6,9 +6,8 @@ from django.core.management.base import BaseCommand from concurrent import futures from django.utils import autoreload -from django.utils.module_loading import import_string -from django_grpc.utils import extract_handlers +from django_grpc.utils import add_servicers, extract_handlers, create_server class Command(BaseCommand): @@ -18,7 +17,7 @@ def add_arguments(self, parser): parser.add_argument('--max_workers', type=int, help="Number of workers") parser.add_argument('--port', type=int, default=50051, help="Port number to listen") parser.add_argument('--autoreload', action='store_true', default=False) - parser.add_argument('--list-handlers', action='store_true', default=False, "Print all registered endpoints") + parser.add_argument('--list-handlers', action='store_true', default=False, help="Print all registered endpoints") def handle(self, *args, **options): if options['autoreload'] is True: @@ -30,16 +29,13 @@ def handle(self, *args, **options): def _serve(self, max_workers, port, *args, **kwargs): autoreload.raise_last_exception() self.stdout.write("Starting server at %s" % datetime.datetime.now()) - # create a gRPC server - server = grpc.server(futures.ThreadPoolExecutor(max_workers=max_workers)) - - self._add_servicers(server) - server.add_insecure_port('[::]:%s' % port) + server = create_server(max_workers, port) server.start() + self.stdout.write("Server is listening port %s" % port) - if kwargs['list-handlers'] is True: + if kwargs['list_handlers'] is True: for handler in extract_handlers(server): self.stdout.write(self.style.INFO(handler)) @@ -52,12 +48,5 @@ def _serve(self, max_workers, port, *args, **kwargs): server.stop(0) sys.exit(0) - def _add_servicers(self, server): - """ - Add servicers to the server - """ - from django.conf import settings - for path in settings.GRPC_SERVICERS: - callback = import_string(path) - callback(server) + diff --git a/django_grpc/utils.py b/django_grpc/utils.py index 80d7ab3..eb6b4f3 100644 --- a/django_grpc/utils.py +++ b/django_grpc/utils.py @@ -1,3 +1,28 @@ +from concurrent import futures + +import grpc +from django.utils.module_loading import import_string + + +def create_server(max_workers, port): + # create a gRPC server + server = grpc.server(futures.ThreadPoolExecutor(max_workers=max_workers)) + + add_servicers(server) + server.add_insecure_port('[::]:%s' % port) + return server + + +def add_servicers(server): + """ + Add servicers to the server + """ + from django.conf import settings + for path in settings.GRPC_SERVICERS: + callback = import_string(path) + callback(server) + + def extract_handlers(server): for it in server._state.generic_handlers[0]._method_handlers.values(): - yield it.unary_unary.__name__ + yield it.unary_unary.__qualname__ diff --git a/requirements_test.txt b/requirements_test.txt index ae74293..0c07a44 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -6,3 +6,5 @@ codecov>=2.0.0 # Additional test requirements go here +pytest +pytest-mock diff --git a/setup.cfg b/setup.cfg index 5a0eefc..561713c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.1.3 +current_version = 0.1.4 commit = True tag = True diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..dfb509c --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,17 @@ +import threading +from time import sleep + +import pytest +from django.core.management import call_command + + + +@pytest.fixture +def grpc_server_async(): + srv = threading.Thread(target=call_grpc_server_command, args=[{"max_workers": 3, "port": 50080, "autoreload": False}]) + srv.start() + sleep(5) + + yield + + srv.join() diff --git a/tests/manage.py b/tests/manage.py new file mode 100755 index 0000000..dc935d6 --- /dev/null +++ b/tests/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/tests/sampleapp/__init__.py b/tests/sampleapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/sampleapp/helloworld_pb2.py b/tests/sampleapp/helloworld_pb2.py new file mode 100644 index 0000000..e18ab9a --- /dev/null +++ b/tests/sampleapp/helloworld_pb2.py @@ -0,0 +1,134 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: helloworld.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='helloworld.proto', + package='helloworld', + syntax='proto3', + serialized_pb=_b('\n\x10helloworld.proto\x12\nhelloworld\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2I\n\x07Greeter\x12>\n\x08SayHello\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x42\x36\n\x1bio.grpc.examples.helloworldB\x0fHelloWorldProtoP\x01\xa2\x02\x03HLWb\x06proto3') +) + + + + +_HELLOREQUEST = _descriptor.Descriptor( + name='HelloRequest', + full_name='helloworld.HelloRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='helloworld.HelloRequest.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=32, + serialized_end=60, +) + + +_HELLOREPLY = _descriptor.Descriptor( + name='HelloReply', + full_name='helloworld.HelloReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='message', full_name='helloworld.HelloReply.message', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=62, + serialized_end=91, +) + +DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST +DESCRIPTOR.message_types_by_name['HelloReply'] = _HELLOREPLY +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), dict( + DESCRIPTOR = _HELLOREQUEST, + __module__ = 'helloworld_pb2' + # @@protoc_insertion_point(class_scope:helloworld.HelloRequest) + )) +_sym_db.RegisterMessage(HelloRequest) + +HelloReply = _reflection.GeneratedProtocolMessageType('HelloReply', (_message.Message,), dict( + DESCRIPTOR = _HELLOREPLY, + __module__ = 'helloworld_pb2' + # @@protoc_insertion_point(class_scope:helloworld.HelloReply) + )) +_sym_db.RegisterMessage(HelloReply) + + +DESCRIPTOR.has_options = True +DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\033io.grpc.examples.helloworldB\017HelloWorldProtoP\001\242\002\003HLW')) + +_GREETER = _descriptor.ServiceDescriptor( + name='Greeter', + full_name='helloworld.Greeter', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=93, + serialized_end=166, + methods=[ + _descriptor.MethodDescriptor( + name='SayHello', + full_name='helloworld.Greeter.SayHello', + index=0, + containing_service=None, + input_type=_HELLOREQUEST, + output_type=_HELLOREPLY, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_GREETER) + +DESCRIPTOR.services_by_name['Greeter'] = _GREETER + +# @@protoc_insertion_point(module_scope) diff --git a/tests/sampleapp/helloworld_pb2_grpc.py b/tests/sampleapp/helloworld_pb2_grpc.py new file mode 100644 index 0000000..0561fe9 --- /dev/null +++ b/tests/sampleapp/helloworld_pb2_grpc.py @@ -0,0 +1,46 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +from tests.sampleapp import helloworld_pb2 + + +class GreeterStub(object): + """The greeting service definition. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.SayHello = channel.unary_unary( + '/helloworld.Greeter/SayHello', + request_serializer=helloworld_pb2.HelloRequest.SerializeToString, + response_deserializer=helloworld_pb2.HelloReply.FromString, + ) + + +class GreeterServicer(object): + """The greeting service definition. + """ + + def SayHello(self, request, context): + """Sends a greeting + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_GreeterServicer_to_server(servicer, server): + rpc_method_handlers = { + 'SayHello': grpc.unary_unary_rpc_method_handler( + servicer.SayHello, + request_deserializer=helloworld_pb2.HelloRequest.FromString, + response_serializer=helloworld_pb2.HelloReply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'helloworld.Greeter', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/tests/sampleapp/servicer.py b/tests/sampleapp/servicer.py new file mode 100644 index 0000000..9161dee --- /dev/null +++ b/tests/sampleapp/servicer.py @@ -0,0 +1,7 @@ +from tests.sampleapp import helloworld_pb2_grpc, helloworld_pb2 + + +class Greeter(helloworld_pb2_grpc.GreeterServicer): + def SayHello(self, request, context): + return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) + diff --git a/tests/sampleapp/utils.py b/tests/sampleapp/utils.py new file mode 100644 index 0000000..e1d0cf8 --- /dev/null +++ b/tests/sampleapp/utils.py @@ -0,0 +1,8 @@ +from tests.sampleapp import helloworld_pb2_grpc +from tests.sampleapp.servicer import Greeter + + +def register_servicer(server): + """ Callback for django_grpc """ + helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) + diff --git a/tests/settings.py b/tests/settings.py index 3a624ea..668250d 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -31,3 +31,13 @@ MIDDLEWARE = () else: MIDDLEWARE_CLASSES = () + + +INSTALLED_APPS = [ + 'django_grpc', + 'tests.sampleapp', +] + +GRPC_SERVICERS = ( + 'tests.sampleapp.utils.register_servicer', +) diff --git a/tests/test_models.py b/tests/test_models.py deleted file mode 100644 index 9849007..0000000 --- a/tests/test_models.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -test_django-grpc ------------- - -Tests for `django-grpc` models module. -""" - -from django.test import TestCase - -from django_grpc import models - - -class TestDjango_grpc(TestCase): - - def setUp(self): - pass - - def test_something(self): - pass - - def tearDown(self): - pass diff --git a/tests/test_server.py b/tests/test_server.py new file mode 100644 index 0000000..62b6537 --- /dev/null +++ b/tests/test_server.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import threading +from time import sleep + +import pytest +from django.core.management import call_command + + +def call_grpc_server_command(options): + call_command("grpcserver", **options) + + +def test_management_command(mocker): + mocker.patch.object('') + + srv = threading.Thread(target=call_grpc_server_command, args=[{"max_workers": 3, "port": 50080, "autoreload": False}]) + srv.start() + sleep(5) + diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..deaf0b6 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,14 @@ +from concurrent import futures + +import grpc +import pytest + +from django_grpc.utils import create_server, extract_handlers + + +def test_extract_handlers(): + server = create_server(1, 50080) + assert list(extract_handlers(server)) == ['Greeter.SayHello'] + + + diff --git a/tox.ini b/tox.ini index ac1043e..e32efcb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,11 @@ [tox] -envlist = +envlist = tests [testenv] setenv = PYTHONPATH = {toxinidir}:{toxinidir}/django_grpc + DJANGO_SETTINGS_MODULE = tests.settings + commands = coverage run --source django_grpc runtests.py deps = -r{toxinidir}/requirements_test.txt @@ -11,4 +13,7 @@ basepython = py36: python3.6 py35: python3.5 py34: python3.4 - py27: python2.7 + + +[pytest] +;mock_use_standalone_module = true