Skip to content

Commit 6bc6b86

Browse files
committed
grpc-js-xds: Add unit test framework
1 parent 9264d58 commit 6bc6b86

26 files changed

+1721
-1
lines changed

packages/grpc-js-xds/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"pretest": "npm run compile",
1414
"posttest": "npm run check",
1515
"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",
16-
"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"
16+
"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",
17+
"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"
1718
},
1819
"repository": {
1920
"type": "git",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
2+
// Copyright 2015 gRPC authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
syntax = "proto3";
17+
18+
package grpc.testing;
19+
20+
import "grpc/testing/echo_messages.proto";
21+
import "grpc/testing/simple_messages.proto";
22+
23+
service EchoTestService {
24+
rpc Echo(EchoRequest) returns (EchoResponse);
25+
rpc Echo1(EchoRequest) returns (EchoResponse);
26+
rpc Echo2(EchoRequest) returns (EchoResponse);
27+
rpc CheckDeadlineUpperBound(SimpleRequest) returns (StringValue);
28+
rpc CheckDeadlineSet(SimpleRequest) returns (StringValue);
29+
// A service which checks that the initial metadata sent over contains some
30+
// expected key value pair
31+
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);
32+
rpc RequestStream(stream EchoRequest) returns (EchoResponse);
33+
rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
34+
rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
35+
rpc Unimplemented(EchoRequest) returns (EchoResponse);
36+
rpc UnimplementedBidi(stream EchoRequest) returns (stream EchoResponse);
37+
}
38+
39+
service EchoTest1Service {
40+
rpc Echo(EchoRequest) returns (EchoResponse);
41+
rpc Echo1(EchoRequest) returns (EchoResponse);
42+
rpc Echo2(EchoRequest) returns (EchoResponse);
43+
// A service which checks that the initial metadata sent over contains some
44+
// expected key value pair
45+
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);
46+
rpc RequestStream(stream EchoRequest) returns (EchoResponse);
47+
rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
48+
rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
49+
rpc Unimplemented(EchoRequest) returns (EchoResponse);
50+
}
51+
52+
service EchoTest2Service {
53+
rpc Echo(EchoRequest) returns (EchoResponse);
54+
rpc Echo1(EchoRequest) returns (EchoResponse);
55+
rpc Echo2(EchoRequest) returns (EchoResponse);
56+
// A service which checks that the initial metadata sent over contains some
57+
// expected key value pair
58+
rpc CheckClientInitialMetadata(SimpleRequest) returns (SimpleResponse);
59+
rpc RequestStream(stream EchoRequest) returns (EchoResponse);
60+
rpc ResponseStream(EchoRequest) returns (stream EchoResponse);
61+
rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
62+
rpc Unimplemented(EchoRequest) returns (EchoResponse);
63+
}
64+
65+
service UnimplementedEchoService {
66+
rpc Unimplemented(EchoRequest) returns (EchoResponse);
67+
}
68+
69+
// A service without any rpc defined to test coverage.
70+
service NoRpcService {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
2+
// Copyright 2015 gRPC authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
syntax = "proto3";
17+
18+
package grpc.testing;
19+
20+
option cc_enable_arenas = true;
21+
22+
import "grpc/testing/xds/v3/orca_load_report.proto";
23+
24+
// Message to be echoed back serialized in trailer.
25+
message DebugInfo {
26+
repeated string stack_entries = 1;
27+
string detail = 2;
28+
}
29+
30+
// Error status client expects to see.
31+
message ErrorStatus {
32+
int32 code = 1;
33+
string error_message = 2;
34+
string binary_error_details = 3;
35+
}
36+
37+
message RequestParams {
38+
bool echo_deadline = 1;
39+
int32 client_cancel_after_us = 2;
40+
int32 server_cancel_after_us = 3;
41+
bool echo_metadata = 4;
42+
bool check_auth_context = 5;
43+
int32 response_message_length = 6;
44+
bool echo_peer = 7;
45+
string expected_client_identity = 8; // will force check_auth_context.
46+
bool skip_cancelled_check = 9;
47+
string expected_transport_security_type = 10;
48+
DebugInfo debug_info = 11;
49+
bool server_die = 12; // Server should not see a request with this set.
50+
string binary_error_details = 13;
51+
ErrorStatus expected_error = 14;
52+
int32 server_sleep_us = 15; // sleep when invoking server for deadline tests
53+
int32 backend_channel_idx = 16; // which backend to send request to
54+
bool echo_metadata_initially = 17;
55+
bool server_notify_client_when_started = 18;
56+
xds.data.orca.v3.OrcaLoadReport backend_metrics = 19;
57+
bool echo_host_from_authority_header = 20;
58+
}
59+
60+
message EchoRequest {
61+
string message = 1;
62+
RequestParams param = 2;
63+
}
64+
65+
message ResponseParams {
66+
int64 request_deadline = 1;
67+
string host = 2;
68+
string peer = 3;
69+
}
70+
71+
message EchoResponse {
72+
string message = 1;
73+
ResponseParams param = 2;
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
// Copyright 2018 gRPC authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
syntax = "proto3";
17+
18+
package grpc.testing;
19+
20+
message SimpleRequest {}
21+
22+
message SimpleResponse {}
23+
24+
message StringValue {
25+
string message = 1;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2020 The gRPC Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Local copy of Envoy xDS proto file, used for testing only.
16+
17+
syntax = "proto3";
18+
19+
package xds.data.orca.v3;
20+
21+
// See section `ORCA load report format` of the design document in
22+
// :ref:`https://github.com/envoyproxy/envoy/issues/6614`.
23+
24+
message OrcaLoadReport {
25+
// CPU utilization expressed as a fraction of available CPU resources. This
26+
// should be derived from the latest sample or measurement.
27+
double cpu_utilization = 1;
28+
29+
// Memory utilization expressed as a fraction of available memory
30+
// resources. This should be derived from the latest sample or measurement.
31+
double mem_utilization = 2;
32+
33+
// Total RPS being served by an endpoint. This should cover all services that an endpoint is
34+
// responsible for.
35+
uint64 rps = 3;
36+
37+
// Application specific requests costs. Each value is an absolute cost (e.g. 3487 bytes of
38+
// storage) associated with the request.
39+
map<string, double> request_cost = 4;
40+
41+
// Resource utilization values. Each value is expressed as a fraction of total resources
42+
// available, derived from the latest sample or measurement.
43+
map<string, double> utilization = 5;
44+
}

packages/grpc-js-xds/test/backend.ts

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2023 gRPC authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
import { loadPackageDefinition, sendUnaryData, Server, ServerCredentials, ServerUnaryCall, UntypedServiceImplementation } from "@grpc/grpc-js";
19+
import { loadSync } from "@grpc/proto-loader";
20+
import { ProtoGrpcType } from "./generated/echo";
21+
import { EchoRequest__Output } from "./generated/grpc/testing/EchoRequest";
22+
import { EchoResponse } from "./generated/grpc/testing/EchoResponse";
23+
24+
const loadedProtos = loadPackageDefinition(loadSync(
25+
[
26+
'grpc/testing/echo.proto'
27+
],
28+
{
29+
keepCase: true,
30+
longs: String,
31+
enums: String,
32+
defaults: true,
33+
oneofs: true,
34+
json: true,
35+
includeDirs: [
36+
// Paths are relative to build/test
37+
__dirname + '/../../proto/'
38+
],
39+
})) as unknown as ProtoGrpcType;
40+
41+
export class Backend {
42+
private server: Server;
43+
private receivedCallCount = 0;
44+
private callListeners: (() => void)[] = [];
45+
private port: number | null = null;
46+
constructor() {
47+
this.server = new Server();
48+
this.server.addService(loadedProtos.grpc.testing.EchoTestService.service, this as unknown as UntypedServiceImplementation);
49+
}
50+
Echo(call: ServerUnaryCall<EchoRequest__Output, EchoResponse>, callback: sendUnaryData<EchoResponse>) {
51+
// call.request.params is currently ignored
52+
this.addCall();
53+
callback(null, {message: call.request.message});
54+
}
55+
56+
addCall() {
57+
this.receivedCallCount++;
58+
this.callListeners.forEach(listener => listener());
59+
}
60+
61+
onCall(listener: () => void) {
62+
this.callListeners.push(listener);
63+
}
64+
65+
start(callback: (error: Error | null, port: number) => void) {
66+
this.server.bindAsync('localhost:0', ServerCredentials.createInsecure(), (error, port) => {
67+
if (!error) {
68+
this.port = port;
69+
this.server.start();
70+
}
71+
callback(error, port);
72+
})
73+
}
74+
75+
startAsync(): Promise<number> {
76+
return new Promise((resolve, reject) => {
77+
this.start((error, port) => {
78+
if (error) {
79+
reject(error);
80+
} else {
81+
resolve(port);
82+
}
83+
});
84+
});
85+
}
86+
87+
getPort(): number {
88+
if (this.port === null) {
89+
throw new Error('Port not set. Backend not yet started.');
90+
}
91+
return this.port;
92+
}
93+
94+
getCallCount() {
95+
return this.receivedCallCount;
96+
}
97+
98+
resetCallCount() {
99+
this.receivedCallCount = 0;
100+
}
101+
102+
shutdown(callback: (error?: Error) => void) {
103+
this.server.tryShutdown(callback);
104+
}
105+
}

0 commit comments

Comments
 (0)