feat: support add multiple stores (#147)

This commit is contained in:
Rick 2023-07-29 22:14:58 +08:00 committed by GitHub
parent 470ef8e337
commit 8d459a9eed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2926 additions and 1550 deletions

3
.gitpod.yml Normal file
View File

@ -0,0 +1,3 @@
tasks:
- init: make build
command: docker run --pull always --network host ghcr.io/linuxsuren/api-testing:master

View File

@ -52,13 +52,11 @@ install-precheck:
cp .github/pre-commit .git/hooks/pre-commit
grpc:
protoc --go_out=. --go_opt=paths=source_relative \
protoc --proto_path=. \
--go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pkg/server/server.proto
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pkg/testing/remote/loader.proto
pkg/server/server.proto \
pkg/testing/remote/loader.proto
grpc-gw:
protoc -I . --grpc-gateway_out . \
--grpc-gateway_opt logtostderr=true \

View File

@ -20,7 +20,7 @@ func NewRootCmd(execer fakeruntime.Execer, gRPCServer gRPCServer,
c.Version = version.GetVersion()
c.AddCommand(createInitCommand(execer),
createRunCommand(), createSampleCmd(),
createServerCmd(gRPCServer, httpServer), createJSONSchemaCmd(),
createServerCmd(execer, gRPCServer, httpServer), createJSONSchemaCmd(),
createServiceCommand(execer), createFunctionCmd())
return
}

View File

@ -10,17 +10,19 @@ import (
)
func TestCreateRunCommand(t *testing.T) {
execer := fakeruntime.FakeExecer{}
cmd := createRunCommand()
assert.Equal(t, "run", cmd.Use)
init := createInitCommand(fakeruntime.FakeExecer{})
init := createInitCommand(execer)
assert.Equal(t, "init", init.Use)
s := createServerCmd(&fakeGRPCServer{}, server.NewFakeHTTPServer())
s := createServerCmd(execer, &fakeGRPCServer{}, server.NewFakeHTTPServer())
assert.NotNil(t, s)
assert.Equal(t, "server", s.Use)
root := NewRootCmd(fakeruntime.FakeExecer{}, NewFakeGRPCServer(), server.NewFakeHTTPServer())
root := NewRootCmd(execer, NewFakeGRPCServer(), server.NewFakeHTTPServer())
root.SetArgs([]string{"init", "-k=demo.yaml", "--wait-namespace", "demo", "--wait-resource", "demo"})
err := root.Execute()
assert.Nil(t, err)

View File

@ -3,11 +3,11 @@ package cmd
import (
"bytes"
"errors"
"fmt"
"log"
"net"
"net/http"
"os"
"path"
"strings"
"time"
@ -19,43 +19,51 @@ import (
"github.com/linuxsuren/api-testing/pkg/testing"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/linuxsuren/api-testing/pkg/util"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
func createServerCmd(gRPCServer gRPCServer, httpServer server.HTTPServer) (c *cobra.Command) {
func createServerCmd(execer fakeruntime.Execer, gRPCServer gRPCServer, httpServer server.HTTPServer) (c *cobra.Command) {
opt := &serverOption{
gRPCServer: gRPCServer,
httpServer: httpServer,
execer: execer,
}
c = &cobra.Command{
Use: "server",
Short: "Run as a server mode",
RunE: opt.runE,
Use: "server",
Short: "Run as a server mode",
PreRunE: opt.preRunE,
RunE: opt.runE,
}
flags := c.Flags()
flags.IntVarP(&opt.port, "port", "p", 7070, "The RPC server port")
flags.IntVarP(&opt.httpPort, "http-port", "", 8080, "The HTTP server port")
flags.BoolVarP(&opt.printProto, "print-proto", "", false, "Print the proto content and exit")
flags.StringVarP(&opt.storage, "storage", "", "local", "The storage type, local or etcd")
flags.StringArrayVarP(&opt.localStorage, "local-storage", "", []string{"*.yaml"}, "The local storage path")
flags.StringVarP(&opt.grpcStorage, "grpc-storage", "", "", "The grpc storage address")
flags.StringVarP(&opt.consolePath, "console-path", "", "", "The path of the console")
flags.StringVarP(&opt.configDir, "config-dir", "", "$HOME/.config/atest", "The config directory")
return
}
type serverOption struct {
gRPCServer gRPCServer
httpServer server.HTTPServer
execer fakeruntime.Execer
port int
httpPort int
printProto bool
storage string
localStorage []string
grpcStorage string
consolePath string
configDir string
}
func (o *serverOption) preRunE(cmd *cobra.Command, args []string) (err error) {
o.configDir = os.ExpandEnv(o.configDir)
err = o.execer.MkdirAll(o.configDir, 0755)
return
}
func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
@ -79,30 +87,15 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
return
}
var loader testing.Writer
switch o.storage {
case "local":
loader = testing.NewFileWriter("")
for _, storage := range o.localStorage {
if err = loader.Put(storage); err != nil {
break
}
loader := testing.NewFileWriter("")
for _, storage := range o.localStorage {
if loadErr := loader.Put(storage); loadErr != nil {
cmd.PrintErrf("failed to load %s, error: %v\n", storage, loadErr)
continue
}
case "grpc":
if o.grpcStorage == "" {
err = errors.New("grpc storage address is required")
return
}
loader, err = remote.NewGRPCLoader(o.grpcStorage)
default:
err = errors.New("invalid storage type")
}
if err != nil {
return
}
removeServer := server.NewRemoteServer(loader)
removeServer := server.NewRemoteServer(loader, remote.NewGRPCloaderFromStore(), o.configDir)
s := o.gRPCServer
go func() {
if gRPCServer, ok := s.(reflection.GRPCServer); ok {
@ -113,13 +106,14 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
s.Serve(lis)
}()
mux := runtime.NewServeMux()
mux := runtime.NewServeMux(runtime.WithMetadata(server.MetadataStoreFunc)) // runtime.WithIncomingHeaderMatcher(func(key string) (s string, b bool) {
err = server.RegisterRunnerHandlerServer(cmd.Context(), mux, removeServer)
if err == nil {
mux.HandlePath(http.MethodGet, "/", frontEndHandlerWithLocation(o.consolePath))
mux.HandlePath(http.MethodGet, "/assets/{asset}", frontEndHandlerWithLocation(o.consolePath))
mux.HandlePath(http.MethodGet, "/healthz", frontEndHandlerWithLocation(o.consolePath))
o.httpServer.WithHandler(mux)
log.Printf("HTTP server listening at %v", httplis.Addr())
err = o.httpServer.Serve(httplis)
}
return

View File

@ -9,6 +9,7 @@ import (
_ "embed"
"github.com/linuxsuren/api-testing/cmd/service"
"github.com/linuxsuren/api-testing/pkg/version"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/spf13/cobra"
@ -272,26 +273,23 @@ type linuxService struct {
commonService
}
const systemCtl = "systemctl"
const serviceName = "atest"
func (s *linuxService) Start() (output string, err error) {
output, err = s.Execer.RunCommandAndReturn(systemCtl, "", "start", serviceName)
output, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "start", service.ServiceName)
return
}
func (s *linuxService) Stop() (output string, err error) {
output, err = s.Execer.RunCommandAndReturn(systemCtl, "", "stop", serviceName)
output, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "stop", service.ServiceName)
return
}
func (s *linuxService) Restart() (output string, err error) {
output, err = s.Execer.RunCommandAndReturn(systemCtl, "", "restart", serviceName)
output, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "restart", service.ServiceName)
return
}
func (s *linuxService) Status() (output string, err error) {
output, err = s.Execer.RunCommandAndReturn(systemCtl, "", "status", serviceName)
output, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "status", service.ServiceName)
if err != nil && err.Error() == "exit status 3" {
// this is normal case
err = nil
@ -301,13 +299,13 @@ func (s *linuxService) Status() (output string, err error) {
func (s *linuxService) Install() (output string, err error) {
if err = os.WriteFile(s.scriptPath, []byte(s.script), os.ModeAppend); err == nil {
output, err = s.Execer.RunCommandAndReturn(systemCtl, "", "enable", serviceName)
output, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "enable", service.ServiceName)
}
return
}
func (s *linuxService) Uninstall() (output string, err error) {
output, err = s.Execer.RunCommandAndReturn(systemCtl, "", "disable", serviceName)
output, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "disable", service.ServiceName)
return
}
@ -324,7 +322,7 @@ type containerService struct {
const defaultImage = "ghcr.io/linuxsuren/api-testing"
func newContainerService(execer fakeruntime.Execer, client, image, tag, localStorage string, writer io.Writer) (service Service) {
func newContainerService(execer fakeruntime.Execer, client, image, tag, localStorage string, writer io.Writer) (svc Service) {
if tag == "" {
tag = "latest"
}
@ -335,7 +333,7 @@ func newContainerService(execer fakeruntime.Execer, client, image, tag, localSto
containerServer := &containerService{
Execer: execer,
client: client,
name: serviceName,
name: service.ServiceName,
image: image,
tag: tag,
localStorage: localStorage,
@ -344,11 +342,11 @@ func newContainerService(execer fakeruntime.Execer, client, image, tag, localSto
}
if strings.HasSuffix(client, ServiceModePodman.String()) {
service = &podmanService{
svc = &podmanService{
containerService: containerServer,
}
} else {
service = containerServer
svc = containerServer
}
return
}
@ -426,14 +424,19 @@ func (s *podmanService) Start() (output string, err error) {
return
}
func (s *podmanService) Stop() (output string, err error) {
output, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "stop", service.PodmanServiceName)
return
}
func (s *podmanService) installService() (output string, err error) {
output, err = s.Execer.RunCommandAndReturn(s.client, "", "generate", "systemd", "--new", "--files", "--name", s.name)
if err == nil {
var result string
result, err = s.Execer.RunCommandAndReturn("mv", "", "container-atest.service", "/etc/systemd/system")
result, err = s.Execer.RunCommandAndReturn("mv", "", service.PodmanServiceName, "/etc/systemd/system")
if err == nil {
output = fmt.Sprintf("%s\n%s", output, result)
if result, err = s.Execer.RunCommandAndReturn("systemctl", "", "enable", "container-atest.service"); err == nil {
if result, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "enable", service.PodmanServiceName); err == nil {
output = fmt.Sprintf("%s\n%s", output, result)
}
}
@ -453,6 +456,6 @@ func (s *podmanService) Uninstall() (output string, err error) {
}
func (s *podmanService) uninstallService() (output string, err error) {
output, err = s.Execer.RunCommandAndReturn("systemctl", "", "disable", "container-atest.service")
output, err = s.Execer.RunCommandAndReturn(service.SystemCtl, "", "disable", service.PodmanServiceName)
return
}

9
cmd/service/constant.go Normal file
View File

@ -0,0 +1,9 @@
package service
const (
// PodmanServiceName is the service name of podman service
PodmanServiceName = "container-atest.service"
SystemCtl = "systemctl"
ServiceName = "atest"
)

View File

@ -155,6 +155,12 @@ func TestService(t *testing.T) {
targetOS: fakeruntime.OSLinux,
mode: ServiceModePodman.String(),
expectOutput: "",
}, {
name: "stop in podman",
action: "stop",
targetOS: fakeruntime.OSLinux,
mode: ServiceModePodman.String(),
expectOutput: "",
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -4,6 +4,7 @@
console.log(Cypress.browser)
describe('Suite Manage', () => {
const suiteName = userID_Alpha()
const store = "local"
const sampleAPIAddress = "http://foo"
console.log(sampleAPIAddress)
@ -13,6 +14,11 @@ describe('Suite Manage', () => {
cy.get('[test-id="open-new-suite-dialog"]').click()
const storeSelect = cy.get('[test-id=suite-form-store] input')
storeSelect.click()
storeSelect.type(store)
storeSelect.trigger('keydown', {key: 'Enter'})
cy.get('[test-id=suite-form-name]').should('be.visible').type(suiteName)
cy.get('[test-id=suite-form-api]').should('be.visible').type(sampleAPIAddress)
cy.get('[test-id=suite-form-submit]').should('be.visible').click()

View File

@ -4,25 +4,32 @@ import TestSuite from './views/TestSuite.vue'
import TemplateFunctions from './views/TemplateFunctions.vue'
import { reactive, ref, watch } from 'vue'
import { ElTree } from 'element-plus'
import type { FormInstance } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { Edit } from '@element-plus/icons-vue'
import type { Suite } from './types'
interface Tree {
id: string
label: string
parent: string
store: string
children?: Tree[]
}
const testCaseName = ref('')
const testSuite = ref('')
const store = ref('')
const handleNodeClick = (data: Tree) => {
if (data.children) {
viewName.value = 'testsuite'
testSuite.value = data.label
store.value = data.store
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': data.store
},
body: JSON.stringify({
name: data.label
})
@ -36,6 +43,7 @@ const handleNodeClick = (data: Tree) => {
data.children?.push({
id: data.label + item.name,
label: item.name,
store: data.store,
parent: data.label
} as Tree)
})
@ -44,6 +52,7 @@ const handleNodeClick = (data: Tree) => {
} else {
testCaseName.value = data.label
testSuite.value = data.parent
store.value = data.store
viewName.value = 'testcase'
}
}
@ -52,14 +61,16 @@ const data = ref([] as Tree[])
const treeRef = ref<InstanceType<typeof ElTree>>()
const currentNodekey = ref('')
function loadTestSuites() {
function loadTestSuites(store: string) {
const requestOptions = {
method: 'POST'
method: 'POST',
headers: {
'X-Store-Name': store
},
}
fetch('/server.Runner/GetSuites', requestOptions)
.then((response) => response.json())
.then((d) => {
data.value = [] as Tree[]
if (!d.data) {
return
}
@ -67,6 +78,7 @@ function loadTestSuites() {
let suite = {
id: k,
label: k,
store: store,
children: [] as Tree[]
} as Tree
@ -74,6 +86,7 @@ function loadTestSuites() {
suite.children?.push({
id: k + item,
label: item,
store: store,
parent: k
} as Tree)
})
@ -95,40 +108,76 @@ function loadTestSuites() {
}
})
}
loadTestSuites()
interface Store {
name: string,
description: string,
}
const stores = ref([] as Store[])
function loadStores() {
const requestOptions = {
method: 'POST',
}
fetch('/server.Runner/GetStores', requestOptions)
.then((response) => response.json())
.then((d) => {
stores.value = d.data
data.value = [] as Tree[]
d.data.forEach((item: any) => {
loadTestSuites(item.name)
})
})
}
loadStores()
const dialogVisible = ref(false)
const suiteCreatingLoading = ref(false)
const suiteFormRef = ref<FormInstance>()
const testSuiteForm = reactive({
name: '',
api: ''
api: '',
store: ''
})
function openTestSuiteCreateDialog() {
dialogVisible.value = true
}
const submitForm = (formEl: FormInstance | undefined) => {
const rules = reactive<FormRules<Suite>>({
name: [{ required: true, message: 'Name is required', trigger: 'blur' }],
store: [{ required: true, message: 'Location is required', trigger: 'blur' }]
})
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
suiteCreatingLoading.value = true
console.log(formEl)
await formEl.validate((valid: boolean, fields) => {
console.log(valid, fields)
if (valid) {
suiteCreatingLoading.value = true
const requestOptions = {
method: 'POST',
body: JSON.stringify({
name: testSuiteForm.name,
api: testSuiteForm.api
})
}
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': testSuiteForm.store
},
body: JSON.stringify({
name: testSuiteForm.name,
api: testSuiteForm.api
})
}
fetch('/server.Runner/CreateTestSuite', requestOptions)
.then((response) => response.json())
.then(() => {
suiteCreatingLoading.value = false
loadTestSuites()
})
dialogVisible.value = false
fetch('/server.Runner/CreateTestSuite', requestOptions)
.then((response) => response.json())
.then(() => {
suiteCreatingLoading.value = false
loadStores()
dialogVisible.value = false
formEl.resetFields()
})
}
})
}
const filterText = ref('')
@ -166,14 +215,16 @@ const viewName = ref('testcase')
<el-main>
<TestCase
v-if="viewName === 'testcase'"
:store="store"
:suite="testSuite"
:name="testCaseName"
@updated="loadTestSuites"
@updated="loadStores"
/>
<TestSuite
v-else-if="viewName === 'testsuite'"
:name="testSuite"
@updated="loadTestSuites"
:store="store"
@updated="loadStores"
/>
</el-main>
</el-container>
@ -182,7 +233,25 @@ const viewName = ref('testcase')
<el-dialog v-model="dialogVisible" title="Create Test Suite" width="30%" draggable>
<template #footer>
<span class="dialog-footer">
<el-form ref="suiteFormRef" status-icon label-width="120px" class="demo-ruleForm">
<el-form
:rules="rules"
:model="testSuiteForm"
ref="suiteFormRef"
status-icon label-width="120px">
<el-form-item label="Location" prop="store">
<el-select v-model="testSuiteForm.store" class="m-2"
test-id="suite-form-store"
filterable=true
default-first-option=true
placeholder="Storage Location" size="middle">
<el-option
v-for="item in stores"
:key="item.name"
:label="item.name"
:value="item.name"
/>
</el-select>
</el-form-item>
<el-form-item label="Name" prop="name">
<el-input v-model="testSuiteForm.name" test-id="suite-form-name" />
</el-form-item>

View File

@ -9,7 +9,8 @@ import { NewSuggestedAPIsQuery, CreateFilter, GetHTTPMethods, FlattenObject } fr
const props = defineProps({
name: String,
suite: String
suite: String,
store: String
})
const emit = defineEmits(['updated'])
@ -23,6 +24,9 @@ function sendRequest() {
const suite = props.suite
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
},
body: JSON.stringify({
suite: suite,
testcase: name
@ -114,6 +118,9 @@ function load() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
},
body: JSON.stringify({
suite: suite,
testcase: name
@ -198,6 +205,9 @@ function saveTestCase() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
},
body: JSON.stringify(testCaseWithSuite.value)
}
fetch('/server.Runner/UpdateTestCase', requestOptions).then((e) => {
@ -218,6 +228,9 @@ function deleteTestCase() {
const suite = props.suite
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
},
body: JSON.stringify({
suite: suite,
testcase: name
@ -336,7 +349,10 @@ function insertOrUpdateIntoMap(pair: Pair, pairs: Pair[]) {
const pupularHeaders = ref([] as Pair[])
const requestOptions = {
method: 'POST'
method: 'POST',
headers: {
'X-Store-Name': props.store
},
}
fetch('/server.Runner/PopularHeaders', requestOptions)
.then((response) => response.json())

View File

@ -7,7 +7,8 @@ import type { Suite, TestCase, Pair } from './types'
import { NewSuggestedAPIsQuery } from './types'
const props = defineProps({
name: String
name: String,
store: String,
})
const emit = defineEmits(['updated'])
let querySuggestedAPIs = NewSuggestedAPIsQuery(props.name!)
@ -24,6 +25,9 @@ const suite = ref({
function load() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
},
body: JSON.stringify({
name: props.name
})
@ -51,6 +55,9 @@ watch(props, () => {
function save() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
},
body: JSON.stringify(suite.value)
}
fetch('/server.Runner/UpdateTestSuite', requestOptions)
@ -91,6 +98,9 @@ const submitForm = async (formEl: FormInstance | undefined) => {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
},
body: JSON.stringify({
suiteName: props.name,
data: {
@ -118,6 +128,9 @@ const submitForm = async (formEl: FormInstance | undefined) => {
function del() {
const requestOptions = {
method: 'POST',
headers: {
'X-Store-Name': props.store
},
body: JSON.stringify({
name: props.name
})
@ -214,7 +227,6 @@ function paramChange() {
ref="testcaseFormRef"
status-icon
label-width="60px"
class="demo-ruleForm"
>
<el-form-item label="Name" prop="name">
<el-input v-model="testCaseForm.name" test-id="case-form-name"/>

View File

@ -74,16 +74,29 @@ tiup playground --db.host 0.0.0.0
```
```shell
# create a network
# start the server with gRPC storage
podman run -p 8080:8080 ghcr.io/linuxsuren/api-testing:master \
atest server --storage grpc --grpc-storage 192.168.1.98:7071 --console-path=/var/www/html
# start the gRPC storage which connect to an ORM database
podman run -p 7071:7071 ghcr.io/linuxsuren/api-testing:master \
atest-store-orm --address 192.168.1.98:4000 --user root --database test
```
# create a config file
mkdir bin
echo "- name: db
kind:
name: database
url: localhost:7071
url: localhost:4000
username: root
properties:
database: test" > bin/stores.yaml
> Please don't forget to replace `192.168.1.98` to your own IP address.
# start the server with gRPC storage
podman run -p 8080:8080 -v bin:var/data/atest \
--network host \
ghcr.io/linuxsuren/api-testing:master \
atest server --console-path=/var/www/html \
--config-dir=/var/data/atest
# start the gRPC storage which ready to connect to an ORM database
podman run -p 7071:7071 \
--network host \
ghcr.io/linuxsuren/api-testing:master atest-store-orm
```
## Extensions
Developers could have a storage extension. Implement a gRPC server according to [loader.proto](../pkg/testing/remote/loader.proto) is required.

View File

@ -2,11 +2,13 @@ package main
import (
"context"
"errors"
"fmt"
"net"
"os"
"github.com/linuxsuren/api-testing/extensions/store-orm/pkg"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/spf13/cobra"
"google.golang.org/grpc"
@ -23,9 +25,6 @@ func main() {
RunE: opt.runE,
}
flags := cmd.Flags()
flags.StringVarP(&opt.user, "user", "u", "root", "The user name of database")
flags.StringVarP(&opt.address, "address", "", "127.0.0.1:4000", "The address of database")
flags.StringVarP(&opt.database, "database", "", "test", "The database name")
flags.IntVarP(&opt.port, "port", "p", 7071, "The port of gRPC server")
if err := cmd.Execute(); err != nil {
os.Exit(1)
@ -34,7 +33,7 @@ func main() {
func (o *option) runE(cmd *cobra.Command, args []string) (err error) {
var removeServer remote.LoaderServer
if removeServer, err = NewRemoteServer(o.user, o.address, o.database); err != nil {
if removeServer, err = NewRemoteServer(); err != nil {
return
}
@ -51,30 +50,27 @@ func (o *option) runE(cmd *cobra.Command, args []string) (err error) {
}
type option struct {
user, address, database string
port int
port int
}
type server struct {
type dbserver struct {
remote.UnimplementedLoaderServer
db *gorm.DB
}
// NewRemoteServer creates a remote server instance
func NewRemoteServer(user, address, database string) (s remote.LoaderServer, err error) {
var db *gorm.DB
if db, err = createDB(user, address, database); err == nil {
s = &server{db: db}
}
func NewRemoteServer() (s remote.LoaderServer, err error) {
s = &dbserver{}
return
}
func createDB(user, address, database string) (db *gorm.DB, err error) {
dsn := fmt.Sprintf("%s:@tcp(%s)/%s?charset=utf8mb4", user, address, database)
fmt.Println("try to connect to", dsn)
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
err = fmt.Errorf("failed to connect to %s, %v", dsn, err)
return
}
@ -83,10 +79,40 @@ func createDB(user, address, database string) (db *gorm.DB, err error) {
return
}
func (s *server) ListTestSuite(context.Context, *remote.Empty) (suites *remote.TestSuites, err error) {
items := make([]*pkg.TestSuite, 0)
s.db.Find(&items)
var dbCache map[string]*gorm.DB = make(map[string]*gorm.DB)
func (s *dbserver) getClient(ctx context.Context) (db *gorm.DB, err error) {
store := remote.GetStoreFromContext(ctx)
if store == nil {
err = errors.New("no connect to database")
} else {
var ok bool
if db, ok = dbCache[store.Name]; ok && db != nil {
return
}
database := "atest"
for key, val := range store.Properties {
if key == "database" {
database = val
}
}
db, err = createDB(store.Username, store.URL, database)
dbCache[store.Name] = db
}
return
}
func (s *dbserver) ListTestSuite(ctx context.Context, _ *server.Empty) (suites *remote.TestSuites, err error) {
items := make([]*pkg.TestSuite, 0)
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
db.Find(&items)
suites = &remote.TestSuites{}
for i := range items {
suites.Data = append(suites.Data, pkg.ConvertToGRPCTestSuite(items[i]))
@ -94,21 +120,31 @@ func (s *server) ListTestSuite(context.Context, *remote.Empty) (suites *remote.T
return
}
func (s *server) CreateTestSuite(ctx context.Context, testSuite *remote.TestSuite) (reply *remote.Empty, err error) {
reply = &remote.Empty{}
s.db.Create(pkg.ConvertToDBTestSuite(testSuite))
func (s *dbserver) CreateTestSuite(ctx context.Context, testSuite *remote.TestSuite) (reply *server.Empty, err error) {
reply = &server.Empty{}
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
db.Create(pkg.ConvertToDBTestSuite(testSuite))
return
}
const nameQuery = `name = ?`
func (s *server) GetTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *remote.TestSuite, err error) {
func (s *dbserver) GetTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *remote.TestSuite, err error) {
query := &pkg.TestSuite{}
s.db.Find(&query, nameQuery, suite.Name)
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
db.Find(&query, nameQuery, suite.Name)
reply = pkg.ConvertToGRPCTestSuite(query)
if suite.Full {
var testcases *remote.TestCases
var testcases *server.TestCases
if testcases, err = s.ListTestCases(ctx, &remote.TestSuite{
Name: suite.Name,
}); err == nil && testcases != nil {
@ -118,9 +154,15 @@ func (s *server) GetTestSuite(ctx context.Context, suite *remote.TestSuite) (rep
return
}
func (s *server) UpdateTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *remote.TestSuite, err error) {
func (s *dbserver) UpdateTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *remote.TestSuite, err error) {
reply = &remote.TestSuite{}
input := pkg.ConvertToDBTestSuite(suite)
testSuiteIdentity(s.db, input).Updates(input)
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
testSuiteIdentity(db, input).Updates(input)
return
}
@ -128,41 +170,63 @@ func testSuiteIdentity(db *gorm.DB, suite *pkg.TestSuite) *gorm.DB {
return db.Model(suite).Where(nameQuery, suite.Name)
}
func (s *server) DeleteTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *remote.Empty, err error) {
reply = &remote.Empty{}
s.db.Delete(suite, nameQuery, suite.Name)
func (s *dbserver) DeleteTestSuite(ctx context.Context, suite *remote.TestSuite) (reply *server.Empty, err error) {
reply = &server.Empty{}
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
db.Delete(suite, nameQuery, suite.Name)
return
}
func (s *server) ListTestCases(ctx context.Context, suite *remote.TestSuite) (result *remote.TestCases, err error) {
func (s *dbserver) ListTestCases(ctx context.Context, suite *remote.TestSuite) (result *server.TestCases, err error) {
items := make([]*pkg.TestCase, 0)
s.db.Find(&items, "suite_name = ?", suite.Name)
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
db.Find(&items, "suite_name = ?", suite.Name)
result = &remote.TestCases{}
result = &server.TestCases{}
for i := range items {
result.Data = append(result.Data, pkg.ConvertToRemoteTestCase(items[i]))
}
return
}
func (s *server) CreateTestCase(ctx context.Context, testcase *remote.TestCase) (reply *remote.Empty, err error) {
func (s *dbserver) CreateTestCase(ctx context.Context, testcase *server.TestCase) (reply *server.Empty, err error) {
payload := pkg.ConverToDBTestCase(testcase)
s.db.Create(&payload)
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
reply = &server.Empty{}
db.Create(&payload)
return
}
func (s *server) GetTestCase(ctx context.Context, testcase *remote.TestCase) (result *remote.TestCase, err error) {
func (s *dbserver) GetTestCase(ctx context.Context, testcase *server.TestCase) (result *server.TestCase, err error) {
item := &pkg.TestCase{}
s.db.Find(&item, "suite_name = ? AND name = ?", testcase.SuiteName, testcase.Name)
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
db.Find(&item, "suite_name = ? AND name = ?", testcase.SuiteName, testcase.Name)
result = pkg.ConvertToRemoteTestCase(item)
return
}
func (s *server) UpdateTestCase(ctx context.Context, testcase *remote.TestCase) (reply *remote.TestCase, err error) {
reply = &remote.TestCase{}
func (s *dbserver) UpdateTestCase(ctx context.Context, testcase *server.TestCase) (reply *server.TestCase, err error) {
reply = &server.TestCase{}
input := pkg.ConverToDBTestCase(testcase)
testCaseIdentiy(s.db, input).Updates(input)
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
testCaseIdentiy(db, input).Updates(input)
data := make(map[string]interface{})
if input.ExpectBody == "" {
@ -173,15 +237,19 @@ func (s *server) UpdateTestCase(ctx context.Context, testcase *remote.TestCase)
}
if len(data) > 0 {
testCaseIdentiy(s.db, input).Updates(data)
testCaseIdentiy(db, input).Updates(data)
}
return
}
func (s *server) DeleteTestCase(ctx context.Context, testcase *remote.TestCase) (reply *remote.Empty, err error) {
reply = &remote.Empty{}
func (s *dbserver) DeleteTestCase(ctx context.Context, testcase *server.TestCase) (reply *server.Empty, err error) {
reply = &server.Empty{}
input := pkg.ConverToDBTestCase(testcase)
testCaseIdentiy(s.db, input).Delete(input)
var db *gorm.DB
if db, err = s.getClient(ctx); err != nil {
return
}
testCaseIdentiy(db, input).Delete(input)
return
}

View File

@ -3,10 +3,11 @@ package pkg
import (
"encoding/json"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
)
func ConverToDBTestCase(testcase *remote.TestCase) (result *TestCase) {
func ConverToDBTestCase(testcase *server.TestCase) (result *TestCase) {
result = &TestCase{
Name: testcase.Name,
SuiteName: testcase.SuiteName,
@ -28,16 +29,16 @@ func ConverToDBTestCase(testcase *remote.TestCase) (result *TestCase) {
result.ExpectStatusCode = int(resp.StatusCode)
result.ExpectHeader = pairToJSON(resp.Header)
result.ExpectBodyFields = pairToJSON(resp.BodyFieldsExpect)
result.ExpectVerify = sliceToJSON(resp.Verify)
result.ExpectVerify = SliceToJSON(resp.Verify)
}
return
}
func ConvertToRemoteTestCase(testcase *TestCase) (result *remote.TestCase) {
result = &remote.TestCase{
func ConvertToRemoteTestCase(testcase *TestCase) (result *server.TestCase) {
result = &server.TestCase{
Name: testcase.Name,
Request: &remote.Request{
Request: &server.Request{
Api: testcase.API,
Method: testcase.Method,
Body: testcase.Body,
@ -46,7 +47,7 @@ func ConvertToRemoteTestCase(testcase *TestCase) (result *remote.TestCase) {
Form: jsonToPair(testcase.Form),
},
Response: &remote.Response{
Response: &server.Response{
StatusCode: int32(testcase.ExpectStatusCode),
Body: testcase.ExpectBody,
Schema: testcase.ExpectSchema,
@ -67,6 +68,9 @@ func ConvertToDBTestSuite(suite *remote.TestSuite) (result *TestSuite) {
result.SpecKind = suite.Spec.Kind
result.SpecURL = suite.Spec.Url
}
if suite.Param != nil {
result.Param = pairToJSON(suite.Param)
}
return
}
@ -74,24 +78,30 @@ func ConvertToGRPCTestSuite(suite *TestSuite) (result *remote.TestSuite) {
result = &remote.TestSuite{
Name: suite.Name,
Api: suite.API,
Spec: &remote.APISpec{
Spec: &server.APISpec{
Kind: suite.SpecKind,
Url: suite.SpecURL,
},
Param: jsonToPair(suite.Param),
}
return
}
func sliceToJSON(slice []string) (result string) {
func SliceToJSON(slice []string) (result string) {
var data []byte
var err error
if data, err = json.Marshal(slice); err == nil {
result = string(data)
if slice != nil {
if data, err = json.Marshal(slice); err == nil {
result = string(data)
}
}
if result == "" {
result = "[]"
}
return
}
func pairToJSON(pair []*remote.Pair) (result string) {
func pairToJSON(pair []*server.Pair) (result string) {
var obj = make(map[string]string)
for i := range pair {
k := pair[i].Key
@ -107,12 +117,12 @@ func pairToJSON(pair []*remote.Pair) (result string) {
return
}
func jsonToPair(jsonStr string) (pairs []*remote.Pair) {
func jsonToPair(jsonStr string) (pairs []*server.Pair) {
pairMap := make(map[string]string, 0)
err := json.Unmarshal([]byte(jsonStr), &pairMap)
if err == nil {
for k, v := range pairMap {
pairs = append(pairs, &remote.Pair{
pairs = append(pairs, &server.Pair{
Key: k, Value: v,
})
}

View File

@ -4,6 +4,7 @@ import (
"testing"
"github.com/linuxsuren/api-testing/extensions/store-orm/pkg"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing/remote"
"github.com/stretchr/testify/assert"
)
@ -25,9 +26,9 @@ func TestConvertToRemoteTestCase(t *testing.T) {
ExpectBodyFields: sampleJSONMap,
ExpectVerify: `["one"]`,
})
assert.Equal(t, &remote.TestCase{
assert.Equal(t, &server.TestCase{
Name: "name",
Request: &remote.Request{
Request: &server.Request{
Api: "api",
Method: "get",
Body: "body",
@ -35,7 +36,7 @@ func TestConvertToRemoteTestCase(t *testing.T) {
Header: samplePairs,
Form: samplePairs,
},
Response: &remote.Response{
Response: &server.Response{
StatusCode: 1,
BodyFieldsExpect: samplePairs,
Verify: []string{"one"},
@ -48,13 +49,13 @@ func TestConvertToRemoteTestCase(t *testing.T) {
func TestConverToDBTestCase(t *testing.T) {
t.Run("without request and response", func(t *testing.T) {
result := pkg.ConverToDBTestCase(&remote.TestCase{})
result := pkg.ConverToDBTestCase(&server.TestCase{})
assert.Equal(t, &pkg.TestCase{}, result)
})
t.Run("only have request", func(t *testing.T) {
result := pkg.ConverToDBTestCase(&remote.TestCase{
Request: &remote.Request{
result := pkg.ConverToDBTestCase(&server.TestCase{
Request: &server.Request{
Api: "api",
Method: "get",
Body: "body",
@ -74,8 +75,8 @@ func TestConverToDBTestCase(t *testing.T) {
})
t.Run("only have response", func(t *testing.T) {
result := pkg.ConverToDBTestCase(&remote.TestCase{
Response: &remote.Response{
result := pkg.ConverToDBTestCase(&server.TestCase{
Response: &server.Response{
StatusCode: 1,
Body: "body",
Schema: "schema",
@ -98,12 +99,18 @@ func TestConverToDBTestCase(t *testing.T) {
func TestConvertTestSuite(t *testing.T) {
t.Run("ConvertToDBTestSuite", func(t *testing.T) {
result := pkg.ConvertToDBTestSuite(&remote.TestSuite{
Name: "name",
Api: "api",
Name: "name",
Api: "api",
Param: samplePairs,
Spec: &server.APISpec{
Kind: "kind",
},
})
assert.Equal(t, &pkg.TestSuite{
Name: "name",
API: "api",
Name: "name",
API: "api",
SpecKind: "kind",
Param: `{"key":"value"}`,
}, result)
})
@ -115,14 +122,18 @@ func TestConvertTestSuite(t *testing.T) {
assert.Equal(t, &remote.TestSuite{
Name: "name",
Api: "api",
Spec: &remote.APISpec{},
Spec: &server.APISpec{},
}, result)
})
t.Run("sliceToJSON", func(t *testing.T) {
assert.Equal(t, "[]", pkg.SliceToJSON(nil))
})
}
const sampleJSONMap = `{"key":"value"}`
var samplePairs []*remote.Pair = []*remote.Pair{{
var samplePairs []*server.Pair = []*server.Pair{{
Key: "key",
Value: "value",
}}

View File

@ -23,4 +23,5 @@ type TestSuite struct {
API string
SpecKind string
SpecURL string
Param string
}

2
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/antonmedv/expr v1.12.1
github.com/cucumber/godog v0.12.6
github.com/ghodss/yaml v1.0.0
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0
github.com/h2non/gock v1.2.0
github.com/invopop/jsonschema v0.7.0
@ -57,6 +58,7 @@ require (
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

61
go.sum
View File

@ -1,3 +1,6 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
@ -6,8 +9,12 @@ github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antonmedv/expr v1.12.1 h1:GTGrGN1kxxb+le0uQKaFRK8By4cvq1sleUCGE/U6hHg=
github.com/antonmedv/expr v1.12.1/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -21,22 +28,36 @@ github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
@ -86,6 +107,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -132,22 +155,41 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -165,16 +207,32 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e h1:Ao9GzfUMPH3zjVfzXG5rlWlk+Q8MXWKwWpwVQE1MXfw=
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
@ -186,6 +244,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
@ -193,3 +252,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

5
pkg/server/constant.go Normal file
View File

@ -0,0 +1,5 @@
package server
const (
HeaderKeyStoreName = "X-Store-Name"
)

43
pkg/server/convert.go Normal file
View File

@ -0,0 +1,43 @@
/**
MIT License
Copyright (c) 2023 Rick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package server
import "github.com/linuxsuren/api-testing/pkg/testing"
func ToGRPCStore(store testing.Store) (result *Store) {
result = &Store{
Name: store.Name,
Kind: &StoreKind{
Name: store.Kind.Name,
Url: store.Kind.URL,
},
Description: store.Description,
Url: store.URL,
Username: store.Username,
Password: store.Password,
Properties: mapToPair(store.Properties),
}
return
}

View File

@ -0,0 +1,62 @@
/**
MIT License
Copyright (c) 2023 Rick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package server
import (
"testing"
atest "github.com/linuxsuren/api-testing/pkg/testing"
"github.com/stretchr/testify/assert"
)
func TestToGRPCStore(t *testing.T) {
assert.Equal(t, &Store{
Name: "test",
Description: "desc",
Kind: &StoreKind{
Name: "test",
Url: urlFoo,
},
Url: urlFoo,
Username: "user",
Password: "pass",
Properties: []*Pair{{
Key: "foo", Value: "bar",
}},
}, ToGRPCStore(atest.Store{
Name: "test",
Description: "desc",
Kind: atest.StoreKind{
Name: "test",
URL: urlFoo,
},
URL: urlFoo,
Username: "user",
Password: "pass",
Properties: map[string]string{
"foo": "bar",
},
}))
}

15
pkg/server/gateway.go Normal file
View File

@ -0,0 +1,15 @@
package server
import (
context "context"
"net/http"
"google.golang.org/grpc/metadata"
)
// MetadataStoreFunc is a function that extracts metadata from a request.
func MetadataStoreFunc(ctx context.Context, r *http.Request) (md metadata.MD) {
store := r.Header.Get(HeaderKeyStoreName)
md = metadata.Pairs(HeaderKeyStoreName, store)
return
}

View File

@ -0,0 +1,20 @@
package server
import (
"context"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestMetadataStoreFunc(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, "/", nil)
req.Header.Set(HeaderKeyStoreName, "test")
if !assert.NoError(t, err) {
return
}
md := MetadataStoreFunc(context.TODO(), req)
assert.Equal(t, "test", md.Get(HeaderKeyStoreName)[0])
}

View File

@ -19,17 +19,24 @@ import (
"github.com/linuxsuren/api-testing/pkg/testing"
"github.com/linuxsuren/api-testing/pkg/version"
"github.com/linuxsuren/api-testing/sample"
"google.golang.org/grpc/metadata"
"gopkg.in/yaml.v3"
)
type server struct {
UnimplementedRunnerServer
loader testing.Writer
loader testing.Writer
storeWriterFactory testing.StoreWriterFactory
configDir string
}
// NewRemoteServer creates a remote server instance
func NewRemoteServer(loader testing.Writer) RunnerServer {
return &server{loader: loader}
func NewRemoteServer(loader testing.Writer, storeWriterFactory testing.StoreWriterFactory, configDir string) RunnerServer {
return &server{
loader: loader,
storeWriterFactory: storeWriterFactory,
configDir: configDir,
}
}
func withDefaultValue(old, defVal any) any {
@ -92,6 +99,33 @@ func resetEnv(oldEnv map[string]string) {
}
}
func (s *server) getLoader(ctx context.Context) (loader testing.Writer) {
var ok bool
loader = s.loader
var mdd metadata.MD
if mdd, ok = metadata.FromIncomingContext(ctx); ok {
storeNameMeta := mdd.Get(HeaderKeyStoreName)
if len(storeNameMeta) > 0 {
storeName := storeNameMeta[0]
if storeName == "local" {
return
}
store, err := testing.NewStoreFactory(s.configDir).GetStore(storeName)
if err == nil && store != nil {
loader, err = s.storeWriterFactory.NewInstance(*store)
if err != nil {
fmt.Println("failed to new grpc loader from store", store.Name, err)
}
} else {
fmt.Println("failed to get store", storeName, err)
}
}
}
return
}
// Run start to run the test task
func (s *server) Run(ctx context.Context, task *TestTask) (reply *TestResult, err error) {
task.Level = withDefaultValue(task.Level, "info").(string)
@ -171,12 +205,13 @@ func (s *server) GetVersion(ctx context.Context, in *Empty) (reply *HelloReply,
}
func (s *server) GetSuites(ctx context.Context, in *Empty) (reply *Suites, err error) {
loader := s.getLoader(ctx)
reply = &Suites{
Data: make(map[string]*Items),
}
var suites []testing.TestSuite
if suites, err = s.loader.ListTestSuite(); err == nil && suites != nil {
if suites, err = loader.ListTestSuite(); err == nil && suites != nil {
for _, suite := range suites {
items := &Items{}
for _, item := range suite.Items {
@ -190,13 +225,15 @@ func (s *server) GetSuites(ctx context.Context, in *Empty) (reply *Suites, err e
}
func (s *server) CreateTestSuite(ctx context.Context, in *TestSuiteIdentity) (reply *HelloReply, err error) {
err = s.loader.CreateSuite(in.Name, in.Api)
loader := s.getLoader(ctx)
err = loader.CreateSuite(in.Name, in.Api)
return
}
func (s *server) GetTestSuite(ctx context.Context, in *TestSuiteIdentity) (result *TestSuite, err error) {
loader := s.getLoader(ctx)
var suite *testing.TestSuite
if suite, _, err = s.loader.GetSuite(in.Name); err == nil && suite != nil {
if suite, _, err = loader.GetSuite(in.Name); err == nil && suite != nil {
result = &TestSuite{
Name: suite.Name,
Api: suite.API,
@ -227,19 +264,22 @@ func convertToTestingTestSuite(in *TestSuite) (suite *testing.TestSuite) {
func (s *server) UpdateTestSuite(ctx context.Context, in *TestSuite) (reply *HelloReply, err error) {
reply = &HelloReply{}
err = s.loader.UpdateSuite(*convertToTestingTestSuite(in))
loader := s.getLoader(ctx)
err = loader.UpdateSuite(*convertToTestingTestSuite(in))
return
}
func (s *server) DeleteTestSuite(ctx context.Context, in *TestSuiteIdentity) (reply *HelloReply, err error) {
reply = &HelloReply{}
err = s.loader.DeleteSuite(in.Name)
loader := s.getLoader(ctx)
err = loader.DeleteSuite(in.Name)
return
}
func (s *server) ListTestCase(ctx context.Context, in *TestSuiteIdentity) (result *Suite, err error) {
var items []testing.TestCase
if items, err = s.loader.ListTestCase(in.Name); err == nil {
loader := s.getLoader(ctx)
if items, err = loader.ListTestCase(in.Name); err == nil {
result = &Suite{}
for _, item := range items {
result.Items = append(result.Items, convertToGRPCTestCase(item))
@ -250,7 +290,8 @@ func (s *server) ListTestCase(ctx context.Context, in *TestSuiteIdentity) (resul
func (s *server) GetTestCase(ctx context.Context, in *TestCaseIdentity) (reply *TestCase, err error) {
var result testing.TestCase
if result, err = s.loader.GetTestCase(in.Suite, in.Testcase); err == nil {
loader := s.getLoader(ctx)
if result, err = loader.GetTestCase(in.Suite, in.Testcase); err == nil {
reply = convertToGRPCTestCase(result)
}
return
@ -286,7 +327,8 @@ func convertToGRPCTestCase(testCase testing.TestCase) (result *TestCase) {
func (s *server) RunTestCase(ctx context.Context, in *TestCaseIdentity) (result *TestCaseResult, err error) {
var targetTestSuite testing.TestSuite
targetTestSuite, err = s.loader.GetTestSuite(in.Suite, true)
loader := s.getLoader(ctx)
targetTestSuite, err = loader.GetTestSuite(in.Suite, true)
if err != nil {
err = nil
result = &TestCaseResult{
@ -397,7 +439,8 @@ func convertToTestingTestCase(in *TestCase) (result testing.TestCase) {
func (s *server) CreateTestCase(ctx context.Context, in *TestCaseWithSuite) (reply *HelloReply, err error) {
reply = &HelloReply{}
err = s.loader.CreateTestCase(in.SuiteName, convertToTestingTestCase(in.Data))
loader := s.getLoader(ctx)
err = loader.CreateTestCase(in.SuiteName, convertToTestingTestCase(in.Data))
return
}
@ -407,12 +450,14 @@ func (s *server) UpdateTestCase(ctx context.Context, in *TestCaseWithSuite) (rep
err = errors.New("data is required")
return
}
err = s.loader.UpdateTestCase(in.SuiteName, convertToTestingTestCase(in.Data))
loader := s.getLoader(ctx)
err = loader.UpdateTestCase(in.SuiteName, convertToTestingTestCase(in.Data))
return
}
func (s *server) DeleteTestCase(ctx context.Context, in *TestCaseIdentity) (reply *HelloReply, err error) {
err = s.loader.DeleteTestCase(in.Suite, in.Testcase)
loader := s.getLoader(ctx)
err = loader.DeleteTestCase(in.Suite, in.Testcase)
return
}
@ -437,7 +482,8 @@ func (s *server) GetSuggestedAPIs(ctx context.Context, in *TestSuiteIdentity) (r
reply = &TestCases{}
var suite *testing.TestSuite
if suite, _, err = s.loader.GetSuite(in.Name); err != nil {
loader := s.loader
if suite, _, err = loader.GetSuite(in.Name); err != nil {
return
}
@ -483,6 +529,29 @@ func (s *server) FunctionsQuery(ctx context.Context, in *SimpleQuery) (reply *Pa
return
}
func (s *server) GetStores(ctx context.Context, in *Empty) (reply *Stores, err error) {
storeFactory := testing.NewStoreFactory(s.configDir)
var stores []testing.Store
if stores, err = storeFactory.GetStores(); err == nil {
reply = &Stores{
Data: make([]*Store, 0),
}
for _, item := range stores {
reply.Data = append(reply.Data, ToGRPCStore(item))
}
}
return
}
func (s *server) CreateStore(ctx context.Context, in *Store) (reply *Store, err error) {
return
}
func (s *server) DeleteStore(ctx context.Context, in *Store) (reply *Store, err error) {
return
}
func (s *server) VerifyStore(ctx context.Context, in *SimpleQuery) (reply *CommonResult, err error) {
return
}
//go:embed data/headers.yaml
var popularHeaders string

View File

@ -22,7 +22,7 @@ const (
func TestRemoteServer(t *testing.T) {
loader := atesting.NewFileWriter("")
loader.Put("testdata/simple.yaml")
server := NewRemoteServer(loader)
server := NewRemoteServer(loader, nil, "")
_, err := server.Run(context.TODO(), &TestTask{
Kind: "fake",
})
@ -93,7 +93,7 @@ func TestRemoteServer(t *testing.T) {
func TestRunTestCase(t *testing.T) {
loader := atesting.NewFileWriter("")
loader.Put("testdata/simple.yaml")
server := NewRemoteServer(loader)
server := NewRemoteServer(loader, nil, "")
defer gock.Clean()
gock.New(urlFoo).Get("/").MatchHeader("key", "value").
@ -266,7 +266,7 @@ func TestUpdateTestCase(t *testing.T) {
assert.NoError(t, err)
ctx := context.Background()
server := NewRemoteServer(writer)
server := NewRemoteServer(writer, nil, "")
_, err = server.UpdateTestCase(ctx, &TestCaseWithSuite{
SuiteName: "simple",
Data: &TestCase{
@ -338,7 +338,7 @@ func TestListTestCase(t *testing.T) {
writer := atesting.NewFileWriter(os.TempDir())
writer.Put(tmpFile.Name())
server := NewRemoteServer(writer)
server := NewRemoteServer(writer, nil, "")
ctx := context.Background()
t.Run("get two testcases", func(t *testing.T) {
@ -471,7 +471,7 @@ func TestFunctionsQuery(t *testing.T) {
func getRemoteServerInTempDir() (server RunnerServer) {
writer := atesting.NewFileWriter(os.TempDir())
server = NewRemoteServer(writer)
server = NewRemoteServer(writer, nil, "")
return
}

View File

@ -717,9 +717,10 @@ type TestCase struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Request *Request `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"`
Response *Response `protobuf:"bytes,3,opt,name=response,proto3" json:"response,omitempty"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
SuiteName string `protobuf:"bytes,2,opt,name=suiteName,proto3" json:"suiteName,omitempty"`
Request *Request `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"`
Response *Response `protobuf:"bytes,4,opt,name=response,proto3" json:"response,omitempty"`
}
func (x *TestCase) Reset() {
@ -761,6 +762,13 @@ func (x *TestCase) GetName() string {
return ""
}
func (x *TestCase) GetSuiteName() string {
if x != nil {
return x.SuiteName
}
return ""
}
func (x *TestCase) GetRequest() *Request {
if x != nil {
return x.Request
@ -1185,6 +1193,305 @@ func (x *SimpleQuery) GetName() string {
return ""
}
type Stores struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Data []*Store `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"`
}
func (x *Stores) Reset() {
*x = Stores{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_server_server_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Stores) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Stores) ProtoMessage() {}
func (x *Stores) ProtoReflect() protoreflect.Message {
mi := &file_pkg_server_server_proto_msgTypes[19]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Stores.ProtoReflect.Descriptor instead.
func (*Stores) Descriptor() ([]byte, []int) {
return file_pkg_server_server_proto_rawDescGZIP(), []int{19}
}
func (x *Stores) GetData() []*Store {
if x != nil {
return x.Data
}
return nil
}
type Store struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"`
Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"`
Properties []*Pair `protobuf:"bytes,6,rep,name=properties,proto3" json:"properties,omitempty"`
Kind *StoreKind `protobuf:"bytes,7,opt,name=kind,proto3" json:"kind,omitempty"`
}
func (x *Store) Reset() {
*x = Store{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_server_server_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Store) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Store) ProtoMessage() {}
func (x *Store) ProtoReflect() protoreflect.Message {
mi := &file_pkg_server_server_proto_msgTypes[20]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Store.ProtoReflect.Descriptor instead.
func (*Store) Descriptor() ([]byte, []int) {
return file_pkg_server_server_proto_rawDescGZIP(), []int{20}
}
func (x *Store) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Store) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *Store) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
func (x *Store) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *Store) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
func (x *Store) GetProperties() []*Pair {
if x != nil {
return x.Properties
}
return nil
}
func (x *Store) GetKind() *StoreKind {
if x != nil {
return x.Kind
}
return nil
}
type StoreKinds struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Data []*StoreKind `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"`
}
func (x *StoreKinds) Reset() {
*x = StoreKinds{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_server_server_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *StoreKinds) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StoreKinds) ProtoMessage() {}
func (x *StoreKinds) ProtoReflect() protoreflect.Message {
mi := &file_pkg_server_server_proto_msgTypes[21]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use StoreKinds.ProtoReflect.Descriptor instead.
func (*StoreKinds) Descriptor() ([]byte, []int) {
return file_pkg_server_server_proto_rawDescGZIP(), []int{21}
}
func (x *StoreKinds) GetData() []*StoreKind {
if x != nil {
return x.Data
}
return nil
}
type StoreKind struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
}
func (x *StoreKind) Reset() {
*x = StoreKind{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_server_server_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *StoreKind) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StoreKind) ProtoMessage() {}
func (x *StoreKind) ProtoReflect() protoreflect.Message {
mi := &file_pkg_server_server_proto_msgTypes[22]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use StoreKind.ProtoReflect.Descriptor instead.
func (*StoreKind) Descriptor() ([]byte, []int) {
return file_pkg_server_server_proto_rawDescGZIP(), []int{22}
}
func (x *StoreKind) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *StoreKind) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
type CommonResult struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *CommonResult) Reset() {
*x = CommonResult{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_server_server_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CommonResult) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CommonResult) ProtoMessage() {}
func (x *CommonResult) ProtoReflect() protoreflect.Message {
mi := &file_pkg_server_server_proto_msgTypes[23]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CommonResult.ProtoReflect.Descriptor instead.
func (*CommonResult) Descriptor() ([]byte, []int) {
return file_pkg_server_server_proto_rawDescGZIP(), []int{23}
}
func (x *CommonResult) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
func (x *CommonResult) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
type Empty struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -1194,7 +1501,7 @@ type Empty struct {
func (x *Empty) Reset() {
*x = Empty{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_server_server_proto_msgTypes[19]
mi := &file_pkg_server_server_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -1207,7 +1514,7 @@ func (x *Empty) String() string {
func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_pkg_server_server_proto_msgTypes[19]
mi := &file_pkg_server_server_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -1220,7 +1527,7 @@ func (x *Empty) ProtoReflect() protoreflect.Message {
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) {
return file_pkg_server_server_proto_rawDescGZIP(), []int{19}
return file_pkg_server_server_proto_rawDescGZIP(), []int{24}
}
var File_pkg_server_server_proto protoreflect.FileDescriptor
@ -1295,126 +1602,171 @@ var file_pkg_server_server_proto_rawDesc = []byte{
0x64, 0x61, 0x74, 0x61, 0x22, 0x31, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65,
0x73, 0x12, 0x24, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73,
0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x77, 0x0a, 0x08, 0x54, 0x65, 0x73, 0x74, 0x43,
0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0xb3, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03,
0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x16,
0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x24, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x95, 0x01, 0x0a, 0x08, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x69, 0x74,
0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x69,
0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x2c, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0xb3, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61,
0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x16, 0x0a,
0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d,
0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x24, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18,
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50,
0x61, 0x69, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x05, 0x71,
0x75, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12,
0x20, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x66, 0x6f, 0x72,
0x6d, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xce, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f,
0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x24, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x05,
0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79,
0x12, 0x20, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x66, 0x6f,
0x72, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xce, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43,
0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x24, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65,
0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a,
0x10, 0x62, 0x6f, 0x64, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x78, 0x70, 0x65, 0x63,
0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x10, 0x62, 0x6f, 0x64, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x73, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66,
0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12,
0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0xa8, 0x01, 0x0a, 0x0e, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74,
0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f,
0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x24,
0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70,
0x75, 0x74, 0x22, 0x2e, 0x0a, 0x04, 0x50, 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x22, 0x29, 0x0a, 0x05, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x04, 0x64,
0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x21, 0x0a,
0x0b, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0xfd, 0x07, 0x0a, 0x06, 0x52, 0x75,
0x6e, 0x6e, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x10, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x12, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x0d, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,
0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65,
0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x2c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x75, 0x69, 0x74,
0x65, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x1a, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x69, 0x74, 0x65,
0x73, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x65,
0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x74, 0x79, 0x1a, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x53, 0x75, 0x69, 0x74, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74,
0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c,
0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x54,
0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x69, 0x74,
0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61,
0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x52, 0x65,
0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x73,
0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54,
0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a,
0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73,
0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73,
0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54,
0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53, 0x75, 0x69, 0x74, 0x65,
0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52,
0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53, 0x75,
0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c,
0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x0e, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65,
0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x10,
0x62, 0x6f, 0x64, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74,
0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x50, 0x61, 0x69, 0x72, 0x52, 0x10, 0x62, 0x6f, 0x64, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73,
0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79,
0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x16,
0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0xa8, 0x01, 0x0a, 0x0e, 0x54, 0x65, 0x73, 0x74, 0x43,
0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x24, 0x0a,
0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61,
0x64, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75,
0x74, 0x22, 0x2e, 0x0a, 0x04, 0x50, 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x22, 0x29, 0x0a, 0x05, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x04, 0x64, 0x61,
0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x21, 0x0a, 0x0b,
0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22,
0x2b, 0x0a, 0x06, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x04, 0x64, 0x61, 0x74,
0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xdc, 0x01, 0x0a,
0x05, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03,
0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a,
0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61,
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2c, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72,
0x74, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72,
0x74, 0x69, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x33, 0x0a, 0x0a, 0x53,
0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x64, 0x61, 0x74,
0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61,
0x22, 0x31, 0x0a, 0x09, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x75, 0x72, 0x6c, 0x22, 0x42, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a,
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x32, 0xfb, 0x09, 0x0a, 0x06, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x03, 0x52,
0x75, 0x6e, 0x12, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x54, 0x61, 0x73, 0x6b, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65,
0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x2c, 0x0a, 0x09, 0x47, 0x65,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0c,
0x47, 0x65, 0x74, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0f,
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12,
0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69,
0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c,
0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0c,
0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x53, 0x75, 0x69, 0x74, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53,
0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x41, 0x50, 0x49, 0x73, 0x12, 0x19, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x49,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x73, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0b,
0x52, 0x75, 0x6e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x49, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48,
0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x0e, 0x50,
0x6f, 0x70, 0x75, 0x6c, 0x61, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x0d, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x00, 0x12, 0x42, 0x0a,
0x10, 0x47, 0x65, 0x74, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x65, 0x64, 0x41, 0x50, 0x49,
0x73, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53,
0x75, 0x69, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x73, 0x22,
0x00, 0x12, 0x36, 0x0a, 0x0e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x51, 0x75,
0x65, 0x72, 0x79, 0x12, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d,
0x70, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x00, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x73, 0x75, 0x72,
0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x70,
0x6b, 0x67, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54,
0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x12,
0x3b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x18,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65,
0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0e,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x19,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65,
0x57, 0x69, 0x74, 0x68, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12,
0x41, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73,
0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43,
0x61, 0x73, 0x65, 0x57, 0x69, 0x74, 0x68, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x12, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79,
0x22, 0x00, 0x12, 0x40, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65,
0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x12,
0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70,
0x6c, 0x79, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x0e, 0x50, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x72, 0x48,
0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50,
0x61, 0x69, 0x72, 0x73, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x0d, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x00, 0x12, 0x31,
0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22,
0x00, 0x12, 0x2d, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00,
0x12, 0x34, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x69, 0x6e, 0x64,
0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x1a, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x4b,
0x69, 0x6e, 0x64, 0x73, 0x22, 0x00, 0x12, 0x2c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x73, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70,
0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x73, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74,
0x6f, 0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65,
0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x12, 0x13, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c,
0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x1a, 0x14, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x00, 0x42, 0x2e,
0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e,
0x75, 0x78, 0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x74, 0x65, 0x73, 0x74,
0x69, 0x6e, 0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1429,7 +1781,7 @@ func file_pkg_server_server_proto_rawDescGZIP() []byte {
return file_pkg_server_server_proto_rawDescData
}
var file_pkg_server_server_proto_msgTypes = make([]protoimpl.MessageInfo, 22)
var file_pkg_server_server_proto_msgTypes = make([]protoimpl.MessageInfo, 27)
var file_pkg_server_server_proto_goTypes = []interface{}{
(*Suites)(nil), // 0: server.Suites
(*Items)(nil), // 1: server.Items
@ -1450,15 +1802,20 @@ var file_pkg_server_server_proto_goTypes = []interface{}{
(*Pair)(nil), // 16: server.Pair
(*Pairs)(nil), // 17: server.Pairs
(*SimpleQuery)(nil), // 18: server.SimpleQuery
(*Empty)(nil), // 19: server.Empty
nil, // 20: server.Suites.DataEntry
nil, // 21: server.TestTask.EnvEntry
(*Stores)(nil), // 19: server.Stores
(*Store)(nil), // 20: server.Store
(*StoreKinds)(nil), // 21: server.StoreKinds
(*StoreKind)(nil), // 22: server.StoreKind
(*CommonResult)(nil), // 23: server.CommonResult
(*Empty)(nil), // 24: server.Empty
nil, // 25: server.Suites.DataEntry
nil, // 26: server.TestTask.EnvEntry
}
var file_pkg_server_server_proto_depIdxs = []int32{
20, // 0: server.Suites.data:type_name -> server.Suites.DataEntry
25, // 0: server.Suites.data:type_name -> server.Suites.DataEntry
16, // 1: server.TestSuite.param:type_name -> server.Pair
4, // 2: server.TestSuite.spec:type_name -> server.APISpec
21, // 3: server.TestTask.env:type_name -> server.TestTask.EnvEntry
26, // 3: server.TestTask.env:type_name -> server.TestTask.EnvEntry
15, // 4: server.TestResult.testCaseResult:type_name -> server.TestCaseResult
12, // 5: server.Suite.items:type_name -> server.TestCase
12, // 6: server.TestCaseWithSuite.data:type_name -> server.TestCase
@ -1472,46 +1829,60 @@ var file_pkg_server_server_proto_depIdxs = []int32{
16, // 14: server.Response.bodyFieldsExpect:type_name -> server.Pair
16, // 15: server.TestCaseResult.header:type_name -> server.Pair
16, // 16: server.Pairs.data:type_name -> server.Pair
1, // 17: server.Suites.DataEntry.value:type_name -> server.Items
6, // 18: server.Runner.Run:input_type -> server.TestTask
19, // 19: server.Runner.Sample:input_type -> server.Empty
19, // 20: server.Runner.GetVersion:input_type -> server.Empty
19, // 21: server.Runner.GetSuites:input_type -> server.Empty
5, // 22: server.Runner.CreateTestSuite:input_type -> server.TestSuiteIdentity
5, // 23: server.Runner.GetTestSuite:input_type -> server.TestSuiteIdentity
3, // 24: server.Runner.UpdateTestSuite:input_type -> server.TestSuite
5, // 25: server.Runner.DeleteTestSuite:input_type -> server.TestSuiteIdentity
5, // 26: server.Runner.ListTestCase:input_type -> server.TestSuiteIdentity
2, // 27: server.Runner.RunTestCase:input_type -> server.TestCaseIdentity
2, // 28: server.Runner.GetTestCase:input_type -> server.TestCaseIdentity
10, // 29: server.Runner.CreateTestCase:input_type -> server.TestCaseWithSuite
10, // 30: server.Runner.UpdateTestCase:input_type -> server.TestCaseWithSuite
2, // 31: server.Runner.DeleteTestCase:input_type -> server.TestCaseIdentity
19, // 32: server.Runner.PopularHeaders:input_type -> server.Empty
5, // 33: server.Runner.GetSuggestedAPIs:input_type -> server.TestSuiteIdentity
18, // 34: server.Runner.FunctionsQuery:input_type -> server.SimpleQuery
7, // 35: server.Runner.Run:output_type -> server.TestResult
8, // 36: server.Runner.Sample:output_type -> server.HelloReply
8, // 37: server.Runner.GetVersion:output_type -> server.HelloReply
0, // 38: server.Runner.GetSuites:output_type -> server.Suites
8, // 39: server.Runner.CreateTestSuite:output_type -> server.HelloReply
3, // 40: server.Runner.GetTestSuite:output_type -> server.TestSuite
8, // 41: server.Runner.UpdateTestSuite:output_type -> server.HelloReply
8, // 42: server.Runner.DeleteTestSuite:output_type -> server.HelloReply
9, // 43: server.Runner.ListTestCase:output_type -> server.Suite
15, // 44: server.Runner.RunTestCase:output_type -> server.TestCaseResult
12, // 45: server.Runner.GetTestCase:output_type -> server.TestCase
8, // 46: server.Runner.CreateTestCase:output_type -> server.HelloReply
8, // 47: server.Runner.UpdateTestCase:output_type -> server.HelloReply
8, // 48: server.Runner.DeleteTestCase:output_type -> server.HelloReply
17, // 49: server.Runner.PopularHeaders:output_type -> server.Pairs
11, // 50: server.Runner.GetSuggestedAPIs:output_type -> server.TestCases
17, // 51: server.Runner.FunctionsQuery:output_type -> server.Pairs
35, // [35:52] is the sub-list for method output_type
18, // [18:35] is the sub-list for method input_type
18, // [18:18] is the sub-list for extension type_name
18, // [18:18] is the sub-list for extension extendee
0, // [0:18] is the sub-list for field type_name
20, // 17: server.Stores.data:type_name -> server.Store
16, // 18: server.Store.properties:type_name -> server.Pair
22, // 19: server.Store.kind:type_name -> server.StoreKind
22, // 20: server.StoreKinds.data:type_name -> server.StoreKind
1, // 21: server.Suites.DataEntry.value:type_name -> server.Items
6, // 22: server.Runner.Run:input_type -> server.TestTask
24, // 23: server.Runner.GetSuites:input_type -> server.Empty
5, // 24: server.Runner.CreateTestSuite:input_type -> server.TestSuiteIdentity
5, // 25: server.Runner.GetTestSuite:input_type -> server.TestSuiteIdentity
3, // 26: server.Runner.UpdateTestSuite:input_type -> server.TestSuite
5, // 27: server.Runner.DeleteTestSuite:input_type -> server.TestSuiteIdentity
5, // 28: server.Runner.ListTestCase:input_type -> server.TestSuiteIdentity
5, // 29: server.Runner.GetSuggestedAPIs:input_type -> server.TestSuiteIdentity
2, // 30: server.Runner.RunTestCase:input_type -> server.TestCaseIdentity
2, // 31: server.Runner.GetTestCase:input_type -> server.TestCaseIdentity
10, // 32: server.Runner.CreateTestCase:input_type -> server.TestCaseWithSuite
10, // 33: server.Runner.UpdateTestCase:input_type -> server.TestCaseWithSuite
2, // 34: server.Runner.DeleteTestCase:input_type -> server.TestCaseIdentity
24, // 35: server.Runner.PopularHeaders:input_type -> server.Empty
18, // 36: server.Runner.FunctionsQuery:input_type -> server.SimpleQuery
24, // 37: server.Runner.GetVersion:input_type -> server.Empty
24, // 38: server.Runner.Sample:input_type -> server.Empty
24, // 39: server.Runner.GetStoreKinds:input_type -> server.Empty
24, // 40: server.Runner.GetStores:input_type -> server.Empty
20, // 41: server.Runner.CreateStore:input_type -> server.Store
20, // 42: server.Runner.DeleteStore:input_type -> server.Store
18, // 43: server.Runner.VerifyStore:input_type -> server.SimpleQuery
7, // 44: server.Runner.Run:output_type -> server.TestResult
0, // 45: server.Runner.GetSuites:output_type -> server.Suites
8, // 46: server.Runner.CreateTestSuite:output_type -> server.HelloReply
3, // 47: server.Runner.GetTestSuite:output_type -> server.TestSuite
8, // 48: server.Runner.UpdateTestSuite:output_type -> server.HelloReply
8, // 49: server.Runner.DeleteTestSuite:output_type -> server.HelloReply
9, // 50: server.Runner.ListTestCase:output_type -> server.Suite
11, // 51: server.Runner.GetSuggestedAPIs:output_type -> server.TestCases
15, // 52: server.Runner.RunTestCase:output_type -> server.TestCaseResult
12, // 53: server.Runner.GetTestCase:output_type -> server.TestCase
8, // 54: server.Runner.CreateTestCase:output_type -> server.HelloReply
8, // 55: server.Runner.UpdateTestCase:output_type -> server.HelloReply
8, // 56: server.Runner.DeleteTestCase:output_type -> server.HelloReply
17, // 57: server.Runner.PopularHeaders:output_type -> server.Pairs
17, // 58: server.Runner.FunctionsQuery:output_type -> server.Pairs
8, // 59: server.Runner.GetVersion:output_type -> server.HelloReply
8, // 60: server.Runner.Sample:output_type -> server.HelloReply
21, // 61: server.Runner.GetStoreKinds:output_type -> server.StoreKinds
19, // 62: server.Runner.GetStores:output_type -> server.Stores
20, // 63: server.Runner.CreateStore:output_type -> server.Store
20, // 64: server.Runner.DeleteStore:output_type -> server.Store
23, // 65: server.Runner.VerifyStore:output_type -> server.CommonResult
44, // [44:66] is the sub-list for method output_type
22, // [22:44] is the sub-list for method input_type
22, // [22:22] is the sub-list for extension type_name
22, // [22:22] is the sub-list for extension extendee
0, // [0:22] is the sub-list for field type_name
}
func init() { file_pkg_server_server_proto_init() }
@ -1749,6 +2120,66 @@ func file_pkg_server_server_proto_init() {
}
}
file_pkg_server_server_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Stores); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_server_server_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Store); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_server_server_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StoreKinds); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_server_server_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StoreKind); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_server_server_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CommonResult); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_server_server_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
@ -1767,7 +2198,7 @@ func file_pkg_server_server_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pkg_server_server_proto_rawDesc,
NumEnums: 0,
NumMessages: 22,
NumMessages: 27,
NumExtensions: 0,
NumServices: 1,
},

File diff suppressed because it is too large Load Diff

View File

@ -5,27 +5,36 @@ option go_package = "github.com/linuxsuren/api-testing/pkg/server";
package server;
service Runner {
// belong to a specific store
rpc Run (TestTask) returns (TestResult) {}
rpc Sample(Empty) returns (HelloReply) {}
rpc GetVersion(Empty) returns (HelloReply) {}
rpc GetSuites(Empty) returns (Suites) {}
rpc CreateTestSuite(TestSuiteIdentity) returns (HelloReply) {}
rpc GetTestSuite(TestSuiteIdentity) returns (TestSuite) {}
rpc UpdateTestSuite(TestSuite) returns (HelloReply) {}
rpc DeleteTestSuite(TestSuiteIdentity) returns (HelloReply) {}
// test cases related
rpc ListTestCase(TestSuiteIdentity) returns (Suite) {}
rpc GetSuggestedAPIs(TestSuiteIdentity) returns (TestCases) {}
rpc RunTestCase(TestCaseIdentity) returns (TestCaseResult) {}
rpc GetTestCase(TestCaseIdentity) returns (TestCase) {}
rpc CreateTestCase(TestCaseWithSuite) returns (HelloReply) {}
rpc UpdateTestCase(TestCaseWithSuite) returns (HelloReply) {}
rpc DeleteTestCase(TestCaseIdentity) returns (HelloReply) {}
// common services
rpc PopularHeaders(Empty) returns (Pairs) {}
rpc GetSuggestedAPIs(TestSuiteIdentity) returns (TestCases) {}
rpc FunctionsQuery(SimpleQuery) returns (Pairs) {}
rpc GetVersion(Empty) returns (HelloReply) {}
rpc Sample(Empty) returns (HelloReply) {}
// stores related interfaces
rpc GetStoreKinds(Empty) returns (StoreKinds) {}
rpc GetStores(Empty) returns (Stores) {}
rpc CreateStore(Store) returns (Store) {}
rpc DeleteStore(Store) returns (Store) {}
rpc VerifyStore(SimpleQuery) returns (CommonResult) {}
}
message Suites {
@ -49,8 +58,8 @@ message TestSuite {
}
message APISpec {
string kind = 1;
string url = 2;
string kind = 1;
string url = 2;
}
message TestSuiteIdentity {
@ -59,11 +68,11 @@ message TestSuiteIdentity {
}
message TestTask {
string data = 1;
string kind = 2;
string caseName = 3;
string level = 4;
map<string, string> env = 5;
string data = 1;
string kind = 2;
string caseName = 3;
string level = 4;
map<string, string> env = 5;
}
message TestResult {
@ -94,8 +103,9 @@ message TestCases {
message TestCase {
string name = 1;
Request request = 2;
Response response = 3;
string suiteName = 2;
Request request = 3;
Response response = 4;
}
message Request {
@ -138,5 +148,33 @@ message SimpleQuery {
string name = 1;
}
message Stores {
repeated Store data = 1;
}
message Store {
string name = 1;
string description = 2;
string url = 3;
string username = 4;
string password = 5;
repeated Pair properties = 6;
StoreKind kind = 7;
}
message StoreKinds {
repeated StoreKind data = 1;
}
message StoreKind {
string name = 1;
string url = 2;
}
message CommonResult {
bool success = 1;
string message = 2;
}
message Empty {
}

View File

@ -22,23 +22,32 @@ const _ = grpc.SupportPackageIsVersion7
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type RunnerClient interface {
// belong to a specific store
Run(ctx context.Context, in *TestTask, opts ...grpc.CallOption) (*TestResult, error)
Sample(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*HelloReply, error)
GetVersion(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*HelloReply, error)
GetSuites(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Suites, error)
CreateTestSuite(ctx context.Context, in *TestSuiteIdentity, opts ...grpc.CallOption) (*HelloReply, error)
GetTestSuite(ctx context.Context, in *TestSuiteIdentity, opts ...grpc.CallOption) (*TestSuite, error)
UpdateTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*HelloReply, error)
DeleteTestSuite(ctx context.Context, in *TestSuiteIdentity, opts ...grpc.CallOption) (*HelloReply, error)
// test cases related
ListTestCase(ctx context.Context, in *TestSuiteIdentity, opts ...grpc.CallOption) (*Suite, error)
GetSuggestedAPIs(ctx context.Context, in *TestSuiteIdentity, opts ...grpc.CallOption) (*TestCases, error)
RunTestCase(ctx context.Context, in *TestCaseIdentity, opts ...grpc.CallOption) (*TestCaseResult, error)
GetTestCase(ctx context.Context, in *TestCaseIdentity, opts ...grpc.CallOption) (*TestCase, error)
CreateTestCase(ctx context.Context, in *TestCaseWithSuite, opts ...grpc.CallOption) (*HelloReply, error)
UpdateTestCase(ctx context.Context, in *TestCaseWithSuite, opts ...grpc.CallOption) (*HelloReply, error)
DeleteTestCase(ctx context.Context, in *TestCaseIdentity, opts ...grpc.CallOption) (*HelloReply, error)
// common services
PopularHeaders(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Pairs, error)
GetSuggestedAPIs(ctx context.Context, in *TestSuiteIdentity, opts ...grpc.CallOption) (*TestCases, error)
FunctionsQuery(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*Pairs, error)
GetVersion(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*HelloReply, error)
Sample(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*HelloReply, error)
// stores related interfaces
GetStoreKinds(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StoreKinds, error)
GetStores(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Stores, error)
CreateStore(ctx context.Context, in *Store, opts ...grpc.CallOption) (*Store, error)
DeleteStore(ctx context.Context, in *Store, opts ...grpc.CallOption) (*Store, error)
VerifyStore(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*CommonResult, error)
}
type runnerClient struct {
@ -58,24 +67,6 @@ func (c *runnerClient) Run(ctx context.Context, in *TestTask, opts ...grpc.CallO
return out, nil
}
func (c *runnerClient) Sample(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := c.cc.Invoke(ctx, "/server.Runner/Sample", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) GetVersion(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := c.cc.Invoke(ctx, "/server.Runner/GetVersion", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) GetSuites(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Suites, error) {
out := new(Suites)
err := c.cc.Invoke(ctx, "/server.Runner/GetSuites", in, out, opts...)
@ -130,6 +121,15 @@ func (c *runnerClient) ListTestCase(ctx context.Context, in *TestSuiteIdentity,
return out, nil
}
func (c *runnerClient) GetSuggestedAPIs(ctx context.Context, in *TestSuiteIdentity, opts ...grpc.CallOption) (*TestCases, error) {
out := new(TestCases)
err := c.cc.Invoke(ctx, "/server.Runner/GetSuggestedAPIs", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) RunTestCase(ctx context.Context, in *TestCaseIdentity, opts ...grpc.CallOption) (*TestCaseResult, error) {
out := new(TestCaseResult)
err := c.cc.Invoke(ctx, "/server.Runner/RunTestCase", in, out, opts...)
@ -184,18 +184,72 @@ func (c *runnerClient) PopularHeaders(ctx context.Context, in *Empty, opts ...gr
return out, nil
}
func (c *runnerClient) GetSuggestedAPIs(ctx context.Context, in *TestSuiteIdentity, opts ...grpc.CallOption) (*TestCases, error) {
out := new(TestCases)
err := c.cc.Invoke(ctx, "/server.Runner/GetSuggestedAPIs", in, out, opts...)
func (c *runnerClient) FunctionsQuery(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*Pairs, error) {
out := new(Pairs)
err := c.cc.Invoke(ctx, "/server.Runner/FunctionsQuery", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) FunctionsQuery(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*Pairs, error) {
out := new(Pairs)
err := c.cc.Invoke(ctx, "/server.Runner/FunctionsQuery", in, out, opts...)
func (c *runnerClient) GetVersion(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := c.cc.Invoke(ctx, "/server.Runner/GetVersion", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) Sample(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := c.cc.Invoke(ctx, "/server.Runner/Sample", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) GetStoreKinds(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StoreKinds, error) {
out := new(StoreKinds)
err := c.cc.Invoke(ctx, "/server.Runner/GetStoreKinds", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) GetStores(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Stores, error) {
out := new(Stores)
err := c.cc.Invoke(ctx, "/server.Runner/GetStores", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) CreateStore(ctx context.Context, in *Store, opts ...grpc.CallOption) (*Store, error) {
out := new(Store)
err := c.cc.Invoke(ctx, "/server.Runner/CreateStore", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) DeleteStore(ctx context.Context, in *Store, opts ...grpc.CallOption) (*Store, error) {
out := new(Store)
err := c.cc.Invoke(ctx, "/server.Runner/DeleteStore", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) VerifyStore(ctx context.Context, in *SimpleQuery, opts ...grpc.CallOption) (*CommonResult, error) {
out := new(CommonResult)
err := c.cc.Invoke(ctx, "/server.Runner/VerifyStore", in, out, opts...)
if err != nil {
return nil, err
}
@ -206,23 +260,32 @@ func (c *runnerClient) FunctionsQuery(ctx context.Context, in *SimpleQuery, opts
// All implementations must embed UnimplementedRunnerServer
// for forward compatibility
type RunnerServer interface {
// belong to a specific store
Run(context.Context, *TestTask) (*TestResult, error)
Sample(context.Context, *Empty) (*HelloReply, error)
GetVersion(context.Context, *Empty) (*HelloReply, error)
GetSuites(context.Context, *Empty) (*Suites, error)
CreateTestSuite(context.Context, *TestSuiteIdentity) (*HelloReply, error)
GetTestSuite(context.Context, *TestSuiteIdentity) (*TestSuite, error)
UpdateTestSuite(context.Context, *TestSuite) (*HelloReply, error)
DeleteTestSuite(context.Context, *TestSuiteIdentity) (*HelloReply, error)
// test cases related
ListTestCase(context.Context, *TestSuiteIdentity) (*Suite, error)
GetSuggestedAPIs(context.Context, *TestSuiteIdentity) (*TestCases, error)
RunTestCase(context.Context, *TestCaseIdentity) (*TestCaseResult, error)
GetTestCase(context.Context, *TestCaseIdentity) (*TestCase, error)
CreateTestCase(context.Context, *TestCaseWithSuite) (*HelloReply, error)
UpdateTestCase(context.Context, *TestCaseWithSuite) (*HelloReply, error)
DeleteTestCase(context.Context, *TestCaseIdentity) (*HelloReply, error)
// common services
PopularHeaders(context.Context, *Empty) (*Pairs, error)
GetSuggestedAPIs(context.Context, *TestSuiteIdentity) (*TestCases, error)
FunctionsQuery(context.Context, *SimpleQuery) (*Pairs, error)
GetVersion(context.Context, *Empty) (*HelloReply, error)
Sample(context.Context, *Empty) (*HelloReply, error)
// stores related interfaces
GetStoreKinds(context.Context, *Empty) (*StoreKinds, error)
GetStores(context.Context, *Empty) (*Stores, error)
CreateStore(context.Context, *Store) (*Store, error)
DeleteStore(context.Context, *Store) (*Store, error)
VerifyStore(context.Context, *SimpleQuery) (*CommonResult, error)
mustEmbedUnimplementedRunnerServer()
}
@ -233,12 +296,6 @@ type UnimplementedRunnerServer struct {
func (UnimplementedRunnerServer) Run(context.Context, *TestTask) (*TestResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method Run not implemented")
}
func (UnimplementedRunnerServer) Sample(context.Context, *Empty) (*HelloReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Sample not implemented")
}
func (UnimplementedRunnerServer) GetVersion(context.Context, *Empty) (*HelloReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented")
}
func (UnimplementedRunnerServer) GetSuites(context.Context, *Empty) (*Suites, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSuites not implemented")
}
@ -257,6 +314,9 @@ func (UnimplementedRunnerServer) DeleteTestSuite(context.Context, *TestSuiteIden
func (UnimplementedRunnerServer) ListTestCase(context.Context, *TestSuiteIdentity) (*Suite, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTestCase not implemented")
}
func (UnimplementedRunnerServer) GetSuggestedAPIs(context.Context, *TestSuiteIdentity) (*TestCases, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSuggestedAPIs not implemented")
}
func (UnimplementedRunnerServer) RunTestCase(context.Context, *TestCaseIdentity) (*TestCaseResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method RunTestCase not implemented")
}
@ -275,12 +335,30 @@ func (UnimplementedRunnerServer) DeleteTestCase(context.Context, *TestCaseIdenti
func (UnimplementedRunnerServer) PopularHeaders(context.Context, *Empty) (*Pairs, error) {
return nil, status.Errorf(codes.Unimplemented, "method PopularHeaders not implemented")
}
func (UnimplementedRunnerServer) GetSuggestedAPIs(context.Context, *TestSuiteIdentity) (*TestCases, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSuggestedAPIs not implemented")
}
func (UnimplementedRunnerServer) FunctionsQuery(context.Context, *SimpleQuery) (*Pairs, error) {
return nil, status.Errorf(codes.Unimplemented, "method FunctionsQuery not implemented")
}
func (UnimplementedRunnerServer) GetVersion(context.Context, *Empty) (*HelloReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented")
}
func (UnimplementedRunnerServer) Sample(context.Context, *Empty) (*HelloReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Sample not implemented")
}
func (UnimplementedRunnerServer) GetStoreKinds(context.Context, *Empty) (*StoreKinds, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetStoreKinds not implemented")
}
func (UnimplementedRunnerServer) GetStores(context.Context, *Empty) (*Stores, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetStores not implemented")
}
func (UnimplementedRunnerServer) CreateStore(context.Context, *Store) (*Store, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateStore not implemented")
}
func (UnimplementedRunnerServer) DeleteStore(context.Context, *Store) (*Store, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteStore not implemented")
}
func (UnimplementedRunnerServer) VerifyStore(context.Context, *SimpleQuery) (*CommonResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method VerifyStore not implemented")
}
func (UnimplementedRunnerServer) mustEmbedUnimplementedRunnerServer() {}
// UnsafeRunnerServer may be embedded to opt out of forward compatibility for this service.
@ -312,42 +390,6 @@ func _Runner_Run_Handler(srv interface{}, ctx context.Context, dec func(interfac
return interceptor(ctx, in, info, handler)
}
func _Runner_Sample_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).Sample(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/Sample",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).Sample(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).GetVersion(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/GetVersion",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).GetVersion(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_GetSuites_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
@ -456,6 +498,24 @@ func _Runner_ListTestCase_Handler(srv interface{}, ctx context.Context, dec func
return interceptor(ctx, in, info, handler)
}
func _Runner_GetSuggestedAPIs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestSuiteIdentity)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).GetSuggestedAPIs(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/GetSuggestedAPIs",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).GetSuggestedAPIs(ctx, req.(*TestSuiteIdentity))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_RunTestCase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestCaseIdentity)
if err := dec(in); err != nil {
@ -564,24 +624,6 @@ func _Runner_PopularHeaders_Handler(srv interface{}, ctx context.Context, dec fu
return interceptor(ctx, in, info, handler)
}
func _Runner_GetSuggestedAPIs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestSuiteIdentity)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).GetSuggestedAPIs(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/GetSuggestedAPIs",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).GetSuggestedAPIs(ctx, req.(*TestSuiteIdentity))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_FunctionsQuery_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SimpleQuery)
if err := dec(in); err != nil {
@ -600,6 +642,132 @@ func _Runner_FunctionsQuery_Handler(srv interface{}, ctx context.Context, dec fu
return interceptor(ctx, in, info, handler)
}
func _Runner_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).GetVersion(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/GetVersion",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).GetVersion(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_Sample_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).Sample(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/Sample",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).Sample(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_GetStoreKinds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).GetStoreKinds(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/GetStoreKinds",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).GetStoreKinds(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_GetStores_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).GetStores(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/GetStores",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).GetStores(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_CreateStore_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Store)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).CreateStore(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/CreateStore",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).CreateStore(ctx, req.(*Store))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_DeleteStore_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Store)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).DeleteStore(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/DeleteStore",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).DeleteStore(ctx, req.(*Store))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_VerifyStore_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SimpleQuery)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).VerifyStore(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/VerifyStore",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).VerifyStore(ctx, req.(*SimpleQuery))
}
return interceptor(ctx, in, info, handler)
}
// Runner_ServiceDesc is the grpc.ServiceDesc for Runner service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -611,14 +779,6 @@ var Runner_ServiceDesc = grpc.ServiceDesc{
MethodName: "Run",
Handler: _Runner_Run_Handler,
},
{
MethodName: "Sample",
Handler: _Runner_Sample_Handler,
},
{
MethodName: "GetVersion",
Handler: _Runner_GetVersion_Handler,
},
{
MethodName: "GetSuites",
Handler: _Runner_GetSuites_Handler,
@ -643,6 +803,10 @@ var Runner_ServiceDesc = grpc.ServiceDesc{
MethodName: "ListTestCase",
Handler: _Runner_ListTestCase_Handler,
},
{
MethodName: "GetSuggestedAPIs",
Handler: _Runner_GetSuggestedAPIs_Handler,
},
{
MethodName: "RunTestCase",
Handler: _Runner_RunTestCase_Handler,
@ -667,14 +831,38 @@ var Runner_ServiceDesc = grpc.ServiceDesc{
MethodName: "PopularHeaders",
Handler: _Runner_PopularHeaders_Handler,
},
{
MethodName: "GetSuggestedAPIs",
Handler: _Runner_GetSuggestedAPIs_Handler,
},
{
MethodName: "FunctionsQuery",
Handler: _Runner_FunctionsQuery_Handler,
},
{
MethodName: "GetVersion",
Handler: _Runner_GetVersion_Handler,
},
{
MethodName: "Sample",
Handler: _Runner_Sample_Handler,
},
{
MethodName: "GetStoreKinds",
Handler: _Runner_GetStoreKinds_Handler,
},
{
MethodName: "GetStores",
Handler: _Runner_GetStores_Handler,
},
{
MethodName: "CreateStore",
Handler: _Runner_CreateStore_Handler,
},
{
MethodName: "DeleteStore",
Handler: _Runner_DeleteStore_Handler,
},
{
MethodName: "VerifyStore",
Handler: _Runner_VerifyStore_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "pkg/server/server.proto",

View File

@ -73,13 +73,16 @@ func (l *fileLoader) ListTestSuite() (suites []TestSuite, err error) {
for l.HasMore() {
var data []byte
if data, err = l.Load(); err != nil {
var loadErr error
if data, loadErr = l.Load(); err != nil {
fmt.Println("failed to load data", loadErr)
continue
}
var testSuite *TestSuite
if testSuite, err = Parse(data); err != nil {
return
if testSuite, loadErr = Parse(data); loadErr != nil {
fmt.Println("failed to parse data", loadErr)
continue
}
suites = append(suites, *testSuite)
}

View File

@ -0,0 +1,53 @@
/**
MIT License
Copyright (c) 2023 Rick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package remote
import (
context "context"
"github.com/linuxsuren/api-testing/pkg/testing"
"google.golang.org/grpc/metadata"
)
func WithStoreContext(ctx context.Context, store *testing.Store) context.Context {
return metadata.NewOutgoingContext(ctx, metadata.New(store.ToMap()))
}
func GetStoreFromContext(ctx context.Context) (store *testing.Store) {
if md, ok := metadata.FromIncomingContext(ctx); ok {
store = MDToStore(md)
}
return
}
func MDToStore(md metadata.MD) *testing.Store {
data := make(map[string]string)
for key, val := range md {
data[key] = val[0]
}
store := testing.MapToStore(data)
return &store
}

View File

@ -0,0 +1,52 @@
/**
MIT License
Copyright (c) 2023 Rick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package remote
import (
context "context"
"testing"
atest "github.com/linuxsuren/api-testing/pkg/testing"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/metadata"
)
func TestWithStoreContext(t *testing.T) {
ctx := WithStoreContext(context.Background(), sampleStore)
md, ok := metadata.FromOutgoingContext(ctx)
assert.True(t, ok)
assert.Equal(t, sampleStore, MDToStore(md))
}
func TestGetStoreFromContext(t *testing.T) {
ctx := metadata.NewIncomingContext(context.Background(), metadata.New(sampleStore.ToMap()))
assert.Equal(t, sampleStore, GetStoreFromContext(ctx))
}
var sampleStore = &atest.Store{
Properties: make(map[string]string),
}

View File

@ -3,6 +3,7 @@ package remote
import (
"fmt"
server "github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing"
)
@ -27,7 +28,7 @@ func convertToGRPCTestSuite(suite *testing.TestSuite) (result *TestSuite) {
Name: suite.Name,
Api: suite.API,
Param: mapToPair(suite.Param),
Spec: &APISpec{
Spec: &server.APISpec{
Kind: suite.Spec.Kind,
Url: suite.Spec.URL,
},
@ -35,7 +36,7 @@ func convertToGRPCTestSuite(suite *testing.TestSuite) (result *TestSuite) {
return
}
func convertToNormalTestCase(testcase *TestCase) (result testing.TestCase) {
func convertToNormalTestCase(testcase *server.TestCase) (result testing.TestCase) {
result = testing.TestCase{
Name: testcase.Name,
}
@ -62,10 +63,10 @@ func convertToNormalTestCase(testcase *TestCase) (result testing.TestCase) {
return
}
func convertToGRPCTestCase(testcase testing.TestCase) (result *TestCase) {
result = &TestCase{
func convertToGRPCTestCase(testcase testing.TestCase) (result *server.TestCase) {
result = &server.TestCase{
Name: testcase.Name,
Request: &Request{
Request: &server.Request{
Api: testcase.Request.API,
Method: testcase.Request.Method,
Body: testcase.Request.Body,
@ -73,7 +74,7 @@ func convertToGRPCTestCase(testcase testing.TestCase) (result *TestCase) {
Query: mapToPair(testcase.Request.Query),
Form: mapToPair(testcase.Request.Form),
},
Response: &Response{
Response: &server.Response{
Body: testcase.Expect.Body,
StatusCode: int32(testcase.Expect.StatusCode),
Schema: testcase.Expect.Schema,
@ -85,10 +86,10 @@ func convertToGRPCTestCase(testcase testing.TestCase) (result *TestCase) {
return
}
func mapToPair(data map[string]string) (pairs []*Pair) {
pairs = make([]*Pair, 0)
func mapToPair(data map[string]string) (pairs []*server.Pair) {
pairs = make([]*server.Pair, 0)
for k, v := range data {
pairs = append(pairs, &Pair{
pairs = append(pairs, &server.Pair{
Key: k,
Value: v,
})
@ -96,10 +97,10 @@ func mapToPair(data map[string]string) (pairs []*Pair) {
return
}
func mapInterToPair(data map[string]interface{}) (pairs []*Pair) {
pairs = make([]*Pair, 0)
func mapInterToPair(data map[string]interface{}) (pairs []*server.Pair) {
pairs = make([]*server.Pair, 0)
for k, v := range data {
pairs = append(pairs, &Pair{
pairs = append(pairs, &server.Pair{
Key: k,
Value: fmt.Sprintf("%v", v),
})
@ -107,7 +108,7 @@ func mapInterToPair(data map[string]interface{}) (pairs []*Pair) {
return
}
func pairToMap(pairs []*Pair) (data map[string]string) {
func pairToMap(pairs []*server.Pair) (data map[string]string) {
data = make(map[string]string)
for _, pair := range pairs {
data[pair.Key] = pair.Value
@ -115,7 +116,7 @@ func pairToMap(pairs []*Pair) (data map[string]string) {
return
}
func pairToInterMap(pairs []*Pair) (data map[string]interface{}) {
func pairToInterMap(pairs []*server.Pair) (data map[string]interface{}) {
data = make(map[string]interface{})
for _, pair := range pairs {
data[pair.Key] = pair.Value

View File

@ -3,6 +3,7 @@ package remote
import (
"testing"
server "github.com/linuxsuren/api-testing/pkg/server"
atest "github.com/linuxsuren/api-testing/pkg/testing"
"github.com/stretchr/testify/assert"
)
@ -23,7 +24,7 @@ func TestConvert(t *testing.T) {
},
}, convertToNormalTestSuite(&TestSuite{
Param: defaultPairs,
Spec: &APISpec{
Spec: &server.APISpec{
Url: "/v1",
Kind: "http",
},
@ -51,12 +52,12 @@ func TestConvert(t *testing.T) {
BodyFieldsExpect: defaultInterMap,
Header: map[string]string{},
},
}, convertToNormalTestCase(&TestCase{
Request: &Request{
}, convertToNormalTestCase(&server.TestCase{
Request: &server.Request{
Api: "/v1",
Header: defaultPairs,
},
Response: &Response{
Response: &server.Response{
BodyFieldsExpect: defaultPairs,
},
}))
@ -79,4 +80,4 @@ func TestConvert(t *testing.T) {
var defaultInterMap = map[string]interface{}{"foo": "bar"}
var defaultMap map[string]string = map[string]string{"foo": "bar"}
var defaultPairs []*Pair = []*Pair{{Key: "foo", Value: "bar"}}
var defaultPairs []*server.Pair = []*server.Pair{{Key: "foo", Value: "bar"}}

View File

@ -3,22 +3,30 @@ package remote
import (
context "context"
server "github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing"
"google.golang.org/grpc"
)
type gRPCLoader struct {
address string
client LoaderClient
store *testing.Store
client LoaderClient
ctx context.Context
}
// NewGRPCLoader creates a new gRPC loader
func NewGRPCLoader(address string) (writer testing.Writer, err error) {
func NewGRPCloaderFromStore() testing.StoreWriterFactory {
return &gRPCLoader{}
}
func (g *gRPCLoader) NewInstance(store testing.Store) (writer testing.Writer, err error) {
address := store.Kind.URL
var conn *grpc.ClientConn
if conn, err = grpc.Dial(address, grpc.WithInsecure()); err == nil {
writer = &gRPCLoader{
address: address,
client: NewLoaderClient(conn),
store: &store,
ctx: WithStoreContext(context.Background(), &store),
client: NewLoaderClient(conn),
}
}
return
@ -54,8 +62,8 @@ func (g *gRPCLoader) Reset() {
}
func (g *gRPCLoader) ListTestCase(suite string) (testcases []testing.TestCase, err error) {
var testCases *TestCases
testCases, err = g.client.ListTestCases(context.Background(), &TestSuite{
var testCases *server.TestCases
testCases, err = g.client.ListTestCases(g.ctx, &TestSuite{
Name: suite,
})
@ -70,8 +78,8 @@ func (g *gRPCLoader) ListTestCase(suite string) (testcases []testing.TestCase, e
return
}
func (g *gRPCLoader) GetTestCase(suite, name string) (testcase testing.TestCase, err error) {
var result *TestCase
result, err = g.client.GetTestCase(context.Background(), &TestCase{
var result *server.TestCase
result, err = g.client.GetTestCase(g.ctx, &server.TestCase{
Name: name,
SuiteName: suite,
})
@ -84,19 +92,19 @@ func (g *gRPCLoader) GetTestCase(suite, name string) (testcase testing.TestCase,
func (g *gRPCLoader) CreateTestCase(suite string, testcase testing.TestCase) (err error) {
payload := convertToGRPCTestCase(testcase)
payload.SuiteName = suite
_, err = g.client.CreateTestCase(context.Background(), payload)
_, err = g.client.CreateTestCase(g.ctx, payload)
return
}
func (g *gRPCLoader) UpdateTestCase(suite string, testcase testing.TestCase) (err error) {
payload := convertToGRPCTestCase(testcase)
payload.SuiteName = suite
_, err = g.client.UpdateTestCase(context.Background(), payload)
_, err = g.client.UpdateTestCase(g.ctx, payload)
return
}
func (g *gRPCLoader) DeleteTestCase(suite, testcase string) (err error) {
_, err = g.client.DeleteTestCase(context.Background(), &TestCase{
_, err = g.client.DeleteTestCase(g.ctx, &server.TestCase{
Name: testcase,
SuiteName: suite,
})
@ -105,7 +113,7 @@ func (g *gRPCLoader) DeleteTestCase(suite, testcase string) (err error) {
func (g *gRPCLoader) ListTestSuite() (suites []testing.TestSuite, err error) {
var items *TestSuites
items, err = g.client.ListTestSuite(context.Background(), &Empty{})
items, err = g.client.ListTestSuite(g.ctx, &server.Empty{})
if err == nil && items != nil {
for _, item := range items.Data {
suites = append(suites, *convertToNormalTestSuite(item))
@ -116,7 +124,7 @@ func (g *gRPCLoader) ListTestSuite() (suites []testing.TestSuite, err error) {
func (g *gRPCLoader) GetTestSuite(name string, full bool) (suite testing.TestSuite, err error) {
var result *TestSuite
if result, err = g.client.GetTestSuite(context.Background(),
if result, err = g.client.GetTestSuite(g.ctx,
&TestSuite{Name: name, Full: full}); err == nil {
suite = testing.TestSuite{
Name: result.Name,
@ -133,7 +141,7 @@ func (g *gRPCLoader) GetTestSuite(name string, full bool) (suite testing.TestSui
}
func (g *gRPCLoader) CreateSuite(name, api string) (err error) {
_, err = g.client.CreateTestSuite(context.Background(), &TestSuite{
_, err = g.client.CreateTestSuite(g.ctx, &TestSuite{
Name: name,
Api: api,
})
@ -142,7 +150,7 @@ func (g *gRPCLoader) CreateSuite(name, api string) (err error) {
func (g *gRPCLoader) GetSuite(name string) (reply *testing.TestSuite, _ string, err error) {
var suite *TestSuite
if suite, err = g.client.GetTestSuite(context.Background(),
if suite, err = g.client.GetTestSuite(g.ctx,
&TestSuite{Name: name}); err != nil {
return
}
@ -152,12 +160,12 @@ func (g *gRPCLoader) GetSuite(name string) (reply *testing.TestSuite, _ string,
}
func (g *gRPCLoader) UpdateSuite(suite testing.TestSuite) (err error) {
_, err = g.client.UpdateTestSuite(context.Background(), convertToGRPCTestSuite(&suite))
_, err = g.client.UpdateTestSuite(g.ctx, convertToGRPCTestSuite(&suite))
return
}
func (g *gRPCLoader) DeleteSuite(name string) (err error) {
_, err = g.client.DeleteTestSuite(context.Background(), &TestSuite{
_, err = g.client.DeleteTestSuite(g.ctx, &TestSuite{
Name: name,
})
return

View File

@ -3,18 +3,25 @@ package remote
import (
"testing"
atest "github.com/linuxsuren/api-testing/pkg/testing"
"github.com/stretchr/testify/assert"
)
func TestNewGRPCLoader(t *testing.T) {
factory := NewGRPCloaderFromStore()
t.Run("invalid address", func(t *testing.T) {
writer, err := NewGRPCLoader("")
writer, err := factory.NewInstance(atest.Store{})
assert.Error(t, err)
assert.Nil(t, writer)
})
t.Run("valid address", func(t *testing.T) {
writer, err := NewGRPCLoader("localhost:8907")
writer, err := factory.NewInstance(atest.Store{
Kind: atest.StoreKind{
URL: "localhost:7070",
},
})
assert.NoError(t, err)
assert.NotNil(t, writer)
@ -31,4 +38,8 @@ func TestNewGRPCLoader(t *testing.T) {
assert.Equal(t, 0, writer.GetCount())
writer.Reset()
})
t.Run("NewGRPCloaderFromStore", func(t *testing.T) {
assert.NotNil(t, NewGRPCloaderFromStore())
})
}

View File

@ -7,6 +7,7 @@
package remote
import (
server "github.com/linuxsuren/api-testing/pkg/server"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@ -72,12 +73,12 @@ type TestSuite struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Api string `protobuf:"bytes,2,opt,name=api,proto3" json:"api,omitempty"`
Param []*Pair `protobuf:"bytes,3,rep,name=param,proto3" json:"param,omitempty"`
Spec *APISpec `protobuf:"bytes,4,opt,name=spec,proto3" json:"spec,omitempty"`
Items []*TestCase `protobuf:"bytes,5,rep,name=items,proto3" json:"items,omitempty"`
Full bool `protobuf:"varint,6,opt,name=full,proto3" json:"full,omitempty"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Api string `protobuf:"bytes,2,opt,name=api,proto3" json:"api,omitempty"`
Param []*server.Pair `protobuf:"bytes,3,rep,name=param,proto3" json:"param,omitempty"`
Spec *server.APISpec `protobuf:"bytes,4,opt,name=spec,proto3" json:"spec,omitempty"`
Items []*server.TestCase `protobuf:"bytes,5,rep,name=items,proto3" json:"items,omitempty"`
Full bool `protobuf:"varint,6,opt,name=full,proto3" json:"full,omitempty"`
}
func (x *TestSuite) Reset() {
@ -126,21 +127,21 @@ func (x *TestSuite) GetApi() string {
return ""
}
func (x *TestSuite) GetParam() []*Pair {
func (x *TestSuite) GetParam() []*server.Pair {
if x != nil {
return x.Param
}
return nil
}
func (x *TestSuite) GetSpec() *APISpec {
func (x *TestSuite) GetSpec() *server.APISpec {
if x != nil {
return x.Spec
}
return nil
}
func (x *TestSuite) GetItems() []*TestCase {
func (x *TestSuite) GetItems() []*server.TestCase {
if x != nil {
return x.Items
}
@ -154,550 +155,68 @@ func (x *TestSuite) GetFull() bool {
return false
}
type APISpec struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"`
Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
}
func (x *APISpec) Reset() {
*x = APISpec{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *APISpec) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*APISpec) ProtoMessage() {}
func (x *APISpec) ProtoReflect() protoreflect.Message {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use APISpec.ProtoReflect.Descriptor instead.
func (*APISpec) Descriptor() ([]byte, []int) {
return file_pkg_testing_remote_loader_proto_rawDescGZIP(), []int{2}
}
func (x *APISpec) GetKind() string {
if x != nil {
return x.Kind
}
return ""
}
func (x *APISpec) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
type TestCases struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Data []*TestCase `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"`
}
func (x *TestCases) Reset() {
*x = TestCases{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TestCases) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TestCases) ProtoMessage() {}
func (x *TestCases) ProtoReflect() protoreflect.Message {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TestCases.ProtoReflect.Descriptor instead.
func (*TestCases) Descriptor() ([]byte, []int) {
return file_pkg_testing_remote_loader_proto_rawDescGZIP(), []int{3}
}
func (x *TestCases) GetData() []*TestCase {
if x != nil {
return x.Data
}
return nil
}
type TestCase struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
SuiteName string `protobuf:"bytes,2,opt,name=suiteName,proto3" json:"suiteName,omitempty"`
Request *Request `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"`
Response *Response `protobuf:"bytes,4,opt,name=response,proto3" json:"response,omitempty"`
}
func (x *TestCase) Reset() {
*x = TestCase{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TestCase) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TestCase) ProtoMessage() {}
func (x *TestCase) ProtoReflect() protoreflect.Message {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TestCase.ProtoReflect.Descriptor instead.
func (*TestCase) Descriptor() ([]byte, []int) {
return file_pkg_testing_remote_loader_proto_rawDescGZIP(), []int{4}
}
func (x *TestCase) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *TestCase) GetSuiteName() string {
if x != nil {
return x.SuiteName
}
return ""
}
func (x *TestCase) GetRequest() *Request {
if x != nil {
return x.Request
}
return nil
}
func (x *TestCase) GetResponse() *Response {
if x != nil {
return x.Response
}
return nil
}
type Request struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Api string `protobuf:"bytes,1,opt,name=api,proto3" json:"api,omitempty"`
Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"`
Header []*Pair `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty"`
Query []*Pair `protobuf:"bytes,4,rep,name=query,proto3" json:"query,omitempty"`
Form []*Pair `protobuf:"bytes,5,rep,name=form,proto3" json:"form,omitempty"`
Body string `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"`
}
func (x *Request) Reset() {
*x = Request{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Request) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Request) ProtoMessage() {}
func (x *Request) ProtoReflect() protoreflect.Message {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Request.ProtoReflect.Descriptor instead.
func (*Request) Descriptor() ([]byte, []int) {
return file_pkg_testing_remote_loader_proto_rawDescGZIP(), []int{5}
}
func (x *Request) GetApi() string {
if x != nil {
return x.Api
}
return ""
}
func (x *Request) GetMethod() string {
if x != nil {
return x.Method
}
return ""
}
func (x *Request) GetHeader() []*Pair {
if x != nil {
return x.Header
}
return nil
}
func (x *Request) GetQuery() []*Pair {
if x != nil {
return x.Query
}
return nil
}
func (x *Request) GetForm() []*Pair {
if x != nil {
return x.Form
}
return nil
}
func (x *Request) GetBody() string {
if x != nil {
return x.Body
}
return ""
}
type Response struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
StatusCode int32 `protobuf:"varint,1,opt,name=statusCode,proto3" json:"statusCode,omitempty"`
Body string `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
Header []*Pair `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty"`
BodyFieldsExpect []*Pair `protobuf:"bytes,4,rep,name=bodyFieldsExpect,proto3" json:"bodyFieldsExpect,omitempty"`
Verify []string `protobuf:"bytes,5,rep,name=verify,proto3" json:"verify,omitempty"`
Schema string `protobuf:"bytes,6,opt,name=schema,proto3" json:"schema,omitempty"`
}
func (x *Response) Reset() {
*x = Response{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Response) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Response) ProtoMessage() {}
func (x *Response) ProtoReflect() protoreflect.Message {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Response.ProtoReflect.Descriptor instead.
func (*Response) Descriptor() ([]byte, []int) {
return file_pkg_testing_remote_loader_proto_rawDescGZIP(), []int{6}
}
func (x *Response) GetStatusCode() int32 {
if x != nil {
return x.StatusCode
}
return 0
}
func (x *Response) GetBody() string {
if x != nil {
return x.Body
}
return ""
}
func (x *Response) GetHeader() []*Pair {
if x != nil {
return x.Header
}
return nil
}
func (x *Response) GetBodyFieldsExpect() []*Pair {
if x != nil {
return x.BodyFieldsExpect
}
return nil
}
func (x *Response) GetVerify() []string {
if x != nil {
return x.Verify
}
return nil
}
func (x *Response) GetSchema() string {
if x != nil {
return x.Schema
}
return ""
}
type Pair struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *Pair) Reset() {
*x = Pair{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Pair) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Pair) ProtoMessage() {}
func (x *Pair) ProtoReflect() protoreflect.Message {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Pair.ProtoReflect.Descriptor instead.
func (*Pair) Descriptor() ([]byte, []int) {
return file_pkg_testing_remote_loader_proto_rawDescGZIP(), []int{7}
}
func (x *Pair) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *Pair) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
type Empty struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *Empty) Reset() {
*x = Empty{}
if protoimpl.UnsafeEnabled {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Empty) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_pkg_testing_remote_loader_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) {
return file_pkg_testing_remote_loader_proto_rawDescGZIP(), []int{8}
}
var File_pkg_testing_remote_loader_proto protoreflect.FileDescriptor
var file_pkg_testing_remote_loader_proto_rawDesc = []byte{
0x0a, 0x1f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x72, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x2f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x22, 0x33, 0x0a, 0x0a, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54,
0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xb6,
0x01, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61,
0x70, 0x69, 0x12, 0x22, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x03, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52,
0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x12, 0x23, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x04,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x41, 0x50,
0x49, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x26, 0x0a, 0x05, 0x69,
0x74, 0x65, 0x6d, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x65, 0x6d,
0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x52, 0x05, 0x69, 0x74,
0x65, 0x6d, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28,
0x08, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x6c, 0x22, 0x2f, 0x0a, 0x07, 0x41, 0x50, 0x49, 0x53, 0x70,
0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x31, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73,
0x74, 0x43, 0x61, 0x73, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x95, 0x01, 0x0a, 0x08,
0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09,
0x73, 0x75, 0x69, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x09, 0x73, 0x75, 0x69, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x72, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0xb3, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70,
0x69, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x24, 0x0a, 0x06, 0x68, 0x65, 0x61,
0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x65, 0x6d, 0x6f,
0x74, 0x65, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12,
0x22, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c,
0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x71, 0x75,
0x65, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52,
0x04, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x06, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xce, 0x01, 0x0a, 0x08, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74,
0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x24, 0x0a, 0x06, 0x68, 0x65,
0x61, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x65, 0x6d,
0x6f, 0x74, 0x65, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
0x12, 0x38, 0x0a, 0x10, 0x62, 0x6f, 0x64, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x78,
0x70, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x72, 0x65, 0x6d,
0x6f, 0x74, 0x65, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x10, 0x62, 0x6f, 0x64, 0x79, 0x46, 0x69,
0x65, 0x6c, 0x64, 0x73, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65,
0x72, 0x69, 0x66, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x65, 0x72, 0x69,
0x66, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x2e, 0x0a, 0x04, 0x50, 0x61,
0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x32, 0xaf, 0x04, 0x0a, 0x06, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x34,
0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12,
0x0d, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12,
0x6f, 0x12, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x1a, 0x17, 0x70, 0x6b, 0x67, 0x2f, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x22, 0x33, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73,
0x12, 0x25, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11,
0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74,
0x65, 0x73, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65,
0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x0d, 0x2e, 0x72, 0x65, 0x6d,
0x6f, 0x74, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x47,
0x65, 0x74, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x72, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x11,
0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xb6, 0x01, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74,
0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x70, 0x69, 0x12, 0x22, 0x0a, 0x05, 0x70,
0x61, 0x72, 0x61, 0x6d, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x73, 0x65, 0x72,
0x76, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x12,
0x23, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04,
0x73, 0x70, 0x65, 0x63, 0x12, 0x26, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x05, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73,
0x74, 0x43, 0x61, 0x73, 0x65, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x12, 0x0a, 0x04,
0x66, 0x75, 0x6c, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x6c,
0x32, 0xaf, 0x04, 0x0a, 0x06, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0d, 0x4c,
0x69, 0x73, 0x74, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x0d, 0x2e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x72, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x22,
0x00, 0x12, 0x35, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53,
0x75, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65,
0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54,
0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74,
0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x11, 0x2e, 0x72, 0x65,
0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x22, 0x00,
0x12, 0x39, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75,
0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0f, 0x44,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x11,
0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74,
0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f,
0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x22, 0x00, 0x12, 0x35,
0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74,
0x65, 0x12, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x53,
0x75, 0x69, 0x74, 0x65, 0x1a, 0x0d, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x73,
0x74, 0x43, 0x61, 0x73, 0x65, 0x73, 0x12, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f,
0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x73, 0x22, 0x00, 0x12, 0x33,
0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65,
0x12, 0x10, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61,
0x73, 0x65, 0x1a, 0x0d, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61,
0x73, 0x65, 0x12, 0x10, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x1a, 0x10, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65,
0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x10, 0x2e, 0x72, 0x65, 0x6d,
0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x1a, 0x10, 0x2e, 0x72,
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x22, 0x00,
0x12, 0x33, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61,
0x73, 0x65, 0x12, 0x10, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x43, 0x61, 0x73, 0x65, 0x1a, 0x0d, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x45, 0x6d,
0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61,
0x70, 0x69, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74,
0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x22, 0x00, 0x12, 0x37, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61,
0x73, 0x65, 0x73, 0x12, 0x11, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x54, 0x65, 0x73,
0x74, 0x53, 0x75, 0x69, 0x74, 0x65, 0x1a, 0x11, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x73, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x0e, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x10, 0x2e,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x1a,
0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00,
0x12, 0x33, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12,
0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73,
0x65, 0x1a, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43,
0x61, 0x73, 0x65, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54,
0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x1a, 0x10, 0x2e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a,
0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73, 0x65, 0x12,
0x10, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x73,
0x65, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x22, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x73, 0x75, 0x72, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2d,
0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74,
0x69, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
@ -712,56 +231,46 @@ func file_pkg_testing_remote_loader_proto_rawDescGZIP() []byte {
return file_pkg_testing_remote_loader_proto_rawDescData
}
var file_pkg_testing_remote_loader_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_pkg_testing_remote_loader_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_pkg_testing_remote_loader_proto_goTypes = []interface{}{
(*TestSuites)(nil), // 0: remote.TestSuites
(*TestSuite)(nil), // 1: remote.TestSuite
(*APISpec)(nil), // 2: remote.APISpec
(*TestCases)(nil), // 3: remote.TestCases
(*TestCase)(nil), // 4: remote.TestCase
(*Request)(nil), // 5: remote.Request
(*Response)(nil), // 6: remote.Response
(*Pair)(nil), // 7: remote.Pair
(*Empty)(nil), // 8: remote.Empty
(*TestSuites)(nil), // 0: remote.TestSuites
(*TestSuite)(nil), // 1: remote.TestSuite
(*server.Pair)(nil), // 2: server.Pair
(*server.APISpec)(nil), // 3: server.APISpec
(*server.TestCase)(nil), // 4: server.TestCase
(*server.Empty)(nil), // 5: server.Empty
(*server.TestCases)(nil), // 6: server.TestCases
}
var file_pkg_testing_remote_loader_proto_depIdxs = []int32{
1, // 0: remote.TestSuites.data:type_name -> remote.TestSuite
7, // 1: remote.TestSuite.param:type_name -> remote.Pair
2, // 2: remote.TestSuite.spec:type_name -> remote.APISpec
4, // 3: remote.TestSuite.items:type_name -> remote.TestCase
4, // 4: remote.TestCases.data:type_name -> remote.TestCase
5, // 5: remote.TestCase.request:type_name -> remote.Request
6, // 6: remote.TestCase.response:type_name -> remote.Response
7, // 7: remote.Request.header:type_name -> remote.Pair
7, // 8: remote.Request.query:type_name -> remote.Pair
7, // 9: remote.Request.form:type_name -> remote.Pair
7, // 10: remote.Response.header:type_name -> remote.Pair
7, // 11: remote.Response.bodyFieldsExpect:type_name -> remote.Pair
8, // 12: remote.Loader.ListTestSuite:input_type -> remote.Empty
1, // 13: remote.Loader.CreateTestSuite:input_type -> remote.TestSuite
1, // 14: remote.Loader.GetTestSuite:input_type -> remote.TestSuite
1, // 15: remote.Loader.UpdateTestSuite:input_type -> remote.TestSuite
1, // 16: remote.Loader.DeleteTestSuite:input_type -> remote.TestSuite
1, // 17: remote.Loader.ListTestCases:input_type -> remote.TestSuite
4, // 18: remote.Loader.CreateTestCase:input_type -> remote.TestCase
4, // 19: remote.Loader.GetTestCase:input_type -> remote.TestCase
4, // 20: remote.Loader.UpdateTestCase:input_type -> remote.TestCase
4, // 21: remote.Loader.DeleteTestCase:input_type -> remote.TestCase
0, // 22: remote.Loader.ListTestSuite:output_type -> remote.TestSuites
8, // 23: remote.Loader.CreateTestSuite:output_type -> remote.Empty
1, // 24: remote.Loader.GetTestSuite:output_type -> remote.TestSuite
1, // 25: remote.Loader.UpdateTestSuite:output_type -> remote.TestSuite
8, // 26: remote.Loader.DeleteTestSuite:output_type -> remote.Empty
3, // 27: remote.Loader.ListTestCases:output_type -> remote.TestCases
8, // 28: remote.Loader.CreateTestCase:output_type -> remote.Empty
4, // 29: remote.Loader.GetTestCase:output_type -> remote.TestCase
4, // 30: remote.Loader.UpdateTestCase:output_type -> remote.TestCase
8, // 31: remote.Loader.DeleteTestCase:output_type -> remote.Empty
22, // [22:32] is the sub-list for method output_type
12, // [12:22] is the sub-list for method input_type
12, // [12:12] is the sub-list for extension type_name
12, // [12:12] is the sub-list for extension extendee
0, // [0:12] is the sub-list for field type_name
2, // 1: remote.TestSuite.param:type_name -> server.Pair
3, // 2: remote.TestSuite.spec:type_name -> server.APISpec
4, // 3: remote.TestSuite.items:type_name -> server.TestCase
5, // 4: remote.Loader.ListTestSuite:input_type -> server.Empty
1, // 5: remote.Loader.CreateTestSuite:input_type -> remote.TestSuite
1, // 6: remote.Loader.GetTestSuite:input_type -> remote.TestSuite
1, // 7: remote.Loader.UpdateTestSuite:input_type -> remote.TestSuite
1, // 8: remote.Loader.DeleteTestSuite:input_type -> remote.TestSuite
1, // 9: remote.Loader.ListTestCases:input_type -> remote.TestSuite
4, // 10: remote.Loader.CreateTestCase:input_type -> server.TestCase
4, // 11: remote.Loader.GetTestCase:input_type -> server.TestCase
4, // 12: remote.Loader.UpdateTestCase:input_type -> server.TestCase
4, // 13: remote.Loader.DeleteTestCase:input_type -> server.TestCase
0, // 14: remote.Loader.ListTestSuite:output_type -> remote.TestSuites
5, // 15: remote.Loader.CreateTestSuite:output_type -> server.Empty
1, // 16: remote.Loader.GetTestSuite:output_type -> remote.TestSuite
1, // 17: remote.Loader.UpdateTestSuite:output_type -> remote.TestSuite
5, // 18: remote.Loader.DeleteTestSuite:output_type -> server.Empty
6, // 19: remote.Loader.ListTestCases:output_type -> server.TestCases
5, // 20: remote.Loader.CreateTestCase:output_type -> server.Empty
4, // 21: remote.Loader.GetTestCase:output_type -> server.TestCase
4, // 22: remote.Loader.UpdateTestCase:output_type -> server.TestCase
5, // 23: remote.Loader.DeleteTestCase:output_type -> server.Empty
14, // [14:24] is the sub-list for method output_type
4, // [4:14] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_pkg_testing_remote_loader_proto_init() }
@ -794,90 +303,6 @@ func file_pkg_testing_remote_loader_proto_init() {
return nil
}
}
file_pkg_testing_remote_loader_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*APISpec); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_testing_remote_loader_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TestCases); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_testing_remote_loader_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TestCase); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_testing_remote_loader_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Request); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_testing_remote_loader_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Response); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_testing_remote_loader_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Pair); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pkg_testing_remote_loader_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@ -885,7 +310,7 @@ func file_pkg_testing_remote_loader_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pkg_testing_remote_loader_proto_rawDesc,
NumEnums: 0,
NumMessages: 9,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -4,18 +4,20 @@ option go_package = "github.com/linuxsuren/api-testing/pkg/testing/remote";
package remote;
import "pkg/server/server.proto";
service Loader {
rpc ListTestSuite(Empty) returns (TestSuites) {}
rpc CreateTestSuite(TestSuite) returns (Empty) {}
rpc ListTestSuite(server.Empty) returns (TestSuites) {}
rpc CreateTestSuite(TestSuite) returns (server.Empty) {}
rpc GetTestSuite(TestSuite) returns (TestSuite) {}
rpc UpdateTestSuite(TestSuite) returns (TestSuite) {}
rpc DeleteTestSuite(TestSuite) returns (Empty) {}
rpc DeleteTestSuite(TestSuite) returns (server.Empty) {}
rpc ListTestCases(TestSuite) returns (TestCases) {}
rpc CreateTestCase(TestCase) returns (Empty) {}
rpc GetTestCase(TestCase) returns (TestCase) {}
rpc UpdateTestCase(TestCase) returns (TestCase) {}
rpc DeleteTestCase(TestCase) returns (Empty) {}
rpc ListTestCases(TestSuite) returns (server.TestCases) {}
rpc CreateTestCase(server.TestCase) returns (server.Empty) {}
rpc GetTestCase(server.TestCase) returns (server.TestCase) {}
rpc UpdateTestCase(server.TestCase) returns (server.TestCase) {}
rpc DeleteTestCase(server.TestCase) returns (server.Empty) {}
}
message TestSuites {
@ -25,50 +27,8 @@ message TestSuites {
message TestSuite {
string name = 1;
string api = 2;
repeated Pair param = 3;
APISpec spec = 4;
repeated TestCase items = 5;
repeated server.Pair param = 3;
server.APISpec spec = 4;
repeated server.TestCase items = 5;
bool full = 6;
}
message APISpec {
string kind = 1;
string url = 2;
}
message TestCases {
repeated TestCase data = 1;
}
message TestCase {
string name = 1;
string suiteName = 2;
Request request = 3;
Response response = 4;
}
message Request {
string api = 1;
string method = 2;
repeated Pair header = 3;
repeated Pair query = 4;
repeated Pair form = 5;
string body = 6;
}
message Response {
int32 statusCode = 1;
string body = 2;
repeated Pair header = 3;
repeated Pair bodyFieldsExpect = 4;
repeated string verify = 5;
string schema = 6;
}
message Pair {
string key = 1;
string value = 2;
}
message Empty {
}

View File

@ -8,6 +8,7 @@ package remote
import (
context "context"
server "github.com/linuxsuren/api-testing/pkg/server"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
@ -22,16 +23,16 @@ const _ = grpc.SupportPackageIsVersion7
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type LoaderClient interface {
ListTestSuite(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TestSuites, error)
CreateTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*Empty, error)
ListTestSuite(ctx context.Context, in *server.Empty, opts ...grpc.CallOption) (*TestSuites, error)
CreateTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*server.Empty, error)
GetTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*TestSuite, error)
UpdateTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*TestSuite, error)
DeleteTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*Empty, error)
ListTestCases(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*TestCases, error)
CreateTestCase(ctx context.Context, in *TestCase, opts ...grpc.CallOption) (*Empty, error)
GetTestCase(ctx context.Context, in *TestCase, opts ...grpc.CallOption) (*TestCase, error)
UpdateTestCase(ctx context.Context, in *TestCase, opts ...grpc.CallOption) (*TestCase, error)
DeleteTestCase(ctx context.Context, in *TestCase, opts ...grpc.CallOption) (*Empty, error)
DeleteTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*server.Empty, error)
ListTestCases(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*server.TestCases, error)
CreateTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.Empty, error)
GetTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.TestCase, error)
UpdateTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.TestCase, error)
DeleteTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.Empty, error)
}
type loaderClient struct {
@ -42,7 +43,7 @@ func NewLoaderClient(cc grpc.ClientConnInterface) LoaderClient {
return &loaderClient{cc}
}
func (c *loaderClient) ListTestSuite(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TestSuites, error) {
func (c *loaderClient) ListTestSuite(ctx context.Context, in *server.Empty, opts ...grpc.CallOption) (*TestSuites, error) {
out := new(TestSuites)
err := c.cc.Invoke(ctx, "/remote.Loader/ListTestSuite", in, out, opts...)
if err != nil {
@ -51,8 +52,8 @@ func (c *loaderClient) ListTestSuite(ctx context.Context, in *Empty, opts ...grp
return out, nil
}
func (c *loaderClient) CreateTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
func (c *loaderClient) CreateTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*server.Empty, error) {
out := new(server.Empty)
err := c.cc.Invoke(ctx, "/remote.Loader/CreateTestSuite", in, out, opts...)
if err != nil {
return nil, err
@ -78,8 +79,8 @@ func (c *loaderClient) UpdateTestSuite(ctx context.Context, in *TestSuite, opts
return out, nil
}
func (c *loaderClient) DeleteTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
func (c *loaderClient) DeleteTestSuite(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*server.Empty, error) {
out := new(server.Empty)
err := c.cc.Invoke(ctx, "/remote.Loader/DeleteTestSuite", in, out, opts...)
if err != nil {
return nil, err
@ -87,8 +88,8 @@ func (c *loaderClient) DeleteTestSuite(ctx context.Context, in *TestSuite, opts
return out, nil
}
func (c *loaderClient) ListTestCases(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*TestCases, error) {
out := new(TestCases)
func (c *loaderClient) ListTestCases(ctx context.Context, in *TestSuite, opts ...grpc.CallOption) (*server.TestCases, error) {
out := new(server.TestCases)
err := c.cc.Invoke(ctx, "/remote.Loader/ListTestCases", in, out, opts...)
if err != nil {
return nil, err
@ -96,8 +97,8 @@ func (c *loaderClient) ListTestCases(ctx context.Context, in *TestSuite, opts ..
return out, nil
}
func (c *loaderClient) CreateTestCase(ctx context.Context, in *TestCase, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
func (c *loaderClient) CreateTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.Empty, error) {
out := new(server.Empty)
err := c.cc.Invoke(ctx, "/remote.Loader/CreateTestCase", in, out, opts...)
if err != nil {
return nil, err
@ -105,8 +106,8 @@ func (c *loaderClient) CreateTestCase(ctx context.Context, in *TestCase, opts ..
return out, nil
}
func (c *loaderClient) GetTestCase(ctx context.Context, in *TestCase, opts ...grpc.CallOption) (*TestCase, error) {
out := new(TestCase)
func (c *loaderClient) GetTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.TestCase, error) {
out := new(server.TestCase)
err := c.cc.Invoke(ctx, "/remote.Loader/GetTestCase", in, out, opts...)
if err != nil {
return nil, err
@ -114,8 +115,8 @@ func (c *loaderClient) GetTestCase(ctx context.Context, in *TestCase, opts ...gr
return out, nil
}
func (c *loaderClient) UpdateTestCase(ctx context.Context, in *TestCase, opts ...grpc.CallOption) (*TestCase, error) {
out := new(TestCase)
func (c *loaderClient) UpdateTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.TestCase, error) {
out := new(server.TestCase)
err := c.cc.Invoke(ctx, "/remote.Loader/UpdateTestCase", in, out, opts...)
if err != nil {
return nil, err
@ -123,8 +124,8 @@ func (c *loaderClient) UpdateTestCase(ctx context.Context, in *TestCase, opts ..
return out, nil
}
func (c *loaderClient) DeleteTestCase(ctx context.Context, in *TestCase, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
func (c *loaderClient) DeleteTestCase(ctx context.Context, in *server.TestCase, opts ...grpc.CallOption) (*server.Empty, error) {
out := new(server.Empty)
err := c.cc.Invoke(ctx, "/remote.Loader/DeleteTestCase", in, out, opts...)
if err != nil {
return nil, err
@ -136,16 +137,16 @@ func (c *loaderClient) DeleteTestCase(ctx context.Context, in *TestCase, opts ..
// All implementations must embed UnimplementedLoaderServer
// for forward compatibility
type LoaderServer interface {
ListTestSuite(context.Context, *Empty) (*TestSuites, error)
CreateTestSuite(context.Context, *TestSuite) (*Empty, error)
ListTestSuite(context.Context, *server.Empty) (*TestSuites, error)
CreateTestSuite(context.Context, *TestSuite) (*server.Empty, error)
GetTestSuite(context.Context, *TestSuite) (*TestSuite, error)
UpdateTestSuite(context.Context, *TestSuite) (*TestSuite, error)
DeleteTestSuite(context.Context, *TestSuite) (*Empty, error)
ListTestCases(context.Context, *TestSuite) (*TestCases, error)
CreateTestCase(context.Context, *TestCase) (*Empty, error)
GetTestCase(context.Context, *TestCase) (*TestCase, error)
UpdateTestCase(context.Context, *TestCase) (*TestCase, error)
DeleteTestCase(context.Context, *TestCase) (*Empty, error)
DeleteTestSuite(context.Context, *TestSuite) (*server.Empty, error)
ListTestCases(context.Context, *TestSuite) (*server.TestCases, error)
CreateTestCase(context.Context, *server.TestCase) (*server.Empty, error)
GetTestCase(context.Context, *server.TestCase) (*server.TestCase, error)
UpdateTestCase(context.Context, *server.TestCase) (*server.TestCase, error)
DeleteTestCase(context.Context, *server.TestCase) (*server.Empty, error)
mustEmbedUnimplementedLoaderServer()
}
@ -153,10 +154,10 @@ type LoaderServer interface {
type UnimplementedLoaderServer struct {
}
func (UnimplementedLoaderServer) ListTestSuite(context.Context, *Empty) (*TestSuites, error) {
func (UnimplementedLoaderServer) ListTestSuite(context.Context, *server.Empty) (*TestSuites, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTestSuite not implemented")
}
func (UnimplementedLoaderServer) CreateTestSuite(context.Context, *TestSuite) (*Empty, error) {
func (UnimplementedLoaderServer) CreateTestSuite(context.Context, *TestSuite) (*server.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateTestSuite not implemented")
}
func (UnimplementedLoaderServer) GetTestSuite(context.Context, *TestSuite) (*TestSuite, error) {
@ -165,22 +166,22 @@ func (UnimplementedLoaderServer) GetTestSuite(context.Context, *TestSuite) (*Tes
func (UnimplementedLoaderServer) UpdateTestSuite(context.Context, *TestSuite) (*TestSuite, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateTestSuite not implemented")
}
func (UnimplementedLoaderServer) DeleteTestSuite(context.Context, *TestSuite) (*Empty, error) {
func (UnimplementedLoaderServer) DeleteTestSuite(context.Context, *TestSuite) (*server.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteTestSuite not implemented")
}
func (UnimplementedLoaderServer) ListTestCases(context.Context, *TestSuite) (*TestCases, error) {
func (UnimplementedLoaderServer) ListTestCases(context.Context, *TestSuite) (*server.TestCases, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTestCases not implemented")
}
func (UnimplementedLoaderServer) CreateTestCase(context.Context, *TestCase) (*Empty, error) {
func (UnimplementedLoaderServer) CreateTestCase(context.Context, *server.TestCase) (*server.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateTestCase not implemented")
}
func (UnimplementedLoaderServer) GetTestCase(context.Context, *TestCase) (*TestCase, error) {
func (UnimplementedLoaderServer) GetTestCase(context.Context, *server.TestCase) (*server.TestCase, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetTestCase not implemented")
}
func (UnimplementedLoaderServer) UpdateTestCase(context.Context, *TestCase) (*TestCase, error) {
func (UnimplementedLoaderServer) UpdateTestCase(context.Context, *server.TestCase) (*server.TestCase, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateTestCase not implemented")
}
func (UnimplementedLoaderServer) DeleteTestCase(context.Context, *TestCase) (*Empty, error) {
func (UnimplementedLoaderServer) DeleteTestCase(context.Context, *server.TestCase) (*server.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteTestCase not implemented")
}
func (UnimplementedLoaderServer) mustEmbedUnimplementedLoaderServer() {}
@ -197,7 +198,7 @@ func RegisterLoaderServer(s grpc.ServiceRegistrar, srv LoaderServer) {
}
func _Loader_ListTestSuite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
in := new(server.Empty)
if err := dec(in); err != nil {
return nil, err
}
@ -209,7 +210,7 @@ func _Loader_ListTestSuite_Handler(srv interface{}, ctx context.Context, dec fun
FullMethod: "/remote.Loader/ListTestSuite",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LoaderServer).ListTestSuite(ctx, req.(*Empty))
return srv.(LoaderServer).ListTestSuite(ctx, req.(*server.Empty))
}
return interceptor(ctx, in, info, handler)
}
@ -305,7 +306,7 @@ func _Loader_ListTestCases_Handler(srv interface{}, ctx context.Context, dec fun
}
func _Loader_CreateTestCase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestCase)
in := new(server.TestCase)
if err := dec(in); err != nil {
return nil, err
}
@ -317,13 +318,13 @@ func _Loader_CreateTestCase_Handler(srv interface{}, ctx context.Context, dec fu
FullMethod: "/remote.Loader/CreateTestCase",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LoaderServer).CreateTestCase(ctx, req.(*TestCase))
return srv.(LoaderServer).CreateTestCase(ctx, req.(*server.TestCase))
}
return interceptor(ctx, in, info, handler)
}
func _Loader_GetTestCase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestCase)
in := new(server.TestCase)
if err := dec(in); err != nil {
return nil, err
}
@ -335,13 +336,13 @@ func _Loader_GetTestCase_Handler(srv interface{}, ctx context.Context, dec func(
FullMethod: "/remote.Loader/GetTestCase",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LoaderServer).GetTestCase(ctx, req.(*TestCase))
return srv.(LoaderServer).GetTestCase(ctx, req.(*server.TestCase))
}
return interceptor(ctx, in, info, handler)
}
func _Loader_UpdateTestCase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestCase)
in := new(server.TestCase)
if err := dec(in); err != nil {
return nil, err
}
@ -353,13 +354,13 @@ func _Loader_UpdateTestCase_Handler(srv interface{}, ctx context.Context, dec fu
FullMethod: "/remote.Loader/UpdateTestCase",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LoaderServer).UpdateTestCase(ctx, req.(*TestCase))
return srv.(LoaderServer).UpdateTestCase(ctx, req.(*server.TestCase))
}
return interceptor(ctx, in, info, handler)
}
func _Loader_DeleteTestCase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestCase)
in := new(server.TestCase)
if err := dec(in); err != nil {
return nil, err
}
@ -371,7 +372,7 @@ func _Loader_DeleteTestCase_Handler(srv interface{}, ctx context.Context, dec fu
FullMethod: "/remote.Loader/DeleteTestCase",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LoaderServer).DeleteTestCase(ctx, req.(*TestCase))
return srv.(LoaderServer).DeleteTestCase(ctx, req.(*server.TestCase))
}
return interceptor(ctx, in, info, handler)
}

145
pkg/testing/store.go Normal file
View File

@ -0,0 +1,145 @@
/**
MIT License
Copyright (c) 2023 Rick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package testing
import (
"os"
"path"
"strings"
"gopkg.in/yaml.v3"
)
type Store struct {
Name string
Kind StoreKind
Description string
URL string
Username string
Password string
Properties map[string]string
}
func (s *Store) ToMap() (result map[string]string) {
result = map[string]string{
"name": s.Name,
"kind": s.Kind.Name,
"kind.url": s.Kind.URL,
"description": s.Description,
"url": s.URL,
"username": s.Username,
"password": s.Password,
}
for key, val := range s.Properties {
result["pro."+key] = val
}
return
}
func MapToStore(data map[string]string) (store Store) {
store = Store{
Name: data["name"],
Description: data["description"],
URL: data["url"],
Username: data["username"],
Password: data["password"],
Kind: StoreKind{
Name: data["kind"],
URL: data["kind.url"],
},
Properties: make(map[string]string),
}
for key, val := range data {
if strings.HasPrefix(key, "pro.") {
store.Properties[strings.TrimPrefix(key, "pro.")] = val
}
}
return
}
// StoreKind represents a gRPC-based store
type StoreKind struct {
Name string
URL string
}
type StoreGetterAndSetter interface {
GetStores() (stores []Store, err error)
GetStore(name string) (store *Store, err error)
DeleteStore(name string) (err error)
UpdateStore(store Store) (err error)
GetStoreKinds() (kinds []StoreKind, err error)
}
type StoreWriterFactory interface {
NewInstance(store Store) (writer Writer, err error)
}
type storeFactory struct {
configDir string
}
func NewStoreFactory(configDir string) StoreGetterAndSetter {
return &storeFactory{
configDir: configDir,
}
}
func (s *storeFactory) GetStores() (stores []Store, err error) {
var data []byte
if data, err = os.ReadFile(path.Join(s.configDir, "stores.yaml")); err == nil {
err = yaml.Unmarshal(data, &stores)
} else {
err = nil
}
stores = append(stores, Store{Name: "local"})
return
}
func (s *storeFactory) GetStore(name string) (store *Store, err error) {
var stores []Store
if stores, err = s.GetStores(); err == nil {
for i := range stores {
item := stores[i]
if item.Name == name {
store = &item
}
}
}
return
}
func (s *storeFactory) DeleteStore(name string) (err error) {
return
}
func (s *storeFactory) UpdateStore(store Store) (err error) {
return
}
func (s *storeFactory) GetStoreKinds() (kinds []StoreKind, err error) {
return
}

128
pkg/testing/store_test.go Normal file
View File

@ -0,0 +1,128 @@
/**
MIT License
Copyright (c) 2023 Rick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package testing
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestStoreConvert(t *testing.T) {
t.Run("ToMap", func(t *testing.T) {
store := sampleStore
assert.Equal(t, sampleStoreMap, store.ToMap())
})
t.Run("MapToStore", func(t *testing.T) {
store := MapToStore(sampleStoreMap)
assert.Equal(t, sampleStore, &store)
})
t.Run("NewStoreFactory", func(t *testing.T) {
assert.NotNil(t, NewStoreFactory(""))
})
}
func TestStoreFactory(t *testing.T) {
factory := NewStoreFactory("testdata")
assert.NotNil(t, factory)
t.Run("GetStoreKinds", func(t *testing.T) {
_, err := factory.GetStoreKinds()
assert.NoError(t, err)
})
t.Run("GetStore", func(t *testing.T) {
store, err := factory.GetStore("db")
assert.Nil(t, err)
assert.Equal(t, &Store{
Name: "db",
Kind: StoreKind{
Name: "database",
URL: "localhost:7071",
},
URL: "localhost:4000",
Username: "root",
Properties: map[string]string{
"database": "test",
},
}, store)
})
t.Run("GetAllStores", func(t *testing.T) {
stores, err := factory.GetStores()
assert.Nil(t, err)
assert.Equal(t, 2, len(stores))
assert.Equal(t, "local", stores[1].Name)
})
t.Run("DeleteStore", func(t *testing.T) {
err := factory.DeleteStore("")
assert.NoError(t, err)
})
t.Run("UpdateStore", func(t *testing.T) {
err := factory.UpdateStore(Store{})
assert.NoError(t, err)
})
t.Run("no stores.yaml found", func(t *testing.T) {
factory := NewStoreFactory("testdata-fake")
stores, err := factory.GetStores()
assert.NoError(t, err)
assert.Equal(t, []Store{{
Name: "local",
}}, stores)
})
}
var sampleStoreMap = map[string]string{
"name": "test",
"url": fooURL,
"kind.url": fooURL,
"kind": "test",
"description": "desc",
"username": "user",
"password": "pass",
"pro.key": "val",
}
var sampleStore = &Store{
Name: "test",
Kind: StoreKind{
Name: "test",
URL: fooURL,
},
URL: fooURL,
Description: "desc",
Username: "user",
Password: "pass",
Properties: map[string]string{
"key": "val",
},
}
const fooURL = "http://foo"

8
pkg/testing/testdata/stores.yaml vendored Normal file
View File

@ -0,0 +1,8 @@
- name: db
kind:
name: database
url: localhost:7071
url: localhost:4000
username: root
properties:
database: test