{"id":348609,"date":"2023-06-10T15:01:30","date_gmt":"2023-06-10T15:01:30","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=348609"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=348609","title":{"rendered":"<span>\u041f\u0438\u0448\u0435\u043c gRPC \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b \u043d\u0430 Go \u0441 Allure \u043e\u0442\u0447\u0435\u0442\u043e\u043c<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>\u0412\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c, \u043a\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c gRPC \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b  \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u044f\u0437\u044b\u043a\u0430 Go, \u0442\u0430\u043a\u0436\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c Allure \u043e\u0442\u0447\u0435\u0442 <\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u0447\u0438\u0442\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e, \u043d\u0443\u0436\u043d\u043e \u0431\u0430\u0437\u043e\u0432\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u044b:<\/p>\n<ul>\n<li>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Remote_procedure_call\" rel=\"noopener noreferrer nofollow\">RPC<\/a>? <\/p>\n<\/li>\n<li>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/GRPC\" rel=\"noopener noreferrer nofollow\">gRPC<\/a>?<\/p>\n<\/li>\n<li>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/Protocol_Buffers\" rel=\"noopener noreferrer nofollow\">protobuf<\/a>? \u0421\u044e\u0434\u0430 \u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043d\u0435\u0441\u0442\u0438 \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u043e \u0441 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u043e\u043c *.proto \u0444\u0430\u0439\u043b\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u041d\u0435\u043f\u043b\u043e\u0445\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0437\u043d\u0430\u0442\u044c\/\u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u044f\u0437\u044b\u043a\u0430 Go, \u0445\u043e\u0442\u044f \u0431\u044b \u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 docker \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f <a href=\"https:\/\/www.docker.com\/\" rel=\"noopener noreferrer nofollow\">docker<\/a>.<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u0435\u0437 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u0432\u044b\u0448\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u043e \u0447\u0435\u043c \u0438\u0434\u0435\u0442 \u0440\u0435\u0447\u044c<\/p>\n<h2>Requirements<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f gRPC \u0441\u0435\u0440\u0432\u0435\u0440. \u041f\u043e\u0438\u0441\u043a\u0430\u043b \u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u043e\u0440\u0430\u0445 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0435 gRPC \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043d\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0430\u0448\u0435\u043b. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0438 \u0443\u0436\u0435 \u043d\u0430 \u043d\u0435\u0433\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b.<\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\" rel=\"noopener noreferrer nofollow\">\u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d \u043d\u0430 \u043c\u043e\u0435\u043c github<\/a>. \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043f\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/README.md\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u0438\u043b\u0430\u0433\u0430\u044e\u0442\u0441\u044f<\/a>. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043a\u0430\u043a-\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c\/\u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u0438\u043c\u0435\u044e\u0449\u0438\u0439\u0441\u044f <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442<\/a>, \u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u043a\u0446\u0438\u044e <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server#setup-protobuf\" rel=\"noopener noreferrer nofollow\">Setup protobuf<\/a><\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0434\u0432\u0443\u043c\u044f \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438:<\/p>\n<ul>\n<li>\n<p>\u041f\u043e <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server#setup-project\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u0439<\/a> \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043d\u0430 \u0432\u0430\u0448\u0443 OS \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440;<\/p>\n<\/li>\n<li>\n<p>\u041b\u0438\u0431\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u043d\u0443\u0442\u0440\u0438 docker, \u0442\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server#setup-project-in-docker\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u0443 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 localhost:8000, \u043d\u0443 \u0438\u043b\u0438 127.0.0.1:8000. \u0414\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0438\u043c\u0435\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 &#171;\u043f\u043e\u0442\u044b\u043a\u0430\u0442\u044c&#187; \u0441\u0435\u0440\u0432\u0435\u0440. \u0422\u0430\u043a\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c Postman, \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u0441\u043a\u0442\u043e\u043f\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 gRPC \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d. <a href=\"https:\/\/blog.postman.com\/postman-now-supports-grpc\/\" rel=\"noopener noreferrer nofollow\">\u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 <\/a>\u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Postman \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 c gRPC<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u042d\u0442\u043e\u0442 \u0436\u0435 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u0445<\/p>\n<pre><code>syntax = \"proto3\"; option go_package = \".\/;articlesservice\";  service ArticlesService {   rpc GetArticle (GetArticleRequest) returns (GetArticleResponse);   rpc CreateArticle(CreateArticleRequest) returns (CreateArticleResponse);   rpc UpdateArticle(UpdateArticleRequest) returns (UpdateArticleResponse);   rpc DeleteArticle(DeleteArticleRequest) returns (DeleteArticleResponse); }  message Article {   string id = 1;   string title = 2;   string author = 3;   string description = 4; }  enum ErrorType {   NOT_FOUND = 0;   ALREADY_EXISTS = 1;   UNSPECIFIED = 2; }  message Error {   string message = 1;   ErrorType type = 2; }  message GetArticleRequest {   string article_id = 1; }  message GetArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message CreateArticleRequest {   Article article = 1; }  message CreateArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message UpdateArticleRequest {   Article article = 1; }  message UpdateArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message DeleteArticleRequest {   string article_id = 1; }  message DeleteArticleResponse {}<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u043c\u0435\u0442\u043e\u0434\u044b:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L5\" rel=\"noopener noreferrer nofollow\">GetArticle<\/a> &#8212; \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e id. \u0417\u0430\u043f\u0440\u043e\u0441 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L29\" rel=\"noopener noreferrer nofollow\">GetArticleRequest<\/a>  \u0438 \u043e\u0442\u0432\u0435\u0442 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L33\" rel=\"noopener noreferrer nofollow\">GetArticleResponse<\/a>;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L6\" rel=\"noopener noreferrer nofollow\">CreateArticle<\/a> &#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u0417\u0430\u043f\u0440\u043e\u0441 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L40\" rel=\"noopener noreferrer nofollow\">CreateArticleRequest<\/a>  \u0438 \u043e\u0442\u0432\u0435\u0442 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L44\" rel=\"noopener noreferrer nofollow\">CreateArticleResponse<\/a>;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L7\" rel=\"noopener noreferrer nofollow\">UpdateArticle<\/a> &#8212; \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u0417\u0430\u043f\u0440\u043e\u0441 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L51\" rel=\"noopener noreferrer nofollow\">UpdateArticleRequest<\/a> \u0438 \u043e\u0442\u0432\u0435\u0442 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L55\" rel=\"noopener noreferrer nofollow\">UpdateArticleResponse<\/a>;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L8\" rel=\"noopener noreferrer nofollow\">DeleteArticle<\/a> &#8212; \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u0417\u0430\u043f\u0440\u043e\u0441<a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L62\" rel=\"noopener noreferrer nofollow\"> DeleteArticleRequest<\/a> \u0438 \u043e\u0442\u0432\u0435\u0442 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L66\" rel=\"noopener noreferrer nofollow\">DeleteArticleResponse<\/a>;<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0436\u0435, \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L11\" rel=\"noopener noreferrer nofollow\">Article<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043c\u0435\u0442\u043e\u0434\u0430\u0445 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438. \u042d\u0442\u0430 \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043b\u0438\u0448\u043d\u0438\u0439 \u0440\u0430\u0437 \u043d\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434<\/p>\n<p>\u0412\u044b\u0448\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043d\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 CRUD \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438. \u041d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u043d\u043e \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u043c \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u0438 \u0442\u0430\u043a\u043e\u0439<\/p>\n<p>\u0421\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/grpc\/grpc-go\" rel=\"noopener noreferrer nofollow\">grpc-go<\/a>  &#8212; \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 gRPC \u043a\u043b\u0438\u0435\u043d\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/dailymotion\/allure-go\" rel=\"noopener noreferrer nofollow\">allure-go<\/a> &#8212; \u0434\u043b\u044f Allure \u043e\u0442\u0447\u0435\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/go-yaml\/yaml\/tree\/v3\" rel=\"noopener noreferrer nofollow\">yaml<\/a> &#8212; \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f yaml \u0444\u0430\u0439\u043b\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/onsi\/gomega\" rel=\"noopener noreferrer nofollow\">gomega<\/a> &#8212; \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/uber-go\/zap\" rel=\"noopener noreferrer nofollow\">zap<\/a> &#8212; \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/uber-go\/dig\" rel=\"noopener noreferrer nofollow\">dig<\/a> &#8212; \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 dependency injection;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\" rel=\"noopener noreferrer nofollow\">sample_go_grpc_server<\/a> &#8212; \u044d\u0442\u043e \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u0440\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0433\u043e\u0432\u043e\u0440\u0438\u043b\u043e\u0441\u044c \u0432\u044b\u0448\u0435, \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u0442\u044c \u0435\u0449\u0435 \u0440\u044f\u0434 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0431\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0438\u043a\u0443\u0434\u0430, \u043d\u043e \u0432\u0441\u0435 \u043e\u043d\u0438 \u0443\u0436\u0435 <a href=\"https:\/\/pkg.go.dev\/std\" rel=\"noopener noreferrer nofollow\">\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432 go<\/a><\/p>\n<h2>Configuration<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u043c\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043f\u0438\u0448\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438<\/p>\n<p>infrastructure\/config-local.yml<\/p>\n<pre><code class=\"yaml\">articlesService:   port: 8000   host: localhost  logger:   isDevMode: true   level: debug<\/code><\/pre>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\" rel=\"noopener noreferrer nofollow\">\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441\u0442\u0430\u0442\u0435\u0439<\/a> \u0438 \u043b\u043e\u0433\u0433\u0435\u0440\u0430, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0431\u043e\u043b\u044c\u0448\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432, \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0445 \u0432 \u044d\u0442\u043e\u0442 <strong>infrastructure\/config-local.yml<\/strong> \u0444\u0430\u0439\u043b<\/p>\n<p><strong>\u041b\u0430\u0439\u0444\u0445\u0430\u043a. <\/strong>\u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b \u0441\u0440\u0430\u0437\u0443 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\u0445, \u0442\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u043e\u0435 \u0438\u0437 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0439, \u043a\u0430\u043a \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_testing\/tree\/main\/infrastructure\" rel=\"noopener noreferrer nofollow\">\u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435<\/a>:<\/p>\n<ul>\n<li>\n<p>config-dev.yml<\/p>\n<\/li>\n<li>\n<p>config-local.yml<\/p>\n<\/li>\n<li>\n<p>config-stable.yml<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f ENV, \u0431\u0443\u0434\u0435\u043c \u0431\u0440\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043d\u0438\u0436\u0435, \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u043c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0447\u0442\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>utils\/config\/internal.go<\/strong><\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u0438, \u0432\u043d\u0443\u0442\u0440\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0443\u0442 &#171;\u043f\u043e\u043c\u0435\u0449\u0430\u0442\u044c\u0441\u044f&#187; \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 <strong>infrastructure\/config-{env}.yml<\/strong><\/p>\n<p>utils\/config\/models.go<\/p>\n<pre><code class=\"go\">package config  type Env string type LogLevel string  const ( DebugLogLevel   LogLevel = \"debug\" InfoLogLevel    LogLevel = \"info\" WarningLogLevel LogLevel = \"warning\" ErrorLogLevel   LogLevel = \"error\" )  type Logger struct { Level     LogLevel `yaml:\"level\"` IsDevMode bool     `yaml:\"isDevMode\"` }  type GrpcService struct { Port               int32  `yaml:\"port\"` Host               string `yaml:\"host\"` InsecureSkipVerify bool   `yaml:\"insecureSkipVerify\"` }  type Config struct { Logger          Logger      `yaml:\"logger\" validate:\"required\"` Articlesservice GrpcService `yaml:\"articlesService\" validate:\"required\"` }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0430\u0440\u0441\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u0438 &#171;\u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c&#187; \u0438\u0445 \u043f\u043e \u043c\u043e\u0434\u0435\u043b\u044f\u043c \u0438\u0437 <strong>utils\/config\/models.go<\/strong> <\/p>\n<p>utils\/config\/internal.go<\/p>\n<pre><code class=\"go\">package config  import ( \"fmt\" \"gopkg.in\/yaml.v3\" \"io\/ioutil\" \"os\" )  func getEnv() (Env, error) { env := os.Getenv(\"ENV\")  if len(env) == 0 { return \"\", fmt.Errorf(\"cannot parse env variable\") }  return Env(env), nil }  func readConfig(configPath string, config interface{}) error { if configPath == `` { return fmt.Errorf(`no config path`) }  configBytes, err := ioutil.ReadFile(configPath)  if err != nil { return err }  if err = yaml.Unmarshal(configBytes, config); err != nil { return err }  return nil }  func NewConfig() (Config, error) { var config Config  env, err := getEnv()  if err != nil { return Config{}, err }  err = readConfig(fmt.Sprintf(\"..\/infrastructure\/config-%s.yml\", env), &amp;config)  if err != nil { return Config{}, err }  return config, nil }<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0432 \u043f\u0443\u0442\u044c \u0434\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a <code>\"..\/infrastructure\/config-%s.yml\"<\/code><\/p>\n<h2>Service<\/h2>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0430\u0448\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c, \u043d\u043e \u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c gRPC \u043a\u043b\u0438\u0435\u043d\u0442<\/p>\n<p>utils\/services\/grpc\/client.go<\/p>\n<pre><code class=\"go\">package grpc  import ( \"fmt\" \"google.golang.org\/grpc\" \"google.golang.org\/grpc\/credentials\/insecure\" \"sample_go_grpc_testing\/utils\/config\" )  func GetGrpcClient(grpcService config.GrpcService) (*grpc.ClientConn, error) { address := fmt.Sprintf(\"%s:%d\", grpcService.Host, grpcService.Port)  return grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials())) }<\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>GetGrpcClient<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 <code>Host<\/code>, <code>Port<\/code>, \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0435\u0433\u043e<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L4\" rel=\"noopener noreferrer nofollow\">ArticlesService<\/a>. \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>client.go &#8212; \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442 \u0438 \u0431\u0438\u043b\u0434\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442;<\/p>\n<\/li>\n<li>\n<p>api.go &#8212; \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u041f\u043e \u0441\u0443\u0442\u0438 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u0435\u0440\u0442\u043a\u0430, \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0430\u043a\u0438\u043d\u0435\u043c \u043b\u043e\u0433\u0438, \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>steps.go &#8212; \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a \u043c\u0435\u0442\u043e\u0434\u0430\u043c \u0438\u0437 api.go allure \u0448\u0430\u0433\u0438, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0432\u0441\u0435 \u0447\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 \u043e\u0442\u0447\u0435\u0442\u043e\u043c. \u041c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0441\u043b\u043e\u0439, \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u044b \u0448\u0430\u0433\u0438 \u043e\u0442\u0447\u0435\u0442\u0430, \u043b\u0438\u0431\u043e \u0441\u0430\u043c \u043e\u0442\u0447\u0435\u0442;<\/p>\n<\/li>\n<\/ul>\n<p>core\/articlesservice\/client.go<\/p>\n<pre><code class=\"go\">package articlesservice  import ( articlesservice \"github.com\/Nikita-Filonov\/sample_go_grpc_server\/gen\/proto\" \"sample_go_grpc_testing\/utils\/config\" \"sample_go_grpc_testing\/utils\/logger\" \"sample_go_grpc_testing\/utils\/services\/grpc\" )  type Client struct { articlesservice.ArticlesServiceClient logger *logger.CtxLogger }  func NewClient(logger logger.Service, conf config.Config) (*Client, error) { conn, err := grpc.GetGrpcClient(conf.Articlesservice)  if err != nil { return &amp;Client{}, err }  return &amp;Client{ logger:                logger.NewPrefix(\"ARTICLES_SERVICE.GRPC.CLIENT\"), ArticlesServiceClient: articlesservice.NewArticlesServiceClient(conn), }, nil }<\/code><\/pre>\n<p>core\/articlesservice\/api.go<\/p>\n<pre><code class=\"go\">package articlesservice  import ( \"context\" articlesservice \"github.com\/Nikita-Filonov\/sample_go_grpc_server\/gen\/proto\" \"github.com\/onsi\/gomega\" )  func (c *Client) getArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.GetArticleRequest, ) *articlesservice.GetArticleResponse { c.logger.InfofJSON(\"GetArticleRequest\", request)  res, err := c.ArticlesServiceClient.GetArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), \"GetArticle error\")  c.logger.InfofJSON(\"GetArticleResponse\", res) return res }  func (c *Client) createArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.CreateArticleRequest, ) *articlesservice.CreateArticleResponse { c.logger.InfofJSON(\"CreateArticleRequest\", request)  res, err := c.ArticlesServiceClient.CreateArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), \"CreateArticle error\")  c.logger.InfofJSON(\"CreateArticleResponse\", res) return res }  func (c *Client) updateArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.UpdateArticleRequest, ) *articlesservice.UpdateArticleResponse { c.logger.InfofJSON(\"UpdateArticleRequest\", request)  res, err := c.ArticlesServiceClient.UpdateArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), \"UpdateArticle error\")  c.logger.InfofJSON(\"UpdateArticleResponse\", res) return res }  func (c *Client) deleteArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.DeleteArticleRequest, ) *articlesservice.DeleteArticleResponse { c.logger.InfofJSON(\"DeleteArticleRequest\", request)  res, err := c.ArticlesServiceClient.DeleteArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), \"DeleteArticle error\")  c.logger.InfofJSON(\"DeleteArticleResponse\", res) return res }<\/code><\/pre>\n<p>\u0418\u0437 \u0447\u0435\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u0435\u0442\u043e\u0434:<\/p>\n<ul>\n<li>\n<p>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440;<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0437\u043e\u0432 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0439 \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u044b;<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e gomega. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u0432\u0435\u0442\u0438\u043b \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u0442\u043e \u0442\u0435\u0441\u0442 \u0443\u043f\u0430\u0434\u0435\u0442 \u0443\u0436\u0435 \u043d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443, \u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u043e\u0439 \u043c\u0435\u0442\u043e\u0434 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0431\u044b\u043b\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 <code>g.Expect(err).Should(gomega.HaveOccurred(), \"GetArticle error\")<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0441\u0435 \u0445\u043e\u0440\u043e\u0448\u043e \u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c, \u0442\u043e \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u043c \u043e\u0442\u0432\u0435\u0442 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<\/li>\n<\/ul>\n<p>core\/articlesservice\/steps.go<\/p>\n<pre><code class=\"go\">package articlesservice  import ( \"context\" articlesservice \"github.com\/Nikita-Filonov\/sample_go_grpc_server\/gen\/proto\" \"github.com\/dailymotion\/allure-go\" \"github.com\/onsi\/gomega\" )  func (c *Client) GetArticle( g gomega.Gomega, request *articlesservice.GetArticleRequest, ) (response *articlesservice.GetArticleResponse) { allure.Step(allure.Description(\"Send ArticlesService.GetArticle request\"), allure.Action(func() { response = c.getArticle(g, context.Background(), request) }))  return response }  func (c *Client) CreateArticle( g gomega.Gomega, request *articlesservice.CreateArticleRequest, ) (response *articlesservice.CreateArticleResponse) { allure.Step(allure.Description(\"Send ArticlesService.CreateArticle request\"), allure.Action(func() { response = c.createArticle(g, context.Background(), request) }))  return response }  func (c *Client) UpdateArticle( g gomega.Gomega, request *articlesservice.UpdateArticleRequest, ) (response *articlesservice.UpdateArticleResponse) { allure.Step(allure.Description(\"Send ArticlesService.UpdateArticle request\"), allure.Action(func() { response = c.updateArticle(g, context.Background(), request) }))  return response }  func (c *Client) DeleteArticle( g gomega.Gomega, request *articlesservice.DeleteArticleRequest, ) (response *articlesservice.DeleteArticleResponse) { allure.Step(allure.Description(\"Send ArticlesService.DeleteArticle request\"), allure.Action(func() { response = c.deleteArticle(g, context.Background(), request) }))  return response }<\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0448\u0430\u0433\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438, \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u0442\u043e\u0438\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0442\u0438\u043b\u0438\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u0434\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<h2>Logger<\/h2>\n<p>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0447\u0435\u043d\u044c \u0432\u0430\u0436\u043d\u044b\u0439 \u0430\u0442\u0440\u0438\u0431\u0443\u0442, \u0431\u0435\u0437 \u043d\u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0447\u0442\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u0434\u0435\u043b\u0430\u0435\u0442 \u0442\u0435\u0441\u0442, \u043d\u0430 \u043a\u0430\u043a\u043e\u043c \u0438\u043c\u0435\u043d\u043d\u043e \u0448\u0430\u0433\u0435 \u043e\u043d \u0443\u043f\u0430\u043b, \u0447\u0442\u043e \u043e\u0442\u0432\u0435\u0442\u0438\u043b \u0441\u0435\u0440\u0432\u0435\u0440<\/p>\n<p> \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/github.com\/uber-go\/zap\" rel=\"noopener noreferrer nofollow\">zap<\/a><\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u0434\u043b\u044f \u043b\u043e\u0433\u0433\u0435\u0440\u0430 \u0438 \u0431\u0438\u043b\u0434\u0435\u0440<\/p>\n<pre><code class=\"go\">package logger  import ( \"sample_go_grpc_testing\/utils\/config\" \"time\"  \"go.uber.org\/zap\" \"go.uber.org\/zap\/zapcore\" )  var MapConfLevelZapLevel = map[config.LogLevel]zapcore.Level{ config.DebugLogLevel:   zap.DebugLevel, config.InfoLogLevel:    zap.InfoLevel, config.WarningLogLevel: zap.WarnLevel, config.ErrorLogLevel:   zap.ErrorLevel, }  type Service interface { NewPrefix(prefix string) *CtxLogger }  type loggerService struct { *zap.Logger }  func (ls *loggerService) NewPrefix(prefix string) *CtxLogger { return &amp;CtxLogger{ls.Logger.Named(prefix)} }  func NewLoggerService(conf config.Config) (Service, error) { cfg := newConfig(conf.Logger)  zapLogger, err := cfg.Build() if err != nil { return nil, err }  return &amp;loggerService{zapLogger}, err }  func utcTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.UTC().Format(\"2006-01-02T15:04:05.000Z0700\")) }  func newEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ TimeKey:        \"@timestamp\", LevelKey:       \"level\", NameKey:        \"logger_name\", CallerKey:      \"caller_file\", MessageKey:     \"message\", StacktraceKey:  \"stacktrace\", LineEnding:     zapcore.DefaultLineEnding, EncodeLevel:    zapcore.CapitalLevelEncoder, EncodeTime:     utcTimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, EncodeCaller:   zapcore.ShortCallerEncoder, } }  func newConfig(conf config.Logger) zap.Config { cfg := zap.Config{ Level:             zap.NewAtomicLevelAt(MapConfLevelZapLevel[conf.Level]), Development:       false, DisableCaller:     false, DisableStacktrace: false, Sampling: &amp;zap.SamplingConfig{ Initial:    100, Thereafter: 100, }, Encoding:         \"json\", EncoderConfig:    newEncoderConfig(), OutputPaths:      []string{\"stdout\"}, ErrorOutputPaths: []string{\"stdout\"}, }  if conf.IsDevMode { cfg.Development = true cfg.Encoding = \"console\" }  return cfg }<\/code><\/pre>\n<ul>\n<li>\n<p><code>utcTimeEncoder<\/code>, <code>newEncoderConfig<\/code>, <code>newConfig<\/code> &#8212; \u043f\u043e \u0441\u0443\u0442\u0438 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u0442\u0438\u043b\u0438\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043b\u043e\u0433\u0433\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>NewLoggerService &#8212; \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u043b\u043e\u0433\u0433\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>NewPrefix &#8212; \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043b\u043e\u0433\u0433\u0435\u0440\u0430 \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0443, \u043a\u0430\u043a \u043c\u044b \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u043b\u0438 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_testing\/blob\/main\/core\/articlesservice\/client.go#L23\" rel=\"noopener noreferrer nofollow\">\u0447\u0443\u0442\u044c \u0440\u0430\u043d\u0435\u0435<\/a> <\/p>\n<\/li>\n<\/ul>\n<h2>Components\/dependency injection<\/h2>\n<p>\u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/uber-go\/dig\" rel=\"noopener noreferrer nofollow\">dig<\/a> \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 dependency injection<\/p>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 dependency injection? \u041f\u0440\u043e \u044d\u0442\u043e \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Dependency_injection#:~:text=In%20software%20engineering%2C%20dependency%20injection,leading%20to%20loosely%20coupled%20programs.\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a><\/p>\n<p>\u0417\u0430\u0447\u0435\u043c \u043d\u0430\u043c \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e? \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044f. \u0410\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b\u044c \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043a\u043e\u043b\u0435\u0441, \u0440\u0443\u043b\u044f, \u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u044f, \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u0438\u043a\u0438, \u0431\u0435\u043d\u0437\u043e\u0431\u0430\u043a\u0430 \u0438 \u0442.\u0434. \u041d\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0442\u043e \u0435\u0441\u0442\u044c \u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044f, \u043d\u0435 \u0432\u043e\u043b\u043d\u0443\u0435\u0442, \u043a\u0430\u043a, \u0433\u0434\u0435, \u043a\u0435\u043c \u0438 \u043a\u043e\u0433\u0434\u0430 \u0431\u044b\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b\/\u0441\u043e\u0437\u0434\u0430\u043d\u044b \u044d\u0442\u0438 \u0434\u0435\u0442\u0430\u043b\u0438, \u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044e \u043d\u0443\u0436\u0435\u043d \u043b\u0438\u0448\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0430\u0448\u0438\u043d\u043e\u0439\/\u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u0435\u043c. \u0412\u043e\u0434\u0438\u0442\u0435\u043b\u044e \u0432\u0430\u0436\u0435\u043d \u043b\u0438\u0448\u044c \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 &#8212; \u0432 \u043c\u0430\u0448\u0438\u043d\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u0435\u0441\u0442\u044c, \u0437\u0430\u0432\u0435\u0441\u0442\u0438 \u0438 \u043f\u043e\u0435\u0445\u0430\u0442\u044c. \u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0438 \u0441 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u043c, \u0434\u043b\u044f \u043d\u0438\u0445 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432\/\u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 \u0438\u043b\u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0440\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0430\u043f\u0438\u0448\u043a\u0438, <a href=\"https:\/\/www.vaultproject.io\/\" rel=\"noopener noreferrer nofollow\">\u0432\u043e\u043b\u044c\u0442<\/a> \u0441 \u043a\u0440\u0435\u0434\u0430\u043c\u0438 \u0438 \u0442.\u0434. \u041f\u043e \u0441\u0443\u0442\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u0442\u0435\u0441\u0442\u0430 \u043d\u0435 \u0432\u0430\u0436\u043d\u043e, \u043a\u0442\u043e, \u0433\u0434\u0435 \u0438 \u043a\u0430\u043a \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043b \u044d\u0442\u0438\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432, \u043d\u0443\u0436\u0435\u043d \u043b\u0438\u0448\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u043f\u043e\u0439\u0442\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043a\u0440\u0435\u0434\u044b \u0438 \u0442.\u0434.<\/p>\n<p>\u041e\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0442\u0435\u0441\u0442\u0430\u0445<\/p>\n<p>utils\/common\/container\/components.go<\/p>\n<pre><code class=\"go\">package container  import ( \"go.uber.org\/dig\" \"sample_go_grpc_testing\/core\/articlesservice\" \"sample_go_grpc_testing\/utils\/config\" \"sample_go_grpc_testing\/utils\/logger\" )  type Components struct { ArticlesService *articlesservice.Client  Logger logger.Service Config config.Config }  func initComponents(c *dig.Container) (*Components, error) { var err error components := Components{}  err = c.Invoke(func( articlesService *articlesservice.Client,  logger logger.Service, conf config.Config, ) { components.Config = conf components.Logger = logger  components.ArticlesService = articlesService })  if err != nil { return nil, err }  return &amp;components, nil }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0431\u0438\u043b\u0434\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b<\/p>\n<p>utils\/common\/container\/api.go<\/p>\n<pre><code class=\"go\">package container  import ( \"go.uber.org\/dig\" \"sample_go_grpc_testing\/core\/articlesservice\" \"sample_go_grpc_testing\/utils\/config\" \"sample_go_grpc_testing\/utils\/logger\" )  func BuildContainer() (*Components, error) { c := dig.New() servicesConstructors := []interface{}{ articlesservice.NewClient, config.NewConfig, logger.NewLoggerService, }  for _, service := range servicesConstructors { err := c.Provide(service) if err != nil { return nil, err } } components, componentsError := initComponents(c)  if componentsError != nil { return nil, componentsError }  return components, nil }<\/code><\/pre>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043f\u0440\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 dig \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/pkg.go.dev\/go.uber.org\/dig\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a><\/p>\n<h2>Assertions<\/h2>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/onsi\/gomega\" rel=\"noopener noreferrer nofollow\">gomega<\/a>. \u041f\u043e\u043b\u043d\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/onsi.github.io\/gomega\/\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a>, \u043b\u0438\u0448\u044c \u043a\u0440\u0430\u0442\u043a\u043e \u0441\u043a\u0430\u0436\u0443, \u0447\u0442\u043e \u0434\u0430\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a:<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f <a href=\"https:\/\/onsi.github.io\/gomega\/#making-assertions\" rel=\"noopener noreferrer nofollow\">\u0440\u0430\u0432\u043d\u044b\/\u043d\u0435 \u0440\u0430\u0432\u043d\u044b<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/onsi.github.io\/gomega\/#handling-errors\" rel=\"noopener noreferrer nofollow\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/onsi.github.io\/gomega\/#handling-errors\" rel=\"noopener noreferrer nofollow\">\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438<\/a>, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u043e\u0436\u0434\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0438\u0441 \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0443\u0436\u043d\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 \u043a\u043e\u0434<\/p>\n<\/li>\n<\/ul>\n<p>\u0423 gomega \u0435\u0449\u0435 \u0435\u0441\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u043a\u0440\u0443\u0442\u044b\u0445 \u0444\u0438\u0447\u0435\u0439 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a, \u043d\u043e \u043d\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u044b\u0448\u0435\u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u043e\u0433\u043e<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043f\u0438\u0448\u0435\u043c gomega \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c gomega \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438<\/p>\n<p>utils\/common\/gomega.go<\/p>\n<pre><code class=\"go\">package common  import ( \"github.com\/dailymotion\/allure-go\" \"github.com\/onsi\/gomega\" \"github.com\/pkg\/errors\" \"runtime\/debug\" \"testing\" \"time\" )  func GetGomega(t *testing.T) gomega.Gomega { g := gomega.NewWithT(t) g.SetDefaultEventuallyTimeout(time.Second * 20) g.SetDefaultEventuallyPollingInterval(time.Second * 3) g.ConfigureWithFailHandler(func(message string, callerSkip ...int) { g.THelper() allure.Fail(errors.New(message)) t.Fatalf(\"\\n%s %s\", message, debug.Stack()) }) g.THelper = t.Helper return g }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435<\/p>\n<p>utils\/assertions\/common\/solutions.go<\/p>\n<pre><code class=\"go\">package solutions  import ( \"fmt\" \"github.com\/dailymotion\/allure-go\" \"github.com\/onsi\/gomega\" )  func AssertToEqual(g gomega.Gomega, actual interface{}, expected interface{}, description string) { step := fmt.Sprintf(\"Checking that '%s' equals to '%s'\", description, PrettifyValue(expected))  allure.Step(allure.Description(step), allure.Action(func() { g.Expect(actual).To(gomega.Equal(expected), step) })) }<\/code><\/pre>\n<p>\u041d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 AssertToEqual, \u043d\u043e \u0432 \u0432\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u043f\u043e \u0442\u0438\u043f\u0443 AssertIsNotNil, AssertIsNil, AssertToHaveLen \u0438 \u0442.\u0434. \u0418\u0445 \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u044a\u044f\u0432\u0438\u0442\u044c \u0432 \u044d\u0442\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u0438 \u0434\u0430\u043b\u0435\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0441\u0435\u043c\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0443.<\/p>\n<p><strong>\u041b\u0430\u0439\u0444\u0445\u0430\u043a. <\/strong>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u043a\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>expected<\/code> \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u0448\u0430\u0431\u043b\u043e\u043d \u0448\u0430\u0433\u0430 <code>\"Checking that '%s' equals to '%s'\"<\/code>. \u0424\u0443\u043d\u043a\u0446\u0438\u044f <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_testing\/blob\/main\/utils\/assertions\/common\/formatting.go#L7\" rel=\"noopener noreferrer nofollow\">PrettifyValue<\/a> \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0434\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e <code>expected<\/code> \u0447\u0438\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u044b\u0439 \u0432\u0438\u0434. \u0415\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u0449\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445, \u0442\u043e \u0442\u0430\u043a\u043e\u0439 <code>\"Checking that '%s' equals to '%s'\"<\/code> \u0448\u0430\u0431\u043b\u043e\u043d \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0441\u0442\u0440\u043e\u043a. \u0422\u0430\u043a\u0436\u0435 \u0432 go \u0435\u0441\u0442\u044c <a href=\"https:\/\/go.dev\/tour\/moretypes\/1\" rel=\"noopener noreferrer nofollow\">\u043f\u043e\u0438\u043d\u0442\u043e\u0440\u044b<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u043e\u0436\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e. \u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u044b\u0439 \u0442\u0438\u043f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <strong>AssertToEqualString<\/strong>, <strong>AssertToEqualInt<\/strong>, <strong>AssertToEqualFloat<\/strong> \u0438 \u0442.\u0434., \u043d\u043e \u0442\u043e\u0433\u0434\u0430 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0438 \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u0443\u044e \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0448\u0430\u0431\u043b\u043e\u043d. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044f <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_testing\/blob\/main\/utils\/assertions\/common\/formatting.go#L7\" rel=\"noopener noreferrer nofollow\">PrettifyValue<\/a> \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 \u043b\u044e\u0431\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0443\u0436\u0435 \u043e\u0442\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439, \u0447\u0435\u043b\u043e\u0432\u0435\u043a\u043e-\u0447\u0438\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <code>type[value]<\/code>, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 string[MyString], int8[4], int32[1234] \u0438 \u0442.\u0434. \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u044d\u0442\u043e \u043b\u0438\u0448\u044c \u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438, \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0435 \u0448\u0430\u0433\u0438 \u0432 \u043e\u0442\u0447\u0435\u0442\u0435<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0436\u0435 \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L4\" rel=\"noopener noreferrer nofollow\">ArticlesService<\/a>, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0438 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L11\" rel=\"noopener noreferrer nofollow\">Article<\/a><\/p>\n<p>utils\/assertions\/articlesservice\/articles.go<\/p>\n<pre><code class=\"go\">package articlesservicechecks  import ( \"fmt\" articlesservice \"github.com\/Nikita-Filonov\/sample_go_grpc_server\/gen\/proto\" \"github.com\/dailymotion\/allure-go\" \"github.com\/onsi\/gomega\" solutions \"sample_go_grpc_testing\/utils\/assertions\/common\" )  func CheckArticle(g gomega.Gomega, actualArticle, expectedArticle *articlesservice.Article) { allure.Step(allure.Description(\"Checking article\"), allure.Action(func() { solutions.AssertToEqual(g, actualArticle.Id, expectedArticle.Id, \"Article Id\") solutions.AssertToEqual(g, actualArticle.Title, expectedArticle.Title, \"Article Title\") solutions.AssertToEqual(g, actualArticle.Author, expectedArticle.Author, \"Article Author\") solutions.AssertToEqual(g, actualArticle.Description, expectedArticle.Description, \"Article Description\") })) }  func CheckArticleError(g gomega.Gomega, actualError, expectedError *articlesservice.Error) { allure.Step(allure.Description(\"Checking article error\"), allure.Action(func() { solutions.AssertToEqual(g, actualError.Type, expectedError.Type, \"Error Type\") solutions.AssertToEqual(g, actualError.Message, expectedError.Message, \"Error Message\") })) }  func CheckArticleNotFoundError(g gomega.Gomega, actualError *articlesservice.Error, articleId string) { expectedError := articlesservice.Error{ Type:    articlesservice.ErrorType_NOT_FOUND, Message: fmt.Sprintf(\"Article with Id %s not found\", articleId), } CheckArticleError(g, actualError, &amp;expectedError) }<\/code><\/pre>\n<p><strong>\u041b\u0430\u0439\u0444\u0445\u0430\u043a.<\/strong> \u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u0438\u0448\u0435\u0442\u0435 \u0442\u0435\u0441\u0442\u044b \u0434\u043b\u044f gRPC \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0442\u043e \u0443 \u0432\u0430\u0441 \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u0435\u0441\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u0438\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0432\u043b\u043e\u0436\u0435\u043d\u044b \u0432 \u0434\u0440\u0443\u0433\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438. \u0414\u043b\u044f \u043e\u0431\u0449\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0441\u0440\u0430\u0437\u0443 \u0441\u0442\u043e\u0438\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0435\u0441\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c <strong>User<\/strong>, \u0442\u043e\u0433\u0434\u0430 \u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 CheckUser \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0435\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"go\">...  func CheckAccount(g gomega.Gomega, actualAccount, expectedAccount *accountservice.Account) { allure.Step(allure.Description(\"Checking account\"), allure.Action(func() { ...         CheckUser(g, actualAccount.User, expectedAccount.User)         ... })) }  ...<\/code><\/pre>\n<p>\u042d\u0442\u043e \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c, \u043f\u0440\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0438\u043b\u0438 \u0436\u0435 \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u043d\u043e\u0441\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0443\u0436\u0435 \u0438\u043c\u0435\u044e\u0449\u0438\u0435\u0441\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438<\/p>\n<h2>Utils<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e \u043a \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043b\u0438\u0448\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0442\u0438\u043b\u0438\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043c\u043e\u0433\u0443\u0442 \u0432 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>GetRandomArticle<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L11\" rel=\"noopener noreferrer nofollow\">Article<\/a> \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u0443\u044e \u0440\u0430\u043d\u0434\u043e\u043c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>RandomString<\/code> \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_testing\/blob\/main\/utils\/fakers\/strings.go\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a><\/p>\n<p>utils\/controllers\/articlesservice\/articles.go<\/p>\n<pre><code class=\"go\">package articlesservicecontrollers  import ( articlesservice \"github.com\/Nikita-Filonov\/sample_go_grpc_server\/gen\/proto\" \"github.com\/google\/uuid\" \"sample_go_grpc_testing\/utils\/fakers\" )  func GetRandomArticle() *articlesservice.Article { return &amp;articlesservice.Article{ Id:          uuid.NewString(), Title:       fakers.RandomString(30), Author:      fakers.RandomString(20), Description: fakers.RandomString(100), } }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0433\u043e\u0442\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 components, gomega<\/p>\n<p>utils\/common\/setup.go<\/p>\n<pre><code class=\"go\">package common  import ( \"fmt\" \"github.com\/onsi\/gomega\" \"go.uber.org\/dig\" \"sample_go_grpc_testing\/utils\/common\/container\" \"testing\" )  func SetupTesting(t *testing.T) (*container.Components, gomega.Gomega) { g := GetGomega(t)  c, err := container.BuildContainer() g.Expect(err).ShouldNot(gomega.HaveOccurred(), fmt.Sprintf(\"unable to build container: %v\", dig.RootCause(err)))  return c, g }<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0432\u044b\u043d\u0435\u0441\u0435\u043c \u0432\u0441\u0435 \u0447\u0430\u0441\u0442\u0438 \u043a\u0430\u0441\u0430\u044e\u0449\u0438\u0435\u0441\u044f \u043e\u0442\u0447\u0435\u0442\u0430 \u0432 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_testing\/tree\/main\/utils\/reports\" rel=\"noopener noreferrer nofollow\">\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442<\/a>, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0442\u043e\u043c \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u0445<\/p>\n<h2>Testing<\/h2>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b. \u0412\u0441\u0435\u0433\u043e \u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L4\" rel=\"noopener noreferrer nofollow\">ArticlesService<\/a>, \u0447\u0435\u0442\u044b\u0440\u0435 \u043c\u0435\u0442\u043e\u0434\u0430, \u0437\u043d\u0430\u0447\u0438\u0442 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0447\u0435\u0442\u044b\u0440\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043f\u043e\u0437\u0438\u0442\u0438\u0432\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u0430<\/p>\n<p>tests\/articles_test.go <\/p>\n<pre><code class=\"go\">package tests  import ( articlesservice \"github.com\/Nikita-Filonov\/sample_go_grpc_server\/gen\/proto\" \"github.com\/dailymotion\/allure-go\" \"github.com\/dailymotion\/allure-go\/severity\" articlesservicechecks \"sample_go_grpc_testing\/utils\/assertions\/articlesservice\" \"sample_go_grpc_testing\/utils\/common\" articlesservicecontrollers \"sample_go_grpc_testing\/utils\/controllers\/articlesservice\" \"sample_go_grpc_testing\/utils\/reports\" \"testing\" )  func TestGetArticle(t *testing.T) { t.Parallel()  allure.Test(t, reports.ArticlesServiceFeature, reports.ArticlesSuite, allure.Severity(severity.Critical), allure.Tags(reports.ArticlesTag), allure.Name(\"Get article\"), allure.Action(func() { c, g := common.SetupTesting(t)  article := articlesservicecontrollers.GetRandomArticle() c.ArticlesService.CreateArticle(g, &amp;articlesservice.CreateArticleRequest{Article: article})  response := c.ArticlesService.GetArticle(g, &amp;articlesservice.GetArticleRequest{ArticleId: article.Id})  articlesservicechecks.CheckArticle(g, response.GetArticle(), article) }), ) }  func TestCreateArticle(t *testing.T) { t.Parallel()  allure.Test(t, reports.ArticlesServiceFeature, reports.ArticlesSuite, allure.Severity(severity.Critical), allure.Tags(reports.ArticlesTag), allure.Name(\"Create article\"), allure.Action(func() { c, g := common.SetupTesting(t)  article := articlesservicecontrollers.GetRandomArticle() response := c.ArticlesService.CreateArticle(g, &amp;articlesservice.CreateArticleRequest{Article: article})  articlesservicechecks.CheckArticle(g, response.GetArticle(), article) }), ) }  func TestUpdateArticle(t *testing.T) { t.Parallel()  allure.Test(t, reports.ArticlesServiceFeature, reports.ArticlesSuite, allure.Severity(severity.Critical), allure.Tags(reports.ArticlesTag), allure.Name(\"Update article\"), allure.Action(func() { c, g := common.SetupTesting(t)  article := articlesservicecontrollers.GetRandomArticle() c.ArticlesService.CreateArticle(g, &amp;articlesservice.CreateArticleRequest{Article: article})  response := c.ArticlesService.UpdateArticle(g, &amp;articlesservice.UpdateArticleRequest{Article: article})  articlesservicechecks.CheckArticle(g, response.GetArticle(), article) }), ) }  func TestDeleteArticle(t *testing.T) { t.Parallel()  allure.Test(t, reports.ArticlesServiceFeature, reports.ArticlesSuite, allure.Severity(severity.Critical), allure.Tags(reports.ArticlesTag), allure.Name(\"Delete article\"), allure.Action(func() { c, g := common.SetupTesting(t)  article := articlesservicecontrollers.GetRandomArticle() c.ArticlesService.CreateArticle(g, &amp;articlesservice.CreateArticleRequest{Article: article})  c.ArticlesService.DeleteArticle(g, &amp;articlesservice.DeleteArticleRequest{ArticleId: article.Id})  response := c.ArticlesService.GetArticle(g, &amp;articlesservice.GetArticleRequest{ArticleId: article.Id})  articlesservicechecks.CheckArticleNotFoundError(g, response.GetError(), article.Id) }), ) }<\/code><\/pre>\n<h2>Report<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u0442\u0435\u0441\u0442\u043e\u0432 \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/README.md\" rel=\"noopener noreferrer nofollow\">\u0441\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/a><\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0442\u0435\u0441\u0442\u044b \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043e\u0442\u0447\u0435\u0442:<\/p>\n<pre><code class=\"bash\">make test<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043e\u0442\u0447\u0435\u0442:<\/p>\n<pre><code class=\"bash\">allure serve<\/code><\/pre>\n<p>\u041b\u0438\u0431\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043e\u0442\u0447\u0435\u0442 \u0438 \u0432 \u043f\u0430\u043f\u043a\u0435 allure-reports \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0444\u0430\u0439\u043b index.html:<\/p>\n<pre><code>allure generate<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/676\/836\/8a8\/6768368a85389f4c25bc369c2f9328ab.png\" width=\"1914\" height=\"934\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/676\/836\/8a8\/6768368a85389f4c25bc369c2f9328ab.png\"\/><\/figure>\n<p><a href=\"https:\/\/nikita-filonov.github.io\/sample_go_grpc_testing\/\" rel=\"noopener noreferrer nofollow\">\u041f\u043e\u043b\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u043e\u0442\u0447\u0435\u0442\u0430 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0442\u0443\u0442<\/a>. <\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412\u0435\u0441\u044c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u043c\u0438\u00a0<a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_testing\" rel=\"noopener noreferrer nofollow\">\u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d \u043d\u0430 \u043c\u043e\u0435\u043c github.<\/a><\/p>\n<p>\u0412\u0435\u0441\u044c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\" rel=\"noopener noreferrer nofollow\">\u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d \u043d\u0430 \u043c\u043e\u0435\u043c github.<\/a><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/736502\/\"> https:\/\/habr.com\/ru\/articles\/736502\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>\u0412\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c, \u043a\u0430\u043a \u043f\u0438\u0441\u0430\u0442\u044c gRPC \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b  \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u044f\u0437\u044b\u043a\u0430 Go, \u0442\u0430\u043a\u0436\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c Allure \u043e\u0442\u0447\u0435\u0442 <\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u0447\u0438\u0442\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e, \u043d\u0443\u0436\u043d\u043e \u0431\u0430\u0437\u043e\u0432\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u044b:<\/p>\n<ul>\n<li>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <a href=\"https:\/\/en.wikipedia.org\/wiki\/Remote_procedure_call\" rel=\"noopener noreferrer nofollow\">RPC<\/a>? <\/p>\n<\/li>\n<li>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/GRPC\" rel=\"noopener noreferrer nofollow\">gRPC<\/a>?<\/p>\n<\/li>\n<li>\n<p>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/Protocol_Buffers\" rel=\"noopener noreferrer nofollow\">protobuf<\/a>? \u0421\u044e\u0434\u0430 \u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043d\u0435\u0441\u0442\u0438 \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u043e \u0441 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u043e\u043c *.proto \u0444\u0430\u0439\u043b\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u041d\u0435\u043f\u043b\u043e\u0445\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0437\u043d\u0430\u0442\u044c\/\u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u044f\u0437\u044b\u043a\u0430 Go, \u0445\u043e\u0442\u044f \u0431\u044b \u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 docker \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f <a href=\"https:\/\/www.docker.com\/\" rel=\"noopener noreferrer nofollow\">docker<\/a>.<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u0435\u0437 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u0432\u044b\u0448\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u043e \u0447\u0435\u043c \u0438\u0434\u0435\u0442 \u0440\u0435\u0447\u044c<\/p>\n<h2>Requirements<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f gRPC \u0441\u0435\u0440\u0432\u0435\u0440. \u041f\u043e\u0438\u0441\u043a\u0430\u043b \u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u043e\u0440\u0430\u0445 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0435 gRPC \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043d\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0430\u0448\u0435\u043b. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0438 \u0443\u0436\u0435 \u043d\u0430 \u043d\u0435\u0433\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b.<\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\" rel=\"noopener noreferrer nofollow\">\u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d \u043d\u0430 \u043c\u043e\u0435\u043c github<\/a>. \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043f\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/README.md\" rel=\"noopener noreferrer nofollow\">\u043f\u0440\u0438\u043b\u0430\u0433\u0430\u044e\u0442\u0441\u044f<\/a>. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043a\u0430\u043a-\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c\/\u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u0438\u043c\u0435\u044e\u0449\u0438\u0439\u0441\u044f <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442<\/a>, \u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0440\u0430\u0437\u0443 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u043a\u0446\u0438\u044e <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server#setup-protobuf\" rel=\"noopener noreferrer nofollow\">Setup protobuf<\/a><\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u043d\u043e \u0434\u0432\u0443\u043c\u044f \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438:<\/p>\n<ul>\n<li>\n<p>\u041f\u043e <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server#setup-project\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u0439<\/a> \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043d\u0430 \u0432\u0430\u0448\u0443 OS \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440;<\/p>\n<\/li>\n<li>\n<p>\u041b\u0438\u0431\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u043d\u0443\u0442\u0440\u0438 docker, \u0442\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server#setup-project-in-docker\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u0443 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044e<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 localhost:8000, \u043d\u0443 \u0438\u043b\u0438 127.0.0.1:8000. \u0414\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0438\u043c\u0435\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 &#171;\u043f\u043e\u0442\u044b\u043a\u0430\u0442\u044c&#187; \u0441\u0435\u0440\u0432\u0435\u0440. \u0422\u0430\u043a\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c Postman, \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u0441\u043a\u0442\u043e\u043f\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 gRPC \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d. <a href=\"https:\/\/blog.postman.com\/postman-now-supports-grpc\/\" rel=\"noopener noreferrer nofollow\">\u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 <\/a>\u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c Postman \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 c gRPC<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0430<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u042d\u0442\u043e\u0442 \u0436\u0435 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u0430\u0445<\/p>\n<pre><code>syntax = \"proto3\"; option go_package = \".\/;articlesservice\";  service ArticlesService {   rpc GetArticle (GetArticleRequest) returns (GetArticleResponse);   rpc CreateArticle(CreateArticleRequest) returns (CreateArticleResponse);   rpc UpdateArticle(UpdateArticleRequest) returns (UpdateArticleResponse);   rpc DeleteArticle(DeleteArticleRequest) returns (DeleteArticleResponse); }  message Article {   string id = 1;   string title = 2;   string author = 3;   string description = 4; }  enum ErrorType {   NOT_FOUND = 0;   ALREADY_EXISTS = 1;   UNSPECIFIED = 2; }  message Error {   string message = 1;   ErrorType type = 2; }  message GetArticleRequest {   string article_id = 1; }  message GetArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message CreateArticleRequest {   Article article = 1; }  message CreateArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message UpdateArticleRequest {   Article article = 1; }  message UpdateArticleResponse {   oneof result {     Error error = 1;     Article article = 2;   } }  message DeleteArticleRequest {   string article_id = 1; }  message DeleteArticleResponse {}<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u043c\u0435\u0442\u043e\u0434\u044b:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L5\" rel=\"noopener noreferrer nofollow\">GetArticle<\/a> &#8212; \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e id. \u0417\u0430\u043f\u0440\u043e\u0441 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L29\" rel=\"noopener noreferrer nofollow\">GetArticleRequest<\/a>  \u0438 \u043e\u0442\u0432\u0435\u0442 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L33\" rel=\"noopener noreferrer nofollow\">GetArticleResponse<\/a>;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L6\" rel=\"noopener noreferrer nofollow\">CreateArticle<\/a> &#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u0417\u0430\u043f\u0440\u043e\u0441 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L40\" rel=\"noopener noreferrer nofollow\">CreateArticleRequest<\/a>  \u0438 \u043e\u0442\u0432\u0435\u0442 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L44\" rel=\"noopener noreferrer nofollow\">CreateArticleResponse<\/a>;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L7\" rel=\"noopener noreferrer nofollow\">UpdateArticle<\/a> &#8212; \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u0417\u0430\u043f\u0440\u043e\u0441 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L51\" rel=\"noopener noreferrer nofollow\">UpdateArticleRequest<\/a> \u0438 \u043e\u0442\u0432\u0435\u0442 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L55\" rel=\"noopener noreferrer nofollow\">UpdateArticleResponse<\/a>;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L8\" rel=\"noopener noreferrer nofollow\">DeleteArticle<\/a> &#8212; \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u0417\u0430\u043f\u0440\u043e\u0441<a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L62\" rel=\"noopener noreferrer nofollow\"> DeleteArticleRequest<\/a> \u0438 \u043e\u0442\u0432\u0435\u0442 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L66\" rel=\"noopener noreferrer nofollow\">DeleteArticleResponse<\/a>;<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0436\u0435, \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L11\" rel=\"noopener noreferrer nofollow\">Article<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043c\u0435\u0442\u043e\u0434\u0430\u0445 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438. \u042d\u0442\u0430 \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043b\u0438\u0448\u043d\u0438\u0439 \u0440\u0430\u0437 \u043d\u0435 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434<\/p>\n<p>\u0412\u044b\u0448\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442 \u043d\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 CRUD \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438. \u041d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u043d\u043e \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u043c \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u0438 \u0442\u0430\u043a\u043e\u0439<\/p>\n<p>\u0421\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/grpc\/grpc-go\" rel=\"noopener noreferrer nofollow\">grpc-go<\/a>  &#8212; \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 gRPC \u043a\u043b\u0438\u0435\u043d\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/dailymotion\/allure-go\" rel=\"noopener noreferrer nofollow\">allure-go<\/a> &#8212; \u0434\u043b\u044f Allure \u043e\u0442\u0447\u0435\u0442\u0430;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/go-yaml\/yaml\/tree\/v3\" rel=\"noopener noreferrer nofollow\">yaml<\/a> &#8212; \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f yaml \u0444\u0430\u0439\u043b\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/onsi\/gomega\" rel=\"noopener noreferrer nofollow\">gomega<\/a> &#8212; \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/uber-go\/zap\" rel=\"noopener noreferrer nofollow\">zap<\/a> &#8212; \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/uber-go\/dig\" rel=\"noopener noreferrer nofollow\">dig<\/a> &#8212; \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 dependency injection;<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\" rel=\"noopener noreferrer nofollow\">sample_go_grpc_server<\/a> &#8212; \u044d\u0442\u043e \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u0440\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0433\u043e\u0432\u043e\u0440\u0438\u043b\u043e\u0441\u044c \u0432\u044b\u0448\u0435, \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u0430\u043a\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u0442\u044c \u0435\u0449\u0435 \u0440\u044f\u0434 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a \u0431\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0438\u043a\u0443\u0434\u0430, \u043d\u043e \u0432\u0441\u0435 \u043e\u043d\u0438 \u0443\u0436\u0435 <a href=\"https:\/\/pkg.go.dev\/std\" rel=\"noopener noreferrer nofollow\">\u0432\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432 go<\/a><\/p>\n<h2>Configuration<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0441 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u043c\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043f\u0438\u0448\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438<\/p>\n<p>infrastructure\/config-local.yml<\/p>\n<pre><code class=\"yaml\">articlesService:   port: 8000   host: localhost  logger:   isDevMode: true   level: debug<\/code><\/pre>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\" rel=\"noopener noreferrer nofollow\">\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441\u0442\u0430\u0442\u0435\u0439<\/a> \u0438 \u043b\u043e\u0433\u0433\u0435\u0440\u0430, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0431\u043e\u043b\u044c\u0448\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432, \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0445 \u0432 \u044d\u0442\u043e\u0442 <strong>infrastructure\/config-local.yml<\/strong> \u0444\u0430\u0439\u043b<\/p>\n<p><strong>\u041b\u0430\u0439\u0444\u0445\u0430\u043a. <\/strong>\u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0442\u0435\u0441\u0442\u044b \u0441\u0440\u0430\u0437\u0443 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\u0445, \u0442\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u043e\u0434 \u043a\u0430\u0436\u0434\u043e\u0435 \u0438\u0437 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0439, \u043a\u0430\u043a \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_testing\/tree\/main\/infrastructure\" rel=\"noopener noreferrer nofollow\">\u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435<\/a>:<\/p>\n<ul>\n<li>\n<p>config-dev.yml<\/p>\n<\/li>\n<li>\n<p>config-local.yml<\/p>\n<\/li>\n<li>\n<p>config-stable.yml<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f ENV, \u0431\u0443\u0434\u0435\u043c \u0431\u0440\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043d\u0438\u0436\u0435, \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u043c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0447\u0442\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0432 \u0444\u0430\u0439\u043b\u0435 <strong>utils\/config\/internal.go<\/strong><\/p>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u0438, \u0432\u043d\u0443\u0442\u0440\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0443\u0442 &#171;\u043f\u043e\u043c\u0435\u0449\u0430\u0442\u044c\u0441\u044f&#187; \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 <strong>infrastructure\/config-{env}.yml<\/strong><\/p>\n<p>utils\/config\/models.go<\/p>\n<pre><code class=\"go\">package config  type Env string type LogLevel string  const ( DebugLogLevel   LogLevel = \"debug\" InfoLogLevel    LogLevel = \"info\" WarningLogLevel LogLevel = \"warning\" ErrorLogLevel   LogLevel = \"error\" )  type Logger struct { Level     LogLevel `yaml:\"level\"` IsDevMode bool     `yaml:\"isDevMode\"` }  type GrpcService struct { Port               int32  `yaml:\"port\"` Host               string `yaml:\"host\"` InsecureSkipVerify bool   `yaml:\"insecureSkipVerify\"` }  type Config struct { Logger          Logger      `yaml:\"logger\" validate:\"required\"` Articlesservice GrpcService `yaml:\"articlesService\" validate:\"required\"` }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0430\u0440\u0441\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u0438 &#171;\u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c&#187; \u0438\u0445 \u043f\u043e \u043c\u043e\u0434\u0435\u043b\u044f\u043c \u0438\u0437 <strong>utils\/config\/models.go<\/strong> <\/p>\n<p>utils\/config\/internal.go<\/p>\n<pre><code class=\"go\">package config  import ( \"fmt\" \"gopkg.in\/yaml.v3\" \"io\/ioutil\" \"os\" )  func getEnv() (Env, error) { env := os.Getenv(\"ENV\")  if len(env) == 0 { return \"\", fmt.Errorf(\"cannot parse env variable\") }  return Env(env), nil }  func readConfig(configPath string, config interface{}) error { if configPath == `` { return fmt.Errorf(`no config path`) }  configBytes, err := ioutil.ReadFile(configPath)  if err != nil { return err }  if err = yaml.Unmarshal(configBytes, config); err != nil { return err }  return nil }  func NewConfig() (Config, error) { var config Config  env, err := getEnv()  if err != nil { return Config{}, err }  err = readConfig(fmt.Sprintf(\"..\/infrastructure\/config-%s.yml\", env), &amp;config)  if err != nil { return Config{}, err }  return config, nil }<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0432 \u043f\u0443\u0442\u044c \u0434\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a <code>\"..\/infrastructure\/config-%s.yml\"<\/code><\/p>\n<h2>Service<\/h2>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0430\u0448\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c, \u043d\u043e \u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c gRPC \u043a\u043b\u0438\u0435\u043d\u0442<\/p>\n<p>utils\/services\/grpc\/client.go<\/p>\n<pre><code class=\"go\">package grpc  import ( \"fmt\" \"google.golang.org\/grpc\" \"google.golang.org\/grpc\/credentials\/insecure\" \"sample_go_grpc_testing\/utils\/config\" )  func GetGrpcClient(grpcService config.GrpcService) (*grpc.ClientConn, error) { address := fmt.Sprintf(\"%s:%d\", grpcService.Host, grpcService.Port)  return grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials())) }<\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>GetGrpcClient<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 <code>Host<\/code>, <code>Port<\/code>, \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0435\u0433\u043e<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0438\u0441\u0430 <a href=\"https:\/\/github.com\/Nikita-Filonov\/sample_go_grpc_server\/blob\/main\/proto\/articles_service.proto#L4\" rel=\"noopener noreferrer nofollow\">ArticlesService<\/a>. \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>client.go &#8212; \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442 \u0438 \u0431\u0438\u043b\u0434\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442;<\/p>\n<\/li>\n<li>\n<p>api.go &#8212; \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u041f\u043e \u0441\u0443\u0442\u0438 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u0435\u0440\u0442\u043a\u0430, \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0430\u043a\u0438\u043d\u0435\u043c \u043b\u043e\u0433\u0438, \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>steps.go &#8212; \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a \u043c\u0435\u0442\u043e\u0434\u0430\u043c \u0438\u0437 api.go allure \u0448\u0430\u0433\u0438, \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435, \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0432\u0441\u0435 \u0447\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 \u043e\u0442\u0447\u0435\u0442\u043e\u043c. \u041c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0441\u043b\u043e\u0439, \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u044b \u0448\u0430\u0433\u0438 \u043e\u0442\u0447\u0435\u0442\u0430, \u043b\u0438\u0431\u043e \u0441\u0430\u043c \u043e\u0442\u0447\u0435\u0442;<\/p>\n<\/li>\n<\/ul>\n<p>core\/articlesservice\/client.go<\/p>\n<pre><code class=\"go\">package articlesservice  import ( articlesservice \"github.com\/Nikita-Filonov\/sample_go_grpc_server\/gen\/proto\" \"sample_go_grpc_testing\/utils\/config\" \"sample_go_grpc_testing\/utils\/logger\" \"sample_go_grpc_testing\/utils\/services\/grpc\" )  type Client struct { articlesservice.ArticlesServiceClient logger *logger.CtxLogger }  func NewClient(logger logger.Service, conf config.Config) (*Client, error) { conn, err := grpc.GetGrpcClient(conf.Articlesservice)  if err != nil { return &amp;Client{}, err }  return &amp;Client{ logger:                logger.NewPrefix(\"ARTICLES_SERVICE.GRPC.CLIENT\"), ArticlesServiceClient: articlesservice.NewArticlesServiceClient(conn), }, nil }<\/code><\/pre>\n<p>core\/articlesservice\/api.go<\/p>\n<pre><code class=\"go\">package articlesservice  import ( \"context\" articlesservice \"github.com\/Nikita-Filonov\/sample_go_grpc_server\/gen\/proto\" \"github.com\/onsi\/gomega\" )  func (c *Client) getArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.GetArticleRequest, ) *articlesservice.GetArticleResponse { c.logger.InfofJSON(\"GetArticleRequest\", request)  res, err := c.ArticlesServiceClient.GetArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), \"GetArticle error\")  c.logger.InfofJSON(\"GetArticleResponse\", res) return res }  func (c *Client) createArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.CreateArticleRequest, ) *articlesservice.CreateArticleResponse { c.logger.InfofJSON(\"CreateArticleRequest\", request)  res, err := c.ArticlesServiceClient.CreateArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), \"CreateArticle error\")  c.logger.InfofJSON(\"CreateArticleResponse\", res) return res }  func (c *Client) updateArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.UpdateArticleRequest, ) *articlesservice.UpdateArticleResponse { c.logger.InfofJSON(\"UpdateArticleRequest\", request)  res, err := c.ArticlesServiceClient.UpdateArticle(ctx, request) g.Expect(err).ShouldNot(gomega.HaveOccurred(), \"UpdateArticle error\")  c.logger.InfofJSON(\"UpdateArticleResponse\", res) return res }  func (c *Client) deleteArticle( g gomega.Gomega, ctx context.Context, request *articlesservice.DeleteArticleRequest, ) *articlesservice.DeleteArticleResponse { c.logger.InfofJSON(\"DeleteArticleRequest\",<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-348609","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/348609","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=348609"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/348609\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=348609"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=348609"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=348609"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}