-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
a.e.aminova
committed
Jun 3, 2024
1 parent
c19bdb5
commit 64a86c7
Showing
22 changed files
with
1,741 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# How to test with k6 | ||
|
||
k6 is used for integration testing. | ||
At first start mockingbird app, then set up `httpHost` and `grpcHost` variables, after that run tests. | ||
|
||
### Shell commands | ||
|
||
- To create new test use `k6 new fileName.js` | ||
|
||
- To run test script use `k6 run filename.js` | ||
|
||
- To group several tests in a single run use scenarios (e.g. see file `scenario.js`). | ||
To run scenario use `k6 run scenario.js` | ||
|
||
### Links | ||
|
||
- [Running k6](https://grafana.com/docs/k6/latest/get-started/running-k6/) | ||
- [Javascript API](https://grafana.com/docs/k6/latest/javascript-api/) |
31 changes: 31 additions & 0 deletions
31
backend/integrationTests/k6/v2/definitions/test_service.proto
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
syntax = "proto3"; | ||
package market_data; | ||
|
||
enum InstrumentIDKind { | ||
ID_1 = 0; | ||
ID_2 = 1; | ||
} | ||
|
||
message PricesRequest { | ||
string instrument_id = 1; | ||
optional InstrumentIDKind instrument_id_kind = 3; | ||
} | ||
|
||
message PricesResponse { | ||
enum Code { | ||
OK = 0; | ||
ERROR = 1; | ||
} | ||
|
||
string instrument_id = 1; | ||
string tracking_id = 3; | ||
Code code = 4; | ||
optional string error = 100; | ||
} | ||
|
||
service OTCMarketDataService { | ||
rpc PricesUnary (PricesRequest) returns (PricesResponse) {} | ||
rpc PricesClient (stream PricesRequest) returns (PricesResponse) {} | ||
rpc PricesServer (PricesRequest) returns (stream PricesResponse) {} | ||
rpc PricesBidi (stream PricesRequest) returns (stream PricesResponse) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import http from 'k6/http'; | ||
import { check, sleep } from 'k6'; | ||
import { default as unaryV2Test } from './unary.js'; | ||
|
||
export const httpHost = 'http://localhost:8228/api/internal/mockingbird'; | ||
export const grpcHost = 'localhost:9000'; | ||
|
||
export function httpUri(path) { | ||
return httpHost + path | ||
} | ||
|
||
export const httpOptions = { | ||
headers: { 'Content-Type': 'application/json' } | ||
} | ||
|
||
export const serviceName = 'beta' | ||
|
||
|
||
export const options = { | ||
scenarios: { | ||
unaryV2Scenario: { | ||
executor: 'per-vu-iterations', | ||
exec: 'unaryV2Scenario', | ||
|
||
startTime: '0s', | ||
gracefulStop: '5s', | ||
tags: { connectionType: 'unary' }, | ||
|
||
vus: 1, | ||
iterations: 1, | ||
maxDuration: '5s', | ||
}, | ||
}, | ||
}; | ||
|
||
export function setup() { | ||
const serviceData = { name: serviceName, suffix: serviceName } | ||
const serviceRes = http.post(httpUri('/v2/service'), JSON.stringify(serviceData), httpOptions) | ||
} | ||
|
||
export function unaryV2Scenario() { | ||
unaryV2Test() | ||
} | ||
|
||
export function teardown() { | ||
|
||
// delete all method descriptions and stubs for service | ||
let methodDescriptionsRes = http.get(httpUri(`/v4/grpcMethodDescription?service=${serviceName}`)) | ||
let methodDescriptionIds = methodDescriptionsRes.json("#.id") | ||
console.info(`methodDescriptionIds=${methodDescriptionIds}`) | ||
|
||
for (let i in methodDescriptionIds) { | ||
let methodDescriptionId = methodDescriptionIds[i] | ||
let grpcStubsRes = http.get(httpUri(`/v4/grpcStub?query=${methodDescriptionId}`)) | ||
let grpcStubIds = grpcStubsRes.json("#.id") | ||
console.info(`Alive grpcStubIds=${grpcStubIds}`) | ||
|
||
for (let i in grpcStubIds) { | ||
let stubId = grpcStubIds[i] | ||
let grpcStubRes = http.del(httpUri(`/v2/grpcStub/${stubId}`)) | ||
console.info(`del stub: id=${stubId}; status=${grpcStubRes.status}`) | ||
} | ||
|
||
let methodDescriptionRes = http.del(httpUri(`/v4/grpcMethodDescription/${methodDescriptionId}`)) | ||
console.info(`del methodDescription: id=${methodDescriptionId}; status=${methodDescriptionRes.status}`) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import encoding from 'k6/encoding'; | ||
import http from 'k6/http'; | ||
import grpc from 'k6/net/grpc'; | ||
import { check, sleep } from 'k6'; | ||
|
||
import { serviceName } from './scenario.js' | ||
import { httpHost, httpUri, httpOptions, grpcHost } from './scenario.js' | ||
import { setup as scenarioSetup, teardown as scenarioTeardown } from './scenario.js' | ||
|
||
const methodName = 'market_data.OTCMarketDataService/PricesUnary' | ||
|
||
const grpcClient = new grpc.Client(); | ||
grpcClient.load(['definitions'], 'test_service.proto'); | ||
|
||
const protoFile = open('./definitions/test_service.proto'); | ||
const base64_proto = encoding.b64encode(protoFile); | ||
|
||
export function setup() { | ||
scenarioSetup() | ||
} | ||
|
||
export default function() { | ||
|
||
const stubData = { | ||
name: "unary v2 countdown stub", | ||
labels: [], | ||
scope: "countdown", | ||
times: 1, | ||
methodName: methodName, | ||
requestClass: "PricesRequest", | ||
requestPredicates:{}, | ||
responseClass: "PricesResponse", | ||
response: { | ||
"data": { | ||
"code": "OK", | ||
"instrument_id": "${req.instrument_id}", | ||
"tracking_id": "${req.instrument_id_kind}" | ||
}, | ||
"mode":"fill" | ||
}, | ||
state: null, | ||
seed: null, | ||
service: serviceName, | ||
requestCodecs: base64_proto, | ||
responseCodecs: base64_proto | ||
} | ||
const stubRes = http.post(httpUri('/v2/grpcStub'), JSON.stringify(stubData), httpOptions) | ||
check(stubRes, { 'create stub status': (r) => r.status === 200 }) | ||
|
||
grpcClient.connect(grpcHost, { plaintext: true }) | ||
|
||
const grpcReq = { instrument_id: 'instrument_1', instrument_id_kind: 'ID_1' } | ||
const response = grpcClient.invoke(methodName, grpcReq) | ||
const expectedMessage = { instrument_id: grpcReq.instrument_id, trackingId: grpcReq.instrument_id_kind, code : "OK" } | ||
check(response, { | ||
'call grpc stub - status is OK': (r) => r && r.status === grpc.StatusOK, | ||
'call grpc stub - check response message': (r) => r.message && r.message.code === "OK" && | ||
r.message.instrumentId === grpcReq.instrument_id && r.message.trackingId === grpcReq.instrument_id_kind, | ||
}) | ||
|
||
const response2 = grpcClient.invoke(methodName, grpcReq) | ||
check(response2, { | ||
'recall grpc stub - status is Internal': (r) => r && r.status === grpc.StatusInternal, | ||
'recall grpc stub - check response message': (r) => r.error && r.error.message === `Can't find any stub for ${methodName}`, | ||
}) | ||
|
||
} | ||
|
||
export function teardown() { | ||
scenarioTeardown() | ||
grpcClient.close() | ||
} |
144 changes: 144 additions & 0 deletions
144
backend/integrationTests/k6/v4/contentType/bidiStreaming.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import encoding from 'k6/encoding'; | ||
import http from 'k6/http'; | ||
import grpc from 'k6/net/grpc'; | ||
import { check, sleep } from 'k6'; | ||
|
||
import { serviceName } from './scenario.js' | ||
import { httpHost, httpUri, httpOptions, grpcHost } from './scenario.js' | ||
import { setup as scenarioSetup, teardown as scenarioTeardown } from './scenario.js' | ||
|
||
const methodName = 'market_data.OTCMarketDataService/PricesBidi' | ||
|
||
const grpcClient = new grpc.Client(); | ||
grpcClient.load(['definitions'], 'test_service.proto'); | ||
|
||
const protoFile = open('./definitions/test_service.proto'); | ||
const base64Proto = encoding.b64encode(protoFile); | ||
|
||
export function setup() { | ||
scenarioSetup() | ||
} | ||
|
||
export default function() { | ||
|
||
const methodDescriptionData = { | ||
id: "bidi-streaming-method-description", | ||
description: "k6 testing", | ||
service: serviceName, | ||
methodName: methodName, | ||
connectionType: "BIDI_STREAMING", | ||
proxyUrl: null, | ||
requestClass: "PricesRequest", | ||
responseClass: "PricesResponse", | ||
requestCodecs: base64Proto, | ||
responseCodecs: base64Proto | ||
} | ||
const methodDescriptionRes = http.post(httpUri('/v4/grpcMethodDescription'), JSON.stringify(methodDescriptionData), httpOptions) | ||
check(methodDescriptionRes, { 'create method description - status is OK': (r) => r.status === 200 }) | ||
|
||
const stubData = { | ||
methodDescriptionId: "bidi-streaming-method-description", | ||
scope: "countdown", | ||
times: 2, | ||
name: "bidirectional streaming v4 countdown stub", | ||
response: { | ||
"data": { | ||
"code": "OK", | ||
"instrument_id": "${req.instrument_id}", | ||
"tracking_id": "${req.instrument_id_kind}" | ||
}, | ||
"mode":"fill" | ||
}, | ||
requestPredicates:{}, | ||
state: null, | ||
seed: null, | ||
persist: null, | ||
labels: [] | ||
} | ||
const stubRes = http.post(httpUri('/v4/grpcStub'), JSON.stringify(stubData), httpOptions) | ||
check(stubRes, { 'create stub - status is OK': (r) => r.status === 200 }) | ||
|
||
grpcClient.connect(grpcHost, { plaintext: true }) | ||
|
||
const grpcReq = [ | ||
{ instrument_id: 'instrument_1', instrument_id_kind: 'ID_1' }, | ||
{ instrument_id: 'instrument_2', instrument_id_kind: 'ID_2' }, | ||
] | ||
|
||
const stream = new grpc.Stream(grpcClient, methodName) | ||
let inputStreamIndex = 0 | ||
|
||
stream.on('data', (data) => { | ||
console.log('Stream#1 Value: ' + JSON.stringify(data)); | ||
check(data, { | ||
'response stream element - validation is OK': (d) => d && inputStreamIndex < grpcReq.length && | ||
d.instrumentId === grpcReq[inputStreamIndex].instrument_id && d.trackingId === grpcReq[inputStreamIndex].instrument_id_kind, | ||
}); | ||
inputStreamIndex += 1 | ||
}) | ||
|
||
stream.on('error', (err) => { | ||
console.log('Stream#1 Error: ' + JSON.stringify(err)); | ||
check(err, { 'response stream element - should be empty': (e) => false }) | ||
}) | ||
|
||
stream.on('end', () => { | ||
console.log('All done'); | ||
}) | ||
|
||
for (let i in grpcReq) { | ||
stream.write(grpcReq[i]) | ||
sleep(0.1) | ||
} | ||
|
||
stream.end(); | ||
|
||
|
||
const stream2 = new grpc.Stream(grpcClient, methodName) | ||
|
||
stream2.on('data', (data) => { | ||
console.log('Stream#2 Value: ' + JSON.stringify(data)); | ||
check(data, { 'recall response stream element - should be empty': (d) => false }) | ||
}) | ||
|
||
stream2.on('error', (err) => { | ||
console.log('Stream#2 Error: ' + JSON.stringify(err)); | ||
check(err, { | ||
'recall response stream element - status is Internal': (e) => e && e.code == 13 && | ||
e.message === `Can't find any stub for ${methodName}`, | ||
}) | ||
}) | ||
|
||
stream2.on('end', () => { | ||
console.log('All done'); | ||
}) | ||
|
||
for (let i in grpcReq) { | ||
stream2.write(grpcReq[i]) | ||
} | ||
|
||
stream2.end(); | ||
|
||
} | ||
|
||
export function teardown() { | ||
scenarioTeardown() | ||
grpcClient.close() | ||
} | ||
|
||
function makeStream(grpcClient, methodName, onData) { | ||
const stream = new Stream(grpcClient, methodName) | ||
|
||
stream.on('data', onData) | ||
|
||
stream.on('error', (err) => { | ||
console.log('Stream Error: ' + JSON.stringify(err)); | ||
}) | ||
|
||
stream.on('end', () => { | ||
console.log('All done'); | ||
}) | ||
|
||
return stream | ||
} | ||
|
Oops, something went wrong.