Skip to content

Commit

Permalink
more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cirospaciari committed Oct 8, 2024
1 parent 2521843 commit 13d248c
Show file tree
Hide file tree
Showing 41 changed files with 9,313 additions and 613 deletions.
248 changes: 141 additions & 107 deletions test/js/third_party/grpc-js/common.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,33 @@
import * as grpc from "@grpc/grpc-js";
import * as loader from "@grpc/proto-loader";
import { bunExe, nodeExe } from "harness";
import { readFileSync } from "fs";
import path from "node:path";
import { AddressInfo } from "ws";

const nodeExecutable = bunExe();
async function nodeEchoServer(env: any) {
env = env || {};
if (!nodeExecutable) throw new Error("node executable not found");
const subprocess = Bun.spawn([nodeExecutable, path.join(import.meta.dir, "node-server.fixture.js")], {
stdout: "pipe",
stderr: "inherit",
stdin: "pipe",
env: { ...env, BUN_DEBUG_QUIET_LOGS: "1" },
});
const reader = subprocess.stdout.getReader();
const data = await reader.read();
const decoder = new TextDecoder("utf-8");
const json = decoder.decode(data.value);
const address = JSON.parse(json);
const url = `localhost:${address.port}`;
return { address, url, subprocess };
}
/*
* Copyright 2019 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.
*
*/

export class TestServer {
#server: any;
#options: grpc.ChannelOptions;
address: AddressInfo | null = null;
url: string = "";
service_type: number = 0;
useTls = false;
constructor(useTls: boolean, options?: grpc.ChannelOptions, service_type = 0) {
this.#options = options || {};
this.useTls = useTls;
this.service_type = service_type;
}
async start() {
const result = await nodeEchoServer({
GRPC_TEST_USE_TLS: this.useTls ? "true" : "false",
GRPC_TEST_OPTIONS: JSON.stringify(this.#options),
GRPC_SERVICE_TYPE: this.service_type.toString(),
"grpc-node.max_session_memory": 1024,
});
this.address = result.address as AddressInfo;
this.url = result.url as string;
this.#server = result.subprocess;
}
import * as loader from "@grpc/proto-loader";
import * as assert2 from "./assert2";
import * as path from "path";
import grpc from "@grpc/grpc-js";
import * as fsPromises from "fs/promises";
import * as os from "os";

shutdown() {
this.#server.stdin.write("shutdown");
this.#server.kill();
}
}
import { GrpcObject, ServiceClientConstructor, ServiceClient, loadPackageDefinition } from "@grpc/grpc-js";
import { readFileSync } from "fs";
import { HealthListener, SubchannelInterface } from "@grpc/grpc-js/build/src/subchannel-interface";
import type { EntityTypes, SubchannelRef } from "@grpc/grpc-js/build/src/channelz";
import { Subchannel } from "@grpc/grpc-js/build/src/subchannel";
import { ConnectivityState } from "@grpc/grpc-js/build/src/connectivity-state";

const protoLoaderOptions = {
keepCase: true,
Expand All @@ -62,93 +37,145 @@ const protoLoaderOptions = {
oneofs: true,
};

function loadProtoFile(file: string) {
export function mockFunction(): never {
throw new Error("Not implemented");
}

export function loadProtoFile(file: string): GrpcObject {
const packageDefinition = loader.loadSync(file, protoLoaderOptions);
return grpc.loadPackageDefinition(packageDefinition);
return loadPackageDefinition(packageDefinition);
}

const protoFile = path.join(import.meta.dir, "fixtures", "echo_service.proto");
const EchoService = loadProtoFile(protoFile).EchoService as grpc.ServiceClientConstructor;
const protoFile = path.join(__dirname, "fixtures", "echo_service.proto");
const echoService = loadProtoFile(protoFile).EchoService as ServiceClientConstructor;

export const ca = readFileSync(path.join(import.meta.dir, "fixtures", "ca.pem"));
const ca = readFileSync(path.join(__dirname, "fixtures", "ca.pem"));
const key = readFileSync(path.join(__dirname, "fixtures", "server1.key"));
const cert = readFileSync(path.join(__dirname, "fixtures", "server1.pem"));

const serviceImpl = {
echo: (call: grpc.ServerUnaryCall<any, any>, callback: grpc.sendUnaryData<any>) => {
callback(null, call.request);
},
};

export class TestServer {
private server: grpc.Server;
private target: string | null = null;
constructor(
public useTls: boolean,
options?: grpc.ServerOptions,
) {
this.server = new grpc.Server(options);
this.server.addService(echoService.service, serviceImpl);
}

private getCredentials(): grpc.ServerCredentials {
if (this.useTls) {
return grpc.ServerCredentials.createSsl(null, [{ private_key: key, cert_chain: cert }], false);
} else {
return grpc.ServerCredentials.createInsecure();
}
}

start(): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.server.bindAsync("localhost:0", this.getCredentials(), (error, port) => {
if (error) {
reject(error);
return;
}
this.target = `localhost:${port}`;
resolve();
});
});
}

startUds(): Promise<void> {
return fsPromises.mkdtemp(path.join(os.tmpdir(), "uds")).then(dir => {
return new Promise<void>((resolve, reject) => {
const target = `unix://${dir}/socket`;
this.server.bindAsync(target, this.getCredentials(), (error, port) => {
if (error) {
reject(error);
return;
}
this.target = target;
resolve();
});
});
});
}

shutdown() {
this.server.forceShutdown();
}

getTarget() {
if (this.target === null) {
throw new Error("Server not yet started");
}
return this.target;
}
}

export class TestClient {
#client: grpc.Client;
constructor(url: string, useTls: boolean | grpc.ChannelCredentials, options?: grpc.ChannelOptions) {
private client: ServiceClient;
constructor(target: string, useTls: boolean, options?: grpc.ChannelOptions) {
let credentials: grpc.ChannelCredentials;
if (useTls instanceof grpc.ChannelCredentials) {
credentials = useTls;
} else if (useTls) {
if (useTls) {
credentials = grpc.credentials.createSsl(ca);
} else {
credentials = grpc.credentials.createInsecure();
}
this.#client = new EchoService(url, credentials, options);
}

static createFromServerWithCredentials(
server: TestServer,
credentials: grpc.ChannelCredentials,
options?: grpc.ChannelOptions,
) {
if (!server.address) {
throw new Error("Cannot create client, server not started");
}
return new TestClient(server.url, credentials, options);
this.client = new echoService(target, credentials, options);
}

static createFromServer(server: TestServer, options?: grpc.ChannelOptions) {
if (!server.address) {
throw new Error("Cannot create client, server not started");
}
return new TestClient(server.url, server.useTls, options);
return new TestClient(server.getTarget(), server.useTls, options);
}

waitForReady(deadline: grpc.Deadline, callback: (error?: Error) => void) {
this.#client.waitForReady(deadline, callback);
}
get client() {
return this.#client;
}
echo(...params: any[]) {
return this.#client.echo(...params);
this.client.waitForReady(deadline, callback);
}

sendRequest(callback: (error?: grpc.ServiceError) => void) {
this.#client.echo(
{
value: "hello",
value2: 1,
},
callback,
);
this.client.echo({}, callback);
}

getChannel() {
return this.#client.getChannel();
sendRequestWithMetadata(metadata: grpc.Metadata, callback: (error?: grpc.ServiceError) => void) {
this.client.echo({}, metadata, callback);
}

getChannelState() {
return this.#client.getChannel().getConnectivityState(false);
return this.client.getChannel().getConnectivityState(false);
}

close() {
this.#client.close();
waitForClientState(deadline: grpc.Deadline, state: ConnectivityState, callback: (error?: Error) => void) {
this.client.getChannel().watchConnectivityState(this.getChannelState(), deadline, err => {
if (err) {
return callback(err);
}

const currentState = this.getChannelState();
if (currentState === state) {
callback();
} else {
return this.waitForClientState(deadline, currentState, callback);
}
});
}
}

export enum ConnectivityState {
IDLE,
CONNECTING,
READY,
TRANSIENT_FAILURE,
SHUTDOWN,
close() {
this.client.close();
}
}

/**
* A mock subchannel that transitions between states on command, to test LB
* policy behavior
*/
export class MockSubchannel implements grpc.experimental.SubchannelInterface {
export class MockSubchannel implements SubchannelInterface {
private state: grpc.connectivityState;
private listeners: Set<grpc.experimental.ConnectivityStateListener> = new Set();
constructor(
Expand Down Expand Up @@ -186,7 +213,7 @@ export class MockSubchannel implements grpc.experimental.SubchannelInterface {
unref(): void {}
getChannelzRef(): SubchannelRef {
return {
kind: "subchannel",
kind: EntityTypes.subchannel,
id: -1,
name: this.address,
};
Expand All @@ -197,4 +224,11 @@ export class MockSubchannel implements grpc.experimental.SubchannelInterface {
realSubchannelEquals(other: grpc.experimental.SubchannelInterface): boolean {
return this === other;
}
isHealthy(): boolean {
return true;
}
addHealthStateWatcher(listener: HealthListener): void {}
removeHealthStateWatcher(listener: HealthListener): void {}
}

export { assert2 };
1 change: 1 addition & 0 deletions test/js/third_party/grpc-js/fixtures/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIRMEDTESTKEY
33 changes: 14 additions & 19 deletions test/js/third_party/grpc-js/fixtures/ca.pem
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQEL
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIw
MDMxNzE4NTk1MVoXDTMwMDMxNTE4NTk1MVowVjELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAsGL0oXflF0LzoM+Bh+qUU9yhqzw2w8OOX5mu/iNCyUOBrqaHi7mGHx73GD01
diNzCzvlcQqdNIH6NQSL7DTpBjca66jYT9u73vZe2MDrr1nVbuLvfu9850cdxiUO
Inv5xf8+sTHG0C+a+VAvMhsLiRjsq+lXKRJyk5zkbbsETybqpxoJ+K7CoSy3yc/k
QIY3TipwEtwkKP4hzyo6KiGd/DPexie4nBUInN3bS1BUeNZ5zeaIC2eg3bkeeW7c
qT55b+Yen6CxY0TEkzBK6AKt/WUialKMgT0wbTxRZO7kUCH3Sq6e/wXeFdJ+HvdV
LPlAg5TnMaNpRdQih/8nRFpsdwIDAQABoyAwHjAMBgNVHRMEBTADAQH/MA4GA1Ud
DwEB/wQEAwICBDANBgkqhkiG9w0BAQsFAAOCAQEAkTrKZjBrJXHps/HrjNCFPb5a
THuGPCSsepe1wkKdSp1h4HGRpLoCgcLysCJ5hZhRpHkRihhef+rFHEe60UePQO3S
CVTtdJB4CYWpcNyXOdqefrbJW5QNljxgi6Fhvs7JJkBqdXIkWXtFk2eRgOIP2Eo9
/OHQHlYnwZFrk6sp4wPyR+A95S0toZBcyDVz7u+hOW0pGK3wviOe9lvRgj/H3Pwt
bewb0l+MhRig0/DVHamyVxrDRbqInU1/GTNCwcZkXKYFWSf92U+kIcTth24Q1gcw
eZiLl5FfrWokUNytFElXob0V0a5/kbhiLc3yWmvWqHTpqCALbVyF+rKJo2f5Kw==
-----END CERTIFICATE-----
MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
Dfcog5wrJytaQ6UA0wE=
-----END CERTIFICATE-----
Loading

0 comments on commit 13d248c

Please sign in to comment.