Skip to content

Commit

Permalink
grpc-js-xds: Add unit test framework
Browse files Browse the repository at this point in the history
  • Loading branch information
murgatroid99 committed Mar 10, 2023
1 parent 9264d58 commit 6bc6b86
Show file tree
Hide file tree
Showing 26 changed files with 1,721 additions and 1 deletion.
3 changes: 2 additions & 1 deletion packages/grpc-js-xds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"pretest": "npm run compile",
"posttest": "npm run check",
"generate-types": "proto-loader-gen-types --keepCase --longs String --enums String --defaults --oneofs --includeComments --includeDirs deps/envoy-api/ deps/xds/ deps/googleapis/ deps/protoc-gen-validate/ -O src/generated/ --grpcLib @grpc/grpc-js envoy/service/discovery/v3/ads.proto envoy/service/load_stats/v3/lrs.proto envoy/config/listener/v3/listener.proto envoy/config/route/v3/route.proto envoy/config/cluster/v3/cluster.proto envoy/config/endpoint/v3/endpoint.proto envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto udpa/type/v1/typed_struct.proto xds/type/v3/typed_struct.proto envoy/extensions/filters/http/fault/v3/fault.proto envoy/service/status/v3/csds.proto",
"generate-interop-types": "proto-loader-gen-types --keep-case --longs String --enums String --defaults --oneofs --json --includeComments --includeDirs proto/ -O interop/generated --grpcLib @grpc/grpc-js grpc/testing/test.proto"
"generate-interop-types": "proto-loader-gen-types --keep-case --longs String --enums String --defaults --oneofs --json --includeComments --includeDirs proto/ -O interop/generated --grpcLib @grpc/grpc-js grpc/testing/test.proto",
"generate-test-types": "proto-loader-gen-types --keep-case --longs String --enums String --defaults --oneofs --json --includeComments --includeDirs proto/ -O test/generated --grpcLib @grpc/grpc-js grpc/testing/echo.proto"
},
"repository": {
"type": "git",
Expand Down
70 changes: 70 additions & 0 deletions packages/grpc-js-xds/proto/grpc/testing/echo.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

// Copyright 2015 gRPC authors.
//
// Licensed 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.

syntax = "proto3";

package grpc.testing;

import "grpc/testing/echo_messages.proto";
import "grpc/testing/simple_messages.proto";

service EchoTestService {
rpc Echo(EchoRequest) returns (EchoResponse);
rpc Echo1(EchoRequest) returns (EchoResponse);
rpc Echo2(EchoRequest) returns (EchoResponse);
rpc CheckDeadlineUpperBound(SimpleRequest) returns (StringValue);
rpc CheckDeadlineSet(SimpleRequest) returns (StringValue);
// A service which checks that the initial metadata sent over contains some
// expected key value pair
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);
rpc RequestStream(stream EchoRequest) returns (EchoResponse);
rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
rpc Unimplemented(EchoRequest) returns (EchoResponse);
rpc UnimplementedBidi(stream EchoRequest) returns (stream EchoResponse);
}

service EchoTest1Service {
rpc Echo(EchoRequest) returns (EchoResponse);
rpc Echo1(EchoRequest) returns (EchoResponse);
rpc Echo2(EchoRequest) returns (EchoResponse);
// A service which checks that the initial metadata sent over contains some
// expected key value pair
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);
rpc RequestStream(stream EchoRequest) returns (EchoResponse);
rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
rpc Unimplemented(EchoRequest) returns (EchoResponse);
}

service EchoTest2Service {
rpc Echo(EchoRequest) returns (EchoResponse);
rpc Echo1(EchoRequest) returns (EchoResponse);
rpc Echo2(EchoRequest) returns (EchoResponse);
// A service which checks that the initial metadata sent over contains some
// expected key value pair
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);
rpc RequestStream(stream EchoRequest) returns (EchoResponse);
rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
rpc Unimplemented(EchoRequest) returns (EchoResponse);
}

service UnimplementedEchoService {
rpc Unimplemented(EchoRequest) returns (EchoResponse);
}

// A service without any rpc defined to test coverage.
service NoRpcService {}
74 changes: 74 additions & 0 deletions packages/grpc-js-xds/proto/grpc/testing/echo_messages.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

// Copyright 2015 gRPC authors.
//
// Licensed 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.

syntax = "proto3";

package grpc.testing;

option cc_enable_arenas = true;

import "grpc/testing/xds/v3/orca_load_report.proto";

// Message to be echoed back serialized in trailer.
message DebugInfo {
repeated string stack_entries = 1;
string detail = 2;
}

// Error status client expects to see.
message ErrorStatus {
int32 code = 1;
string error_message = 2;
string binary_error_details = 3;
}

message RequestParams {
bool echo_deadline = 1;
int32 client_cancel_after_us = 2;
int32 server_cancel_after_us = 3;
bool echo_metadata = 4;
bool check_auth_context = 5;
int32 response_message_length = 6;
bool echo_peer = 7;
string expected_client_identity = 8; // will force check_auth_context.
bool skip_cancelled_check = 9;
string expected_transport_security_type = 10;
DebugInfo debug_info = 11;
bool server_die = 12; // Server should not see a request with this set.
string binary_error_details = 13;
ErrorStatus expected_error = 14;
int32 server_sleep_us = 15; // sleep when invoking server for deadline tests
int32 backend_channel_idx = 16; // which backend to send request to
bool echo_metadata_initially = 17;
bool server_notify_client_when_started = 18;
xds.data.orca.v3.OrcaLoadReport backend_metrics = 19;
bool echo_host_from_authority_header = 20;
}

message EchoRequest {
string message = 1;
RequestParams param = 2;
}

message ResponseParams {
int64 request_deadline = 1;
string host = 2;
string peer = 3;
}

message EchoResponse {
string message = 1;
ResponseParams param = 2;
}
26 changes: 26 additions & 0 deletions packages/grpc-js-xds/proto/grpc/testing/simple_messages.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

// Copyright 2018 gRPC authors.
//
// Licensed 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.

syntax = "proto3";

package grpc.testing;

message SimpleRequest {}

message SimpleResponse {}

message StringValue {
string message = 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2020 The gRPC Authors
//
// Licensed 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.

// Local copy of Envoy xDS proto file, used for testing only.

syntax = "proto3";

package xds.data.orca.v3;

// See section `ORCA load report format` of the design document in
// :ref:`https://github.com/envoyproxy/envoy/issues/6614`.

message OrcaLoadReport {
// CPU utilization expressed as a fraction of available CPU resources. This
// should be derived from the latest sample or measurement.
double cpu_utilization = 1;

// Memory utilization expressed as a fraction of available memory
// resources. This should be derived from the latest sample or measurement.
double mem_utilization = 2;

// Total RPS being served by an endpoint. This should cover all services that an endpoint is
// responsible for.
uint64 rps = 3;

// Application specific requests costs. Each value is an absolute cost (e.g. 3487 bytes of
// storage) associated with the request.
map<string, double> request_cost = 4;

// Resource utilization values. Each value is expressed as a fraction of total resources
// available, derived from the latest sample or measurement.
map<string, double> utilization = 5;
}
105 changes: 105 additions & 0 deletions packages/grpc-js-xds/test/backend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2023 gRPC authors.
*
* Licensed 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.
*
*/

import { loadPackageDefinition, sendUnaryData, Server, ServerCredentials, ServerUnaryCall, UntypedServiceImplementation } from "@grpc/grpc-js";
import { loadSync } from "@grpc/proto-loader";
import { ProtoGrpcType } from "./generated/echo";
import { EchoRequest__Output } from "./generated/grpc/testing/EchoRequest";
import { EchoResponse } from "./generated/grpc/testing/EchoResponse";

const loadedProtos = loadPackageDefinition(loadSync(
[
'grpc/testing/echo.proto'
],
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
json: true,
includeDirs: [
// Paths are relative to build/test
__dirname + '/../../proto/'
],
})) as unknown as ProtoGrpcType;

export class Backend {
private server: Server;
private receivedCallCount = 0;
private callListeners: (() => void)[] = [];
private port: number | null = null;
constructor() {
this.server = new Server();
this.server.addService(loadedProtos.grpc.testing.EchoTestService.service, this as unknown as UntypedServiceImplementation);
}
Echo(call: ServerUnaryCall<EchoRequest__Output, EchoResponse>, callback: sendUnaryData<EchoResponse>) {
// call.request.params is currently ignored
this.addCall();
callback(null, {message: call.request.message});
}

addCall() {
this.receivedCallCount++;
this.callListeners.forEach(listener => listener());
}

onCall(listener: () => void) {
this.callListeners.push(listener);
}

start(callback: (error: Error | null, port: number) => void) {
this.server.bindAsync('localhost:0', ServerCredentials.createInsecure(), (error, port) => {
if (!error) {
this.port = port;
this.server.start();
}
callback(error, port);
})
}

startAsync(): Promise<number> {
return new Promise((resolve, reject) => {
this.start((error, port) => {
if (error) {
reject(error);
} else {
resolve(port);
}
});
});
}

getPort(): number {
if (this.port === null) {
throw new Error('Port not set. Backend not yet started.');
}
return this.port;
}

getCallCount() {
return this.receivedCallCount;
}

resetCallCount() {
this.receivedCallCount = 0;
}

shutdown(callback: (error?: Error) => void) {
this.server.tryShutdown(callback);
}
}
Loading

0 comments on commit 6bc6b86

Please sign in to comment.