/*
* Copyright 2017 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.server.grpc;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import java.util.Map;
import java.util.function.Function;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.protobuf.Descriptors.ServiceDescriptor;
import com.linecorp.armeria.common.grpc.GrpcSerializationFormats;
import com.linecorp.armeria.common.http.HttpHeaders;
import com.linecorp.armeria.grpc.testing.Messages.CompressionType;
import com.linecorp.armeria.grpc.testing.Messages.ReconnectInfo;
import com.linecorp.armeria.grpc.testing.Messages.SimpleRequest;
import com.linecorp.armeria.grpc.testing.Messages.SimpleResponse;
import com.linecorp.armeria.grpc.testing.Messages.StreamingOutputCallRequest;
import com.linecorp.armeria.grpc.testing.Messages.TestMessage;
import com.linecorp.armeria.grpc.testing.ReconnectServiceGrpc;
import com.linecorp.armeria.grpc.testing.ReconnectServiceGrpc.ReconnectServiceImplBase;
import com.linecorp.armeria.grpc.testing.TestServiceGrpc;
import com.linecorp.armeria.grpc.testing.TestServiceGrpc.TestServiceImplBase;
import com.linecorp.armeria.protobuf.EmptyProtos.Empty;
import com.linecorp.armeria.server.PathMapping;
import com.linecorp.armeria.server.ServiceConfig;
import com.linecorp.armeria.server.VirtualHostBuilder;
import com.linecorp.armeria.server.docs.EndpointInfo;
import com.linecorp.armeria.server.docs.EnumInfo;
import com.linecorp.armeria.server.docs.EnumValueInfo;
import com.linecorp.armeria.server.docs.FieldInfo;
import com.linecorp.armeria.server.docs.FieldRequirement;
import com.linecorp.armeria.server.docs.MethodInfo;
import com.linecorp.armeria.server.docs.ServiceInfo;
import com.linecorp.armeria.server.docs.ServiceSpecification;
import com.linecorp.armeria.server.docs.StructInfo;
import com.linecorp.armeria.server.docs.TypeSignature;
import com.linecorp.armeria.server.grpc.GrpcDocServicePlugin.ServiceEntry;
import io.netty.util.AsciiString;
public class GrpcDocServicePluginTest {
private static final ServiceDescriptor TEST_SERVICE_DESCRIPTOR =
com.linecorp.armeria.grpc.testing.Test.getDescriptor()
.findServiceByName("TestService");
private GrpcDocServicePlugin generator = new GrpcDocServicePlugin();
@Test
public void services() throws Exception {
ServiceConfig testService = new ServiceConfig(
new VirtualHostBuilder().build(),
PathMapping.ofPrefix("/test"),
new GrpcServiceBuilder().addService(mock(TestServiceImplBase.class)).build());
HttpHeaders testExampleHeaders = HttpHeaders.of(AsciiString.of("test"), "service");
ServiceConfig reconnectService = new ServiceConfig(
new VirtualHostBuilder().build(),
PathMapping.ofPrefix("/reconnect"),
new GrpcServiceBuilder().addService(mock(ReconnectServiceImplBase.class)).build());
HttpHeaders reconnectExampleHeaders = HttpHeaders.of(AsciiString.of("reconnect"), "never");
ServiceSpecification specification = generator.generateSpecification(
ImmutableSet.of(testService, reconnectService));
Map<String, ServiceInfo> services = specification
.services()
.stream()
.collect(toImmutableMap(ServiceInfo::name, Function.identity()));
assertThat(services).containsOnlyKeys(TestServiceGrpc.SERVICE_NAME,
ReconnectServiceGrpc.SERVICE_NAME);
ServiceInfo testServiceInfo = services.get(TestServiceGrpc.SERVICE_NAME);
assertThat(testServiceInfo.endpoints())
.containsExactly(new EndpointInfo(
"*",
"/test/armeria.grpc.testing.TestService/*",
"",
GrpcSerializationFormats.PROTO,
ImmutableSet.of(GrpcSerializationFormats.PROTO)));
ServiceInfo fooServiceInfo = services.get(ReconnectServiceGrpc.SERVICE_NAME);
assertThat(fooServiceInfo.endpoints())
.containsExactly(new EndpointInfo(
"*",
"/reconnect/armeria.grpc.testing.ReconnectService/*",
"",
GrpcSerializationFormats.PROTO,
ImmutableSet.of(GrpcSerializationFormats.PROTO)));
}
@Test
public void newEnumInfo() throws Exception {
EnumInfo enumInfo = generator.newEnumInfo(CompressionType.getDescriptor());
assertThat(enumInfo).isEqualTo(new EnumInfo(
"armeria.grpc.testing.CompressionType",
ImmutableList.of(new EnumValueInfo("NONE"),
new EnumValueInfo("GZIP"),
new EnumValueInfo("DEFLATE"))));
}
@Test
public void newListInfo() throws Exception {
TypeSignature list = generator.newFieldTypeInfo(
ReconnectInfo.getDescriptor().findFieldByNumber(ReconnectInfo.BACKOFF_MS_FIELD_NUMBER));
assertThat(list).isEqualTo(TypeSignature.ofContainer("repeated", GrpcDocServicePlugin.INT32));
}
@Test
public void newMapInfo() throws Exception {
TypeSignature map = generator.newFieldTypeInfo(
StreamingOutputCallRequest.getDescriptor().findFieldByNumber(
StreamingOutputCallRequest.OPTIONS_FIELD_NUMBER));
assertThat(map).isEqualTo(TypeSignature.ofMap(GrpcDocServicePlugin.STRING, GrpcDocServicePlugin.INT32));
}
@Test
public void newMethodInfo() throws Exception {
MethodInfo methodInfo = generator.newMethodInfo(
TEST_SERVICE_DESCRIPTOR.findMethodByName("UnaryCall"));
assertThat(methodInfo.name()).isEqualTo("UnaryCall");
assertThat(methodInfo.returnTypeSignature().name()).isEqualTo("armeria.grpc.testing.SimpleResponse");
assertThat(methodInfo.returnTypeSignature().namedTypeDescriptor())
.contains(SimpleResponse.getDescriptor());
assertThat(methodInfo.parameters()).hasSize(1);
assertThat(methodInfo.parameters().get(0).name()).isEqualTo("request");
assertThat(methodInfo.parameters().get(0).typeSignature().name())
.isEqualTo("armeria.grpc.testing.SimpleRequest");
assertThat(methodInfo.parameters().get(0).typeSignature().namedTypeDescriptor())
.contains(SimpleRequest.getDescriptor());
assertThat(methodInfo.exceptionTypeSignatures()).isEmpty();
assertThat(methodInfo.docString()).isNull();
}
@Test
public void newServiceInfo() throws Exception {
ServiceInfo service = generator.newServiceInfo(
new ServiceEntry(
TEST_SERVICE_DESCRIPTOR,
ImmutableList.of(
new EndpointInfo("*", "/foo", "a",
GrpcSerializationFormats.PROTO,
ImmutableSet.of(GrpcSerializationFormats.PROTO)),
new EndpointInfo("*", "/debug/foo", "b",
GrpcSerializationFormats.JSON,
ImmutableSet.of(GrpcSerializationFormats.JSON)))));
assertThat(service.endpoints()).hasSize(2);
assertThat(service.endpoints()).containsExactlyInAnyOrder(
new EndpointInfo("*", "/debug/foo", "b",
GrpcSerializationFormats.JSON,
ImmutableSet.of(GrpcSerializationFormats.JSON)),
new EndpointInfo("*", "/foo", "a",
GrpcSerializationFormats.PROTO,
ImmutableSet.of(GrpcSerializationFormats.PROTO)));
Map<String, MethodInfo> functions = service
.methods()
.stream()
.collect(toImmutableMap(MethodInfo::name, Function.identity()));
assertThat(functions).hasSize(7);
MethodInfo emptyCall = functions.get("EmptyCall");
assertThat(emptyCall.name()).isEqualTo("EmptyCall");
assertThat(emptyCall.parameters())
.containsExactly(
new FieldInfo(
"request",
FieldRequirement.REQUIRED,
TypeSignature.ofNamed("armeria.grpc.testing.Empty", Empty.getDescriptor())));
assertThat(emptyCall.returnTypeSignature())
.isEqualTo(TypeSignature.ofNamed("armeria.grpc.testing.Empty", Empty.getDescriptor()));
// Just sanity check that all methods are present, function conversion is more thoroughly tested in
// newMethodInfo()
assertThat(functions.get("UnaryCall").name()).isEqualTo("UnaryCall");
assertThat(functions.get("StreamingOutputCall").name()).isEqualTo("StreamingOutputCall");
assertThat(functions.get("StreamingInputCall").name()).isEqualTo("StreamingInputCall");
assertThat(functions.get("FullDuplexCall").name()).isEqualTo("FullDuplexCall");
assertThat(functions.get("HalfDuplexCall").name()).isEqualTo("HalfDuplexCall");
assertThat(functions.get("UnimplementedCall").name()).isEqualTo("UnimplementedCall");
}
@Test
public void newStructInfo() throws Exception {
StructInfo structInfo = (StructInfo) generator.newStructInfo(TestMessage.getDescriptor());
assertThat(structInfo.name()).isEqualTo("armeria.grpc.testing.TestMessage");
assertThat(structInfo.fields()).hasSize(18);
assertThat(structInfo.fields().get(0).name()).isEqualTo("bool");
assertThat(structInfo.fields().get(0).typeSignature()).isEqualTo(GrpcDocServicePlugin.BOOL);
assertThat(structInfo.fields().get(1).name()).isEqualTo("int32");
assertThat(structInfo.fields().get(1).typeSignature()).isEqualTo(GrpcDocServicePlugin.INT32);
assertThat(structInfo.fields().get(2).name()).isEqualTo("int64");
assertThat(structInfo.fields().get(2).typeSignature()).isEqualTo(GrpcDocServicePlugin.INT64);
assertThat(structInfo.fields().get(3).name()).isEqualTo("uint32");
assertThat(structInfo.fields().get(3).typeSignature()).isEqualTo(GrpcDocServicePlugin.UINT32);
assertThat(structInfo.fields().get(4).name()).isEqualTo("uint64");
assertThat(structInfo.fields().get(4).typeSignature()).isEqualTo(GrpcDocServicePlugin.UINT64);
assertThat(structInfo.fields().get(5).name()).isEqualTo("sint32");
assertThat(structInfo.fields().get(5).typeSignature()).isEqualTo(GrpcDocServicePlugin.SINT32);
assertThat(structInfo.fields().get(6).name()).isEqualTo("sint64");
assertThat(structInfo.fields().get(6).typeSignature()).isEqualTo(GrpcDocServicePlugin.SINT64);
assertThat(structInfo.fields().get(7).name()).isEqualTo("fixed32");
assertThat(structInfo.fields().get(7).typeSignature()).isEqualTo(GrpcDocServicePlugin.FIXED32);
assertThat(structInfo.fields().get(8).name()).isEqualTo("fixed64");
assertThat(structInfo.fields().get(8).typeSignature()).isEqualTo(GrpcDocServicePlugin.FIXED64);
assertThat(structInfo.fields().get(9).name()).isEqualTo("float");
assertThat(structInfo.fields().get(9).typeSignature()).isEqualTo(GrpcDocServicePlugin.FLOAT);
assertThat(structInfo.fields().get(10).name()).isEqualTo("double");
assertThat(structInfo.fields().get(10).typeSignature()).isEqualTo(GrpcDocServicePlugin.DOUBLE);
assertThat(structInfo.fields().get(11).name()).isEqualTo("string");
assertThat(structInfo.fields().get(11).typeSignature()).isEqualTo(GrpcDocServicePlugin.STRING);
assertThat(structInfo.fields().get(12).name()).isEqualTo("bytes");
assertThat(structInfo.fields().get(12).typeSignature()).isEqualTo(GrpcDocServicePlugin.BYTES);
assertThat(structInfo.fields().get(13).name()).isEqualTo("test_enum");
assertThat(structInfo.fields().get(13).typeSignature().signature())
.isEqualTo("armeria.grpc.testing.TestEnum");
assertThat(structInfo.fields().get(14).name()).isEqualTo("nested");
assertThat(structInfo.fields().get(14).typeSignature().signature())
.isEqualTo("armeria.grpc.testing.TestMessage.Nested");
assertThat(structInfo.fields().get(15).name()).isEqualTo("strings");
assertThat(structInfo.fields().get(15).typeSignature().typeParameters())
.containsExactly(GrpcDocServicePlugin.STRING);
assertThat(structInfo.fields().get(16).name()).isEqualTo("map");
assertThat(structInfo.fields().get(16).typeSignature().typeParameters())
.containsExactly(GrpcDocServicePlugin.STRING, GrpcDocServicePlugin.INT32);
assertThat(structInfo.fields().get(17).name()).isEqualTo("self");
assertThat(structInfo.fields().get(17).typeSignature().signature())
.isEqualTo("armeria.grpc.testing.TestMessage");
}
}