diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cf36f0..e098723 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,15 @@ jobs: sudo apt install --allow-downgrades -y $(cat third_party/userver/scripts/docs/en/deps/${{matrix.os}}.md | tr '\n' ' ') python3 -m pip install -r requirements.txt + - name: Install mongo + run: | + wget -qO- https://pgp.mongodb.com/server-7.0.asc | sudo gpg --dearmor | sudo tee /usr/share/keyrings/mongodb-server-7.0.gpg >/dev/null + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" \ + | sudo tee -a /etc/apt/sources.list.d/mongodb-org-7.0.list + sudo apt update + sudo apt install mongodb-org + sudo apt install mongodb-mongosh + - name: Setup ccache run: | ccache -M 2.0GB @@ -67,17 +76,17 @@ jobs: - name: Test run after install if: matrix.make == 'test-release' run: >- - ./local_installation/bin/service_template - --config=./local_installation/etc/service_template/static_config.yaml - --config_vars=./local_installation/etc/service_template/config_vars.yaml + ./local_installation/bin/mongo_grpc_service_template + --config=./local_installation/etc/mongo_grpc_service_template/static_config.yaml + --config_vars=./local_installation/etc/mongo_grpc_service_template/config_vars.yaml & - name: Check work run service if: matrix.make == 'test-release' run: | - ps aux | grep service_template | grep config && curl http://localhost:8080/ping -v + ps aux | grep mongo_grpc_service_template | grep config && curl http://localhost:8080/ping -v - name: Stop all if: matrix.make == 'test-release' run: | - killall service_template + killall mongo_grpc_service_template diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 8dc1e9a..b224ff2 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -12,6 +12,7 @@ name: Docker build jobs: tests: + if: false runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -33,7 +34,7 @@ jobs: sudo apt install --allow-downgrades -y docker-compose - name: Setup ccache - run: docker-compose run --rm service_template-container bash -c 'ccache -M 2.0GB && ccache -s' + run: docker-compose run --rm mongo_grpc_service_template-container bash -c 'ccache -M 2.0GB && ccache -s' - name: Cmake run: make docker-cmake-release diff --git a/CMakeLists.txt b/CMakeLists.txt index c4513d2..e658b9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,14 @@ cmake_minimum_required(VERSION 3.12) -project(service_template CXX) +project(mongo_grpc_service_template CXX) # Adding userver dependency -find_package(userver COMPONENTS core postgresql QUIET) +find_package(userver COMPONENTS core mongo grpc QUIET) if(NOT userver_FOUND) # Fallback to subdirectory usage + # Enable userver libraries that are needed in this project + set(USERVER_FEATURE_MONGODB ON CACHE BOOL "" FORCE) + set(USERVER_FEATURE_GRPC ON CACHE BOOL "" FORCE) + # Compatibility mode: some systems don't support these features set(USERVER_FEATURE_CRYPTOPP_BLAKE2 OFF CACHE BOOL "" FORCE) set(USERVER_FEATURE_GRPC_CHANNELZ OFF CACHE BOOL "" FORCE) @@ -25,8 +29,14 @@ userver_setup_environment() add_library(${PROJECT_NAME}_objs OBJECT src/hello.hpp src/hello.cpp + src/hello_client.hpp + src/hello_client.cpp ) -target_link_libraries(${PROJECT_NAME}_objs PUBLIC userver::core) +target_link_libraries(${PROJECT_NAME}_objs PUBLIC userver::mongo userver::grpc) + +# Create a proto library with userver extensions +userver_add_grpc_library(${PROJECT_NAME}_proto PROTOS handlers/hello.proto) +target_link_libraries(${PROJECT_NAME}_objs PUBLIC ${PROJECT_NAME}_proto) # The Service diff --git a/Makefile b/Makefile index 234880f..65b7c8c 100644 --- a/Makefile +++ b/Makefile @@ -29,23 +29,20 @@ build_release/CMakeCache.txt: cmake-release # Build using cmake .PHONY: build-debug build-release build-debug build-release: build-%: build_%/CMakeCache.txt - cmake --build build_$* -j $(NPROCS) --target service_template + cmake --build build_$* -j $(NPROCS) --target mongo_grpc_service_template # Test .PHONY: test-debug test-release test-debug test-release: test-%: build-% - cmake --build build_$* -j $(NPROCS) --target service_template_unittest - cmake --build build_$* -j $(NPROCS) --target service_template_benchmark + cmake --build build_$* -j $(NPROCS) --target mongo_grpc_service_template_unittest + cmake --build build_$* -j $(NPROCS) --target mongo_grpc_service_template_benchmark cd build_$* && ((test -t 1 && GTEST_COLOR=1 PYTEST_ADDOPTS="--color=yes" ctest -V) || ctest -V) pycodestyle tests # Start the service (via testsuite service runner) .PHONY: start-debug start-release start-debug start-release: start-%: - cmake --build build_$* -v --target=start-service_template - -.PHONY: service-start-debug service-start-release -service-start-debug service-start-release: service-start-%: start-% + cmake --build build_$* -v --target=start-mongo_grpc_service_template # Cleanup data .PHONY: clean-debug clean-release @@ -61,7 +58,7 @@ dist-clean: # Install .PHONY: install-debug install-release install-debug install-release: install-%: build-% - cmake --install build_$* -v --component service_template + cmake --install build_$* -v --component mongo_grpc_service_template .PHONY: install install: install-release @@ -75,22 +72,19 @@ format: # Internal hidden targets that are used only in docker environment .PHONY: --in-docker-start-debug --in-docker-start-release --in-docker-start-debug --in-docker-start-release: --in-docker-start-%: install-% - /home/user/.local/bin/service_template \ - --config /home/user/.local/etc/service_template/static_config.yaml \ - --config_vars /home/user/.local/etc/service_template/config_vars.yaml + /home/user/.local/bin/mongo_grpc_service_template \ + --config /home/user/.local/etc/mongo_grpc_service_template/static_config.yaml \ + --config_vars /home/user/.local/etc/mongo_grpc_service_template/config_vars.yaml # Build and run service in docker environment .PHONY: docker-start-debug docker-start-release docker-start-debug docker-start-release: docker-start-%: - $(DOCKER_COMPOSE) run -p 8080:8080 --rm service_template-container make -- --in-docker-start-$* - -.PHONY: docker-start-service-debug docker-start-service-release -docker-start-service-debug docker-start-service-release: docker-start-service-%: docker-start-% + $(DOCKER_COMPOSE) run -p 8080:8080 -p 8081:8081 --rm mongo_grpc_service_template-container make -- --in-docker-start-$* # Start specific target in docker environment .PHONY: docker-cmake-debug docker-build-debug docker-test-debug docker-clean-debug docker-install-debug docker-cmake-release docker-build-release docker-test-release docker-clean-release docker-install-release docker-cmake-debug docker-build-debug docker-test-debug docker-clean-debug docker-install-debug docker-cmake-release docker-build-release docker-test-release docker-clean-release docker-install-release: docker-%: - $(DOCKER_COMPOSE) run --rm service_template-container make $* + $(DOCKER_COMPOSE) run --rm mongo_grpc_service_template-container make $* # Stop docker container and cleanup data .PHONY: docker-clean-data diff --git a/README.md b/README.md index e25c0cd..55ddd2f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# service_template +# mongo_grpc_service_template -Template of a C++ service that uses [userver framework](https://github.com/userver-framework/userver). +Template of a C++ service that uses [userver framework](https://github.com/userver-framework/userver) with MongoDB and gRPC. ## Download and Build @@ -9,7 +9,7 @@ To create your own userver-based service follow the following steps: 1. Press the green "Use this template button" at the top of this github page 2. Clone the service `git clone your-service-repo && cd your-service-repo && git submodule update --init` -3. Give a proper name to your service and replace all the occurences of "service_template" string with that name +3. Give a proper name to your service and replace all the occurences of "mongo_grpc_service_template" string with that name 4. Feel free to tweak, adjust or fully rewrite the source code of your service. diff --git a/configs/config_vars.docker.yaml b/configs/config_vars.docker.yaml new file mode 100644 index 0000000..af5431c --- /dev/null +++ b/configs/config_vars.docker.yaml @@ -0,0 +1,13 @@ +worker-threads: 4 +worker-fs-threads: 2 +worker-grpc-threads: 2 +logger-level: debug + +is_testing: false + +server-port: 8080 +server-grpc-port: 8081 + +hello-endpoint: '[::1]:8081' + +dbconnection: 'mongodb://localhost:27017/admin' diff --git a/configs/config_vars.testing.yaml b/configs/config_vars.testing.yaml index 828e658..967dd10 100644 --- a/configs/config_vars.testing.yaml +++ b/configs/config_vars.testing.yaml @@ -1,7 +1,13 @@ worker-threads: 4 worker-fs-threads: 2 +worker-grpc-threads: 2 logger-level: debug is_testing: true server-port: 8080 +server-grpc-port: 8081 + +hello-endpoint: '[::1]:8081' + +dbconnection: 'mongodb://localhost:27217/admin' diff --git a/configs/config_vars.yaml b/configs/config_vars.yaml index d44ed88..8da72bf 100644 --- a/configs/config_vars.yaml +++ b/configs/config_vars.yaml @@ -1,7 +1,13 @@ worker-threads: 4 worker-fs-threads: 2 +worker-grpc-threads: 2 logger-level: info is_testing: false server-port: 8080 +server-grpc-port: 8081 + +hello-endpoint: '[::1]:8081' + +dbconnection: 'mongodb://localhost:27017/admin' diff --git a/configs/static_config.yaml b/configs/static_config.yaml index f516c91..8de9b4a 100644 --- a/configs/static_config.yaml +++ b/configs/static_config.yaml @@ -7,9 +7,48 @@ components_manager: fs-task-processor: # Make a separate task processor for filesystem bound tasks. worker_threads: $worker-fs-threads + grpc-blocking-task-processor: + worker_threads: $worker-grpc-threads + thread_name: grpc-worker + default_task_processor: main-task-processor components: # Configuring components that were registered via component_list + # Settings common to all gRPC client factories + grpc-client-common: + # The TaskProcessor for blocking connection initiation + blocking-task-processor: grpc-blocking-task-processor + + grpc-client-factory: + # Optional channel parameters for gRPC Core + # https://grpc.github.io/grpc/core/group__grpc__arg__keys.html + channel-args: {} + middlewares: + - grpc-client-logging + - grpc-client-deadline-propagation + + grpc-client-logging: + grpc-client-deadline-propagation: + + handler-hello: + + hello-client: + endpoint: $hello-endpoint + + grpc-server: + port: 8081 + service-defaults: + middlewares: + - grpc-server-logging + - grpc-server-deadline-propagation + - grpc-server-congestion-control + task-processor: main-task-processor + + congestion-control: + grpc-server-logging: + grpc-server-deadline-propagation: + grpc-server-congestion-control: + server: listener: # configuring the main listening socket... port: $server-port # ...to listen on this port and... @@ -32,17 +71,15 @@ components_manager: testsuite-support: {} http-client: - load-enabled: $is_testing - fs-task-processor: fs-task-processor - - dns-client: + load-enabled: $is-testing fs-task-processor: fs-task-processor tests-control: - load-enabled: $is_testing + load-enabled: $is-testing path: /tests/{action} method: POST task_processor: main-task-processor + handler-ping: path: /ping method: GET @@ -50,7 +87,8 @@ components_manager: throttling_enabled: false url_trailing_slash: strict-match - handler-hello: # Finally! Our handler. - path: /hello # Registering handler by URL '/hello'. - method: GET,POST # It will only reply to GET (HEAD) and POST requests. - task_processor: main-task-processor # Run it on CPU bound task processor + mongo-db-1: + dbconnection: $dbconnection + + dns-client: + fs-task-processor: fs-task-processor diff --git a/docker-compose.yml b/docker-compose.yml index ae9ee6b..acf2ac0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,45 @@ version: "2.3" services: - service_template-container: + mongo: + container_name: service-mongo + image: mongo + ports: + - 27017 + networks: + - mongo + + mongo_grpc_service_template-container: image: ghcr.io/userver-framework/ubuntu-22.04-userver-pg:latest privileged: true - network_mode: bridge environment: - PREFIX=${PREFIX:-~/.local} - - CCACHE_DIR=/service_template/.ccache + - CCACHE_DIR=/mongo_grpc_service_template/.ccache - CORES_DIR=/cores volumes: - - .:/service_template:rw + - .:/mongo_grpc_service_template:rw ports: - 8080:8080 - working_dir: /service_template + - 8081:8081 + working_dir: /mongo_grpc_service_template entrypoint: - ./tests/run_as_user.sh + depends_on: + - mongo + networks: + - mongo + - dockerbridge + +networks: + mongo: + driver: bridge + dockerbridge: + enable_ipv6: true + driver: bridge + driver_opts: + com.docker.network.enable_ipv6: "true" + ipam: + driver: default + config: + - subnet: 2001:3984:3989::/64 + gateway: 2001:3984:3989::1 diff --git a/proto/handlers/hello.proto b/proto/handlers/hello.proto new file mode 100644 index 0000000..9a2b548 --- /dev/null +++ b/proto/handlers/hello.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package handlers.api; + +service HelloService { + rpc SayHello(HelloRequest) returns(HelloResponse) {} +} + +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string text = 1; +} diff --git a/src/hello.cpp b/src/hello.cpp index d61e628..7bb5a15 100644 --- a/src/hello.cpp +++ b/src/hello.cpp @@ -2,37 +2,55 @@ #include -#include - -namespace service_template { - -namespace { - -class Hello final : public userver::server::handlers::HttpHandlerBase { -public: - static constexpr std::string_view kName = "handler-hello"; - - using HttpHandlerBase::HttpHandlerBase; - - std::string HandleRequestThrow( - const userver::server::http::HttpRequest &request, - userver::server::request::RequestContext &) const override { - return service_template::SayHelloTo(request.GetArg("name")); +#include +#include +#include +#include + +namespace mongo_grpc_service_template { + +Hello::SayHelloResult Hello::SayHello(CallContext& /*context*/, + handlers::api::HelloRequest&& request) { + auto name = request.name(); + + auto user_type = UserType::kFirstTime; + if (!name.empty()) { + auto users = mongo_pool_->GetCollection("hello_users"); + auto result = users.FindAndModify( + userver::formats::bson::MakeDoc("name", name), + userver::formats::bson::MakeDoc("$inc", userver::formats::bson::MakeDoc("count", 1)), + userver::storages::mongo::options::Upsert{} + ); + + if (result.ModifiedCount() > 0) { + user_type = UserType::kKnown; + } } -}; - -} // namespace + if (name.substr(0, 5) == "mock_") { + name = client_.SayHello(name.substr(5)); + } + handlers::api::HelloResponse response; + response.set_text(SayHelloTo(name, user_type)); + return response; +} -std::string SayHelloTo(std::string_view name) { +std::string SayHelloTo(std::string_view name, UserType type) { if (name.empty()) { name = "unknown user"; } - return fmt::format("Hello, {}!\n", name); + switch (type) { + case UserType::kFirstTime: + return fmt::format("Hello, {}!\n", name); + case UserType::kKnown: + return fmt::format("Hi again, {}!\n", name); + } + + UASSERT(false); } -void AppendHello(userver::components::ComponentList &component_list) { +void AppendHello(userver::components::ComponentList& component_list) { component_list.Append(); } -} // namespace service_template +} // namespace mongo_grpc_service_template diff --git a/src/hello.hpp b/src/hello.hpp index a438f8b..90db001 100644 --- a/src/hello.hpp +++ b/src/hello.hpp @@ -3,12 +3,39 @@ #include #include +#include +#include #include +#include +#include "hello_client.hpp" -namespace service_template { +namespace mongo_grpc_service_template { -std::string SayHelloTo(std::string_view name); +enum class UserType { kFirstTime, kKnown }; + +std::string SayHelloTo(std::string_view name, UserType type); + +class Hello final : public handlers::api::HelloServiceBase::Component { + public: + static constexpr std::string_view kName = "handler-hello"; + + Hello(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& component_context) + : handlers::api::HelloServiceBase::Component(config, component_context), + mongo_pool_( + component_context + .FindComponent("mongo-db-1") + .GetPool()), + client_(component_context.FindComponent()) {} + + SayHelloResult SayHello(CallContext& context, + handlers::api::HelloRequest&& request); + + private: + userver::storages::mongo::PoolPtr mongo_pool_; + HelloClient& client_; +}; void AppendHello(userver::components::ComponentList &component_list); -} // namespace service_template +} // namespace mongo_grpc_service_template diff --git a/src/hello_benchmark.cpp b/src/hello_benchmark.cpp index 528d577..9a32a6a 100644 --- a/src/hello_benchmark.cpp +++ b/src/hello_benchmark.cpp @@ -14,7 +14,8 @@ void HelloBenchmark(benchmark::State& state) { for (auto _ : state) { const auto name = kNames[i++ % std::size(kNames)]; - auto result = service_template::SayHelloTo(name); + auto result = mongo_grpc_service_template::SayHelloTo( + name, mongo_grpc_service_template::UserType::kFirstTime); benchmark::DoNotOptimize(result); } }); diff --git a/src/hello_client.cpp b/src/hello_client.cpp new file mode 100644 index 0000000..837d70a --- /dev/null +++ b/src/hello_client.cpp @@ -0,0 +1,51 @@ +#include "hello_client.hpp" + +#include + +#include + +namespace mongo_grpc_service_template { + +std::string HelloClient::SayHello(std::string name) { + handlers::api::HelloRequest request; + request.set_name(std::move(name)); + + // Deadline must be set manually for each RPC + auto context = std::make_unique(); + context->set_deadline( + userver::engine::Deadline::FromDuration(std::chrono::seconds{20})); + + // Initiate the RPC. No actual actions have been taken thus far besides + // preparing to send the request. + auto stream = client_.SayHello(request, std::move(context)); + + // Complete the unary RPC by sending the request and receiving the response. + // The client should call `Finish` (in case of single response) or `Read` + // until `false` (in case of response stream), otherwise the RPC will be + // cancelled. + handlers::api::HelloResponse response = stream.Finish(); + + return std::move(*response.mutable_text()); +} + +userver::yaml_config::Schema HelloClient::GetStaticConfigSchema() { + return userver::yaml_config::MergeSchemas< + userver::components::LoggableComponentBase>(R"( +type: object +description: > + a user-defined wrapper around api::GreeterServiceClient that provides + a simplified interface. +additionalProperties: false +properties: + endpoint: + type: string + description: > + Some other service endpoint (URI). +)"); +} + +void AppendHelloClient(userver::components::ComponentList& component_list) { + component_list.Append(); +} + +} // namespace mongo_grpc_service_template diff --git a/src/hello_client.hpp b/src/hello_client.hpp new file mode 100644 index 0000000..b7c2cca --- /dev/null +++ b/src/hello_client.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mongo_grpc_service_template { + +class HelloClient final : public userver::components::LoggableComponentBase { + public: + static constexpr std::string_view kName = "hello-client"; + + HelloClient(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& component_context) + : userver::components::LoggableComponentBase(config, component_context), + client_factory_( + component_context + .FindComponent() + .GetFactory()), + // The client needs a fixed endpoint + client_(client_factory_.MakeClient( + "hello-client", config["endpoint"].As())) {} + + std::string SayHello(std::string name); + + static userver::yaml_config::Schema GetStaticConfigSchema(); + + private: + userver::ugrpc::client::ClientFactory& client_factory_; + handlers::api::HelloServiceClient client_; +}; + +void AppendHelloClient(userver::components::ComponentList& component_list); + + +} // namespace mongo_grpc_service_template + +template <> +inline constexpr bool userver::components::kHasValidate = true; diff --git a/src/hello_test.cpp b/src/hello_test.cpp index 68186df..db2d7b4 100644 --- a/src/hello_test.cpp +++ b/src/hello_test.cpp @@ -3,6 +3,14 @@ #include UTEST(SayHelloTo, Basic) { - EXPECT_EQ(service_template::SayHelloTo("Developer"), "Hello, Developer!\n"); - EXPECT_EQ(service_template::SayHelloTo({}), "Hello, unknown user!\n"); + using mongo_grpc_service_template::SayHelloTo; + using mongo_grpc_service_template::UserType; + + EXPECT_EQ(SayHelloTo("Developer", UserType::kFirstTime), + "Hello, Developer!\n"); + + EXPECT_EQ(SayHelloTo({}, UserType::kFirstTime), "Hello, unknown user!\n"); + + EXPECT_EQ(SayHelloTo("Developer", UserType::kKnown), + "Hi again, Developer!\n"); } diff --git a/src/main.cpp b/src/main.cpp index b8b4166..c4a0b0f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,22 +1,45 @@ -#include #include #include +#include +#include #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include "hello.hpp" +#include "hello_client.hpp" int main(int argc, char* argv[]) { - auto component_list = userver::components::MinimalServerComponentList() - .Append() - .Append() - .Append() - .Append() - .Append(); + auto component_list = + userver::components::MinimalServerComponentList() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append("mongo-db-1"); - service_template::AppendHello(component_list); + mongo_grpc_service_template::AppendHello(component_list); + mongo_grpc_service_template::AppendHelloClient(component_list); return userver::utils::DaemonMain(argc, argv, component_list); } diff --git a/tests/conftest.py b/tests/conftest.py index 699ab94..7ea2006 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1 +1,59 @@ -pytest_plugins = ['pytest_userver.plugins.core'] +import pathlib +import sys + +import pytest +import grpc + +import handlers.hello_pb2_grpc as hello_services # noqa: E402, E501 + +USERVER_CONFIG_HOOKS = ['prepare_service_config'] +pytest_plugins = [ + 'pytest_userver.plugins.mongo', + 'pytest_userver.plugins.grpc', +] + + +MONGO_COLLECTIONS = { + 'hello_users': { + 'settings': { + 'collection': 'hello_users', + 'connection': 'admin', + 'database': 'admin', + }, + 'indexes': [], + }, +} + + +@pytest.fixture(scope='session') +def mongodb_settings(): + return MONGO_COLLECTIONS + + +@pytest.fixture +def grpc_service(grpc_channel, service_client): + return hello_services.HelloServiceStub(grpc_channel) + + +@pytest.fixture(scope='session') +def mock_grpc_hello_session(grpc_mockserver, create_grpc_mock): + mock = create_grpc_mock(hello_services.HelloServiceServicer) + hello_services.add_HelloServiceServicer_to_server( + mock.servicer, grpc_mockserver, + ) + return mock + + +@pytest.fixture +def mock_grpc_server(mock_grpc_hello_session): + with mock_grpc_hello_session.mock() as mock: + yield mock + + +@pytest.fixture(scope='session') +def prepare_service_config(grpc_mockserver_endpoint): + def patch_config(config, config_vars): + components = config['components_manager']['components'] + components['hello-client']['endpoint'] = grpc_mockserver_endpoint + + return patch_config diff --git a/tests/pytest.ini b/tests/pytest.ini index c64b29b..51bfd1b 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -1,3 +1,4 @@ [pytest] asyncio_mode = auto log_level = debug +mockserver-tracing-enabled = true diff --git a/tests/test_basic.py b/tests/test_basic.py deleted file mode 100644 index 452e737..0000000 --- a/tests/test_basic.py +++ /dev/null @@ -1,5 +0,0 @@ -# Start via `make test-debug` or `make test-release` -async def test_basic(service_client): - response = await service_client.post('/hello', params={'name': 'Tester'}) - assert response.status == 200 - assert response.text == 'Hello, Tester!\n' diff --git a/tests/test_hello.py b/tests/test_hello.py new file mode 100644 index 0000000..db18179 --- /dev/null +++ b/tests/test_hello.py @@ -0,0 +1,39 @@ +import pytest + +import handlers.hello_pb2 as hello_protos # noqa: E402, E501 + +# Start the tests via `make test-debug` or `make test-release` + + +async def test_grpc_client(mock_grpc_server, grpc_service): + @mock_grpc_server('SayHello') + async def mock_say_hello(request, context): + assert request.name + return hello_protos.HelloResponse( + text=f'{request.name}!!', + ) + + request = hello_protos.HelloRequest(name='mock_userver') + response = await grpc_service.SayHello(request) + assert response.text == 'Hello, userver!!!\n' + assert mock_say_hello.times_called == 1 + + +async def test_first_time_users(grpc_service): + request = hello_protos.HelloRequest(name='userver') + response = await grpc_service.SayHello(request) + assert response.text == 'Hello, userver!\n' + + +async def test_db_updates(grpc_service): + request = hello_protos.HelloRequest(name='World') + response = await grpc_service.SayHello(request) + assert response.text == 'Hello, World!\n' + + request = hello_protos.HelloRequest(name='World') + response = await grpc_service.SayHello(request) + assert response.text == 'Hi again, World!\n' + + request = hello_protos.HelloRequest(name='World') + response = await grpc_service.SayHello(request) + assert response.text == 'Hi again, World!\n' diff --git a/third_party/Readme.md b/third_party/Readme.md index 2e623aa..c2db79a 100644 --- a/third_party/Readme.md +++ b/third_party/Readme.md @@ -6,6 +6,6 @@ userver framework. For example: ``` cd /data/code git clone --depth 1 https://github.com/userver-framework/userver.git -git clone --depth 1 https://github.com/userver-framework/service_template.git -ln -s /data/code/userver /data/code/service_template/third_party/userver +git clone --depth 1 https://github.com/userver-framework/mongo_grpc_service_template.git +ln -s /data/code/userver /data/code/mongo_grpc_service_template/third_party/userver ```