feat: add ui for the api-testing (#102)

* feat: add ui project for the atest

* add grpc gateway support

* complete the very basic ui of atest

* add local storage flag into the server sub-command

* add unit tests for the remote server

* put the console into the docker image

* remove unused files from console

* add more unit tests

* support to execute test case on ui

* fix the code smells located by sonarqube

---------

Co-authored-by: rick <linuxsuren@users.noreply.github.com>
This commit is contained in:
Rick 2023-06-26 08:47:24 +08:00 committed by GitHub
parent 7ff05a862e
commit 02d7e23cb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 17208 additions and 128 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ dist/
.vscode/launch.json
sample.yaml
.DS_Store
console/atest-ui/node_modules

View File

@ -4,6 +4,7 @@ WORKDIR /workspace
COPY cmd/ cmd/
COPY pkg/ pkg/
COPY extensions/ extensions/
COPY console/atest-ui atest-ui/
COPY sample/ sample/
COPY go.mod go.mod
COPY go.sum go.sum
@ -17,6 +18,13 @@ RUN go mod download
RUN CGO_ENABLE=0 go build -ldflags "-w -s" -o atest .
RUN CGO_ENABLE=0 go build -ldflags "-w -s" -o atest-collector extensions/collector/main.go
FROM node:20-alpine3.17 AS ui
WORKDIR /workspace
COPY --from=builder /workspace/atest-ui .
RUN npm install --ignore-scripts
RUN npm run build-only
FROM ubuntu:23.04
LABEL "com.github.actions.name"="API testing"
@ -35,4 +43,8 @@ COPY --from=builder /workspace/atest-collector /usr/local/bin/atest-collector
COPY --from=builder /workspace/LICENSE /LICENSE
COPY --from=builder /workspace/README.md /README.md
CMD ["atest", "server"]
RUN mkdir -p /var/www
COPY --from=builder /workspace/sample /var/www/sample
COPY --from=ui /workspace/dist /var/www/html
CMD ["atest", "server", "--console-path=/var/www/html", "--local-storage=/var/www/sample/testsuite-*.yaml"]

View File

@ -7,7 +7,7 @@ goreleaser:
build-image:
docker build -t ghcr.io/linuxsuren/api-testing:dev .
run-image:
docker run ghcr.io/linuxsuren/api-testing:dev
docker run -p 7070:7070 -p 8080:8080 ghcr.io/linuxsuren/api-testing:dev
copy: build
sudo cp bin/atest /usr/local/bin/
copy-restart: build
@ -24,13 +24,25 @@ grpc:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pkg/server/server.proto
grpc-gw:
protoc -I . --grpc-gateway_out . \
--grpc-gateway_opt logtostderr=true \
--grpc-gateway_opt paths=source_relative \
--grpc-gateway_opt generate_unbound_methods=true \
pkg/server/server.proto
grpc-js:
protoc -I=pkg/server server.proto \
--js_out=import_style=commonjs:bin \
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:bin
# https://github.com/grpc/grpc-web
grpc-ts:
protoc -I=pkg/server server.proto \
--js_out=import_style=commonjs,binary:console/atest-ui/src \
--grpc-web_out=import_style=typescript,mode=grpcwebtext:console/atest-ui/src
grpc-java:
protoc --plugin=protoc-gen-grpc-java=/usr/local/bin/protoc-gen-grpc-java \
--grpc-java_out=bin --proto_path=pkg/server server.proto
install-tool:
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
hd i protoc-gen-grpc-web

View File

@ -15,7 +15,7 @@ This is a API testing tool.
* Validate the response body with [JSON schema](https://json-schema.org/)
* Pre and post handle with the API request
* Output reference between TestCase
* Run in server mode, and provide the [gRPC endpoint](pkg/server/server.proto)
* Run in server mode, and provide the [gRPC](pkg/server/server.proto) and HTTP endpoint
* [VS Code extension](https://github.com/LinuxSuRen/vscode-api-testing) support
* [HTTP API record](extensions/collector)
@ -66,9 +66,9 @@ consume: 1m2.153686448s
## Use in Docker
Use `atest` as server mode in Docker:
Use `atest` as server mode in Docker, then you could visit the UI from `8080` and the gRPC endpoint from `7070`:
```
docker run -p 7070:7070 ghcr.io/linuxsuren/api-testing
docker run -p 7070:7070 -p 8080:8080 ghcr.io/linuxsuren/api-testing
```
Use `atest-collector` in Docker:

View File

@ -5,6 +5,7 @@ import (
"testing"
"github.com/linuxsuren/api-testing/cmd"
"github.com/linuxsuren/api-testing/pkg/server"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/stretchr/testify/assert"
)
@ -35,7 +36,8 @@ func TestCreateFunctionCommand(t *testing.T) {
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cmd.NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, cmd.NewFakeGRPCServer())
c := cmd.NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"},
cmd.NewFakeGRPCServer(), server.NewFakeHTTPServer())
buf := new(bytes.Buffer)
c.SetOut(buf)

View File

@ -6,12 +6,14 @@ import (
"testing"
"github.com/linuxsuren/api-testing/cmd"
"github.com/linuxsuren/api-testing/pkg/server"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/stretchr/testify/assert"
)
func TestJSONSchemaCmd(t *testing.T) {
c := cmd.NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, cmd.NewFakeGRPCServer())
c := cmd.NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"},
cmd.NewFakeGRPCServer(), server.NewFakeHTTPServer())
buf := new(bytes.Buffer)
c.SetOut(buf)

View File

@ -3,13 +3,15 @@ package cmd
import (
"os"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/version"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/spf13/cobra"
)
// NewRootCmd creates the root command
func NewRootCmd(execer fakeruntime.Execer, gRPCServer gRPCServer) (c *cobra.Command) {
func NewRootCmd(execer fakeruntime.Execer, gRPCServer gRPCServer,
httpServer server.HTTPServer) (c *cobra.Command) {
c = &cobra.Command{
Use: "atest",
Short: "API testing tool",
@ -18,7 +20,7 @@ func NewRootCmd(execer fakeruntime.Execer, gRPCServer gRPCServer) (c *cobra.Comm
c.Version = version.GetVersion()
c.AddCommand(createInitCommand(execer),
createRunCommand(), createSampleCmd(),
createServerCmd(gRPCServer), createJSONSchemaCmd(),
createServerCmd(gRPCServer, httpServer), createJSONSchemaCmd(),
createServiceCommand(execer), createFunctionCmd())
return
}

View File

@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/linuxsuren/api-testing/pkg/server"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
)
@ -15,18 +16,18 @@ func TestCreateRunCommand(t *testing.T) {
init := createInitCommand(fakeruntime.FakeExecer{})
assert.Equal(t, "init", init.Use)
server := createServerCmd(&fakeGRPCServer{})
assert.NotNil(t, server)
assert.Equal(t, "server", server.Use)
s := createServerCmd(&fakeGRPCServer{}, server.NewFakeHTTPServer())
assert.NotNil(t, s)
assert.Equal(t, "server", s.Use)
root := NewRootCmd(fakeruntime.FakeExecer{}, NewFakeGRPCServer())
root := NewRootCmd(fakeruntime.FakeExecer{}, NewFakeGRPCServer(), server.NewFakeHTTPServer())
root.SetArgs([]string{"init", "-k=demo.yaml", "--wait-namespace", "demo", "--wait-resource", "demo"})
err := root.Execute()
assert.Nil(t, err)
}
func TestRootCmd(t *testing.T) {
c := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, NewFakeGRPCServer())
c := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, NewFakeGRPCServer(), server.NewFakeHTTPServer())
assert.NotNil(t, c)
assert.Equal(t, "atest", c.Use)
}

View File

@ -5,13 +5,15 @@ import (
"testing"
"github.com/linuxsuren/api-testing/cmd"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/sample"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/stretchr/testify/assert"
)
func TestSampleCmd(t *testing.T) {
c := cmd.NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, cmd.NewFakeGRPCServer())
c := cmd.NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"},
cmd.NewFakeGRPCServer(), server.NewFakeHTTPServer())
buf := new(bytes.Buffer)
c.SetOut(buf)

View File

@ -5,14 +5,21 @@ import (
"fmt"
"log"
"net"
"net/http"
"path"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/linuxsuren/api-testing/pkg/testing"
"github.com/spf13/cobra"
"google.golang.org/grpc"
)
func createServerCmd(gRPCServer gRPCServer) (c *cobra.Command) {
opt := &serverOption{gRPCServer: gRPCServer}
func createServerCmd(gRPCServer gRPCServer, httpServer server.HTTPServer) (c *cobra.Command) {
opt := &serverOption{
gRPCServer: gRPCServer,
httpServer: httpServer,
}
c = &cobra.Command{
Use: "server",
Short: "Run as a server mode",
@ -20,14 +27,22 @@ func createServerCmd(gRPCServer gRPCServer) (c *cobra.Command) {
}
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.localStorage, "local-storage", "", "", "The local storage path")
flags.StringVarP(&opt.consolePath, "console-path", "", "", "The path of the console")
return
}
type serverOption struct {
gRPCServer gRPCServer
port int
printProto bool
httpServer server.HTTPServer
port int
httpPort int
printProto bool
localStorage string
consolePath string
}
func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
@ -38,19 +53,56 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
return
}
var lis net.Listener
var (
lis net.Listener
httplis net.Listener
)
lis, err = net.Listen("tcp", fmt.Sprintf(":%d", o.port))
if err != nil {
return
}
httplis, err = net.Listen("tcp", fmt.Sprintf(":%d", o.httpPort))
if err != nil {
return
}
loader := testing.NewFileLoader()
if o.localStorage != "" {
if err = loader.Put(o.localStorage); err != nil {
return
}
}
removeServer := server.NewRemoteServer(loader)
s := o.gRPCServer
server.RegisterRunnerServer(s, server.NewRemoteServer())
log.Printf("server listening at %v", lis.Addr())
s.Serve(lis)
go func() {
server.RegisterRunnerServer(s, removeServer)
log.Printf("gRPC server listening at %v", lis.Addr())
s.Serve(lis)
}()
mux := runtime.NewServeMux()
err = server.RegisterRunnerHandlerServer(cmd.Context(), mux, removeServer)
if err == nil {
mux.HandlePath("GET", "/", frontEndHandlerWithLocation(o.consolePath))
mux.HandlePath("GET", "/assets/{asset}", frontEndHandlerWithLocation(o.consolePath))
o.httpServer.WithHandler(mux)
err = o.httpServer.Serve(httplis)
}
return
}
func frontEndHandlerWithLocation(consolePath string) func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
return func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
target := r.URL.Path
if target == "/" {
target = "/index.html"
}
http.ServeFile(w, r, path.Join(consolePath, target))
}
}
type gRPCServer interface {
Serve(lis net.Listener) error
grpc.ServiceRegistrar

View File

@ -2,9 +2,11 @@ package cmd
import (
"bytes"
"net/http"
"strings"
"testing"
"github.com/linuxsuren/api-testing/pkg/server"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/stretchr/testify/assert"
)
@ -29,7 +31,7 @@ func TestPrintProto(t *testing.T) {
},
}, {
name: "random port",
args: []string{"server", "-p=0"},
args: []string{"server", "-p=0", "--http-port=0", "--local-storage=./*"},
verify: func(t *testing.T, buf *bytes.Buffer, err error) {
assert.Nil(t, err)
},
@ -37,7 +39,8 @@ func TestPrintProto(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
buf := new(bytes.Buffer)
root := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, &fakeGRPCServer{})
root := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"},
&fakeGRPCServer{}, server.NewFakeHTTPServer())
root.SetOut(buf)
root.SetArgs(tt.args)
err := root.Execute()
@ -45,3 +48,27 @@ func TestPrintProto(t *testing.T) {
})
}
}
func TestFrontEndHandlerWithLocation(t *testing.T) {
handler := frontEndHandlerWithLocation("testdata")
req, err := http.NewRequest("GET", "/", nil)
assert.NoError(t, err)
buf := new(bytes.Buffer)
handler(&fakeResponseWriter{buf: buf}, req, map[string]string{})
assert.Equal(t, "404 page not found\n", buf.String())
}
type fakeResponseWriter struct {
buf *bytes.Buffer
}
func (w *fakeResponseWriter) Header() http.Header {
return make(http.Header)
}
func (w *fakeResponseWriter) Write(data []byte) (int, error) {
return w.buf.Write(data)
}
func (w *fakeResponseWriter) WriteHeader(int) {
// do nothing due to this is a fake response writer
}

View File

@ -5,18 +5,19 @@ import (
"os"
"testing"
"github.com/linuxsuren/api-testing/pkg/server"
fakeruntime "github.com/linuxsuren/go-fake-runtime"
"github.com/stretchr/testify/assert"
)
func TestService(t *testing.T) {
root := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, NewFakeGRPCServer())
root := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "linux"}, NewFakeGRPCServer(), server.NewFakeHTTPServer())
root.SetArgs([]string{"service", "fake"})
root.SetOut(new(bytes.Buffer))
err := root.Execute()
assert.NotNil(t, err)
notLinux := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "fake"}, NewFakeGRPCServer())
notLinux := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: "fake"}, NewFakeGRPCServer(), server.NewFakeHTTPServer())
notLinux.SetArgs([]string{"service", paramAction, "install"})
notLinux.SetOut(new(bytes.Buffer))
err = notLinux.Execute()
@ -87,7 +88,8 @@ func TestService(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
buf := new(bytes.Buffer)
normalRoot := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: tt.targetOS, ExpectOutput: tt.expectOutput}, NewFakeGRPCServer())
normalRoot := NewRootCmd(fakeruntime.FakeExecer{ExpectOS: tt.targetOS, ExpectOutput: tt.expectOutput},
NewFakeGRPCServer(), server.NewFakeHTTPServer())
normalRoot.SetOut(buf)
normalRoot.SetArgs([]string{"service", "--action", tt.action, "--script-path", tmpFile.Name()})
err = normalRoot.Execute()

View File

@ -0,0 +1,25 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting'
],
overrides: [
{
files: [
'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}'
],
'extends': [
'plugin:cypress/recommended'
]
}
],
parserOptions: {
ecmaVersion: 'latest'
}
}

28
console/atest-ui/.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}

View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

View File

@ -0,0 +1,72 @@
# atest-ui
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
npm run test:unit
```
### Run End-to-End Tests with [Cypress](https://www.cypress.io/)
```sh
npm run test:e2e:dev
```
This runs the end-to-end tests against the Vite development server.
It is much faster than the production build.
But it's still recommended to test the production build with `test:e2e` before deploying (e.g. in CI environments):
```sh
npm run build
npm run test:e2e
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```
```sh
npm install -g cnpm --registry=https://registry.npmmirror.com
```

View File

@ -0,0 +1,8 @@
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}',
baseUrl: 'http://localhost:4173'
}
})

View File

@ -0,0 +1,8 @@
// https://on.cypress.io/api
describe('My First Test', () => {
it('visits the app root url', () => {
cy.visit('/')
cy.contains('h1', 'You did it!')
})
})

View File

@ -0,0 +1,10 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["./**/*", "../support/**/*"],
"compilerOptions": {
"isolatedModules": false,
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
}
}

View File

@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -0,0 +1,39 @@
/// <reference types="cypress" />
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
export {}

View File

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

1
console/atest-ui/env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

3
console/atest-ui/gen.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
./node_modules/.bin/grpc_tools_node_protoc --plugin=protoc-gen-ts=. --ts_out=../../pkg/server -I ../../pkg/server ../../pkg/server/server.proto

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Testing</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

14269
console/atest-ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
{
"name": "-atest-ui",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "run-p type-check build-only",
"preview": "vite preview",
"test:unit": "vitest",
"test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'",
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"element-plus": "^2.3.7",
"vue": "^3.3.4",
"vue-router": "^4.2.2"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@tsconfig/node18": "^2.0.1",
"@types/dotenv": "^8.2.0",
"@types/google-protobuf": "^3.15.6",
"@types/jsdom": "^21.1.1",
"@types/node": "^18.16.18",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"@vue/eslint-config-prettier": "^7.1.0",
"@vue/eslint-config-typescript": "^11.0.3",
"@vue/test-utils": "^2.3.2",
"@vue/tsconfig": "^0.4.0",
"cypress": "^12.14.0",
"eslint": "^8.39.0",
"eslint-plugin-cypress": "^2.13.3",
"eslint-plugin-vue": "^9.11.0",
"grpc_tools_node_protoc_ts": "^5.3.3",
"grpc-tools": "^1.12.4",
"jsdom": "^22.1.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.8",
"start-server-and-test": "^2.0.0",
"typescript": "~5.0.4",
"vite": "^4.3.9",
"vitest": "^0.32.0",
"vue-tsc": "^1.6.5"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,139 @@
<script setup lang="ts">
import TestCase from './views/TestCase.vue'
import { ref } from 'vue'
import { ElTree } from "element-plus"
interface Tree {
id: string
label: string
parent: string
children?: Tree[]
}
const testCaseName = ref('')
const testSuite = ref('')
const handleNodeClick = (data: Tree) => {
testCaseName.value = data.label
testSuite.value = data.parent
}
const data = ref([])
const treeRef = ref<InstanceType<typeof ElTree>>()
const requestOptions = {
method: 'POST'
};
fetch('/server.Runner/GetSuites', requestOptions)
.then(response => response.json())
.then(d => {
data.value = []
Object.keys(d.data).map(k => {
console.log(d.data[k])
let suite = {
id: k,
label: k,
children: [],
}
d.data[k].data.forEach((item: any) => {
suite.children?.push({
id: item,
label: item,
parent: k,
})
})
data.value.push(suite)
})
// treeRef.value.updateKeyChildren('1', data[0].children)
});
</script>
<template>
<div class="common-layout">
<el-container>
<el-aside width="200px">
<el-tree :data="data" :props="defaultProps"
default-expand-all
ref="treeRef"
node-key="id"
@node-click="handleNodeClick" />
</el-aside>
<el-main>
<TestCase :suite="testSuite" :name="testCaseName"/>
</el-main>
</el-container>
</div>
</template>
<style scoped>
header {
line-height: 1.5;
max-height: 100vh;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
nav {
width: 100%;
font-size: 12px;
text-align: center;
margin-top: 2rem;
}
nav a.router-link-exact-active {
color: var(--color-text);
}
nav a.router-link-exact-active:hover {
background-color: transparent;
}
nav a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
}
nav a:first-of-type {
border: 0;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
nav {
text-align: left;
margin-left: -1rem;
font-size: 1rem;
padding: 1rem 0;
margin-top: 1rem;
}
}
.demo-tabs > .el-tabs__content {
padding: 32px;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
</style>

View File

@ -0,0 +1,73 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #fff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition: color 0.5s, background-color 0.5s;
line-height: 1.6;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@ -0,0 +1,22 @@
@import './base.css';
#app {
/* max-width: 1280px; */
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}

View File

@ -0,0 +1,17 @@
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
// import router from './router'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus, {
locale: zhCn,
})
// app.use(router)
app.mount('#app')

View File

@ -0,0 +1,202 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { TabsPaneContext } from 'element-plus'
const props = defineProps({
name: String,
suite: String,
})
const testResult = ref('')
function sendRequest() {
const name = props.name
const suite = props.suite
const requestOptions = {
method: 'POST',
body: JSON.stringify({
suite: suite,
testcase: name,
})
};
fetch('/server.Runner/RunTestCase', requestOptions)
.then(response => response.json())
.then(e => {
testResult.value = e.body
});
}
interface Pair{
key: string,
value: string
}
const emptyPair: Pair[] = []
const verifyList = ref('')
const requestBody = ref('')
const bodyFieldsExpect = ref('')
const headersData = ref(emptyPair)
watch(props, (p) => {
const name = p.name
const suite = p.suite
const requestOptions = {
method: 'POST',
body: JSON.stringify({
suite: suite,
testcase: name,
})
};
fetch('/server.Runner/GetTestCase', requestOptions)
.then(response => response.json())
.then(e => {
if (e.request.method === "") {
e.request.method = "GET"
}
value.value = e.request.method
input.value = e.request.api
requestBody.value = e.request.body
verifyList.value = e.response.verify
headersData.value = []
e.request.header.forEach(h => {
headersData.value.push({
key: h.key,
value: h.value
})
})
let items: Pair[] = []
e.response.bodyFieldsExpect.forEach(b => {
items.push({
key: b.key,
value: b.value
})
})
bodyFieldsExpect.value = items
});
})
const value = ref('')
const options = [
{
value: 'GET',
label: 'GET',
},
{
value: 'POST',
label: 'POST',
},
{
value: 'DELETE',
label: 'DELETE',
},
{
value: 'PUT',
label: 'PUT',
},
]
const activeName = ref('second')
const handleClick = (tab: TabsPaneContext, event: Event) => {
console.log(tab, event)
}
const input = ref('')
function change() {
let lastItem = headersData.value[headersData.value.length - 1]
if (lastItem.key !== '') {
headersData.value.push({
key: '',
value: ''
})
}
}
const radio1 = ref('1')
</script>
<template>
<div class="common-layout">
<el-container>
<el-header style="padding-left: 5px;">
<el-select v-model="value" class="m-2" placeholder="Method" size="large">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-input v-model="input" placeholder="API Address" style="width: 70%; margin-left: 5px; margin-right: 5px;"/>
<el-button type="primary" @click="sendRequest">Send</el-button>
</el-header>
<el-main>
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="Headers" name="second">
<el-table :data="headersData" style="width: 100%">
<el-table-column label="Key" width="180">
<template #default="scope">
<el-input v-model="scope.row.key" placeholder="Key" @change="change" />
</template>
</el-table-column>
<el-table-column label="Value">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-input v-model="scope.row.value" placeholder="Value" />
</div>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="Body" name="third">
<el-radio-group v-model="radio1">
<el-radio :label="1">none</el-radio>
<el-radio :label="2">form-data</el-radio>
<el-radio :label="3">raw</el-radio>
<el-radio :label="4">x-www-form-urlencoded</el-radio>
</el-radio-group>
<el-input v-model="requestBody" :autosize="{ minRows: 4, maxRows: 8 }" type="textarea"
placeholder="Please input" />
</el-tab-pane>
<el-tab-pane label="BodyFiledExpect" name="fourth">
<el-table :data="bodyFieldsExpect" style="width: 100%">
<el-table-column label="Key" width="180">
<template #default="scope">
<el-input v-model="scope.row.key" placeholder="Key" @change="change" />
</template>
</el-table-column>
<el-table-column label="Value">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-input v-model="scope.row.value" placeholder="Value" />
</div>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="Verify" name="fifth">
<div v-for="verify in verifyList" :key="verify">
<el-input :value="verify" placeholder="API Address" />
</div>
</el-tab-pane>
</el-tabs>
</el-main>
<el-footer>
<div>Test Result:</div>
<el-input
v-model="testResult"
:autosize="{ minRows: 4, maxRows: 6 }"
readonly="true"
type="textarea"
placeholder="Please input"
/>
</el-footer>
</el-container>
</div>
</template>

View File

@ -0,0 +1,12 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "vue.config.ts"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@ -0,0 +1,20 @@
{
"files": [],
"compilerOptions":{
"module":"commonjs",
"allowJs": true,
"target": "es6",
"esModuleInterop": true
},
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.vitest.json"
}
]
}

View File

@ -0,0 +1,15 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"module": "ESNext",
"types": ["node"]
}
}

View File

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.app.json",
"exclude": [],
"compilerOptions": {
"composite": true,
"lib": [],
"types": ["node", "jsdom"]
}
}

View File

@ -0,0 +1,26 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
vueJsx(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
proxy: {
'/server.Runner': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
},
},
},
})

View File

@ -0,0 +1,18 @@
import { fileURLToPath } from 'node:url'
import { mergeConfig } from 'vite'
import { configDefaults, defineConfig } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(
viteConfig,
defineConfig({
test: {
environment: 'jsdom',
exclude: [...configDefaults.exclude, 'e2e/*'],
root: fileURLToPath(new URL('./', import.meta.url)),
transformMode: {
web: [/\.[jt]sx$/]
}
}
})
)

18
go.mod
View File

@ -7,6 +7,7 @@ require (
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
github.com/antonmedv/expr v1.12.1
github.com/ghodss/yaml v1.0.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
github.com/linuxsuren/go-fake-runtime v0.0.0-20230426144714-1a7a0d160d3f
@ -15,24 +16,26 @@ require (
github.com/stretchr/testify v1.8.2
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/sync v0.1.0
google.golang.org/grpc v1.54.0
google.golang.org/protobuf v1.28.1
google.golang.org/grpc v1.55.0
google.golang.org/protobuf v1.30.0
)
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
@ -40,10 +43,11 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
golang.org/x/crypto v0.3.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
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/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
gopkg.in/yaml.v3 v3.0.1 // indirect
)

44
go.sum
View File

@ -9,19 +9,23 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
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/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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.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.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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=
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
@ -36,11 +40,12 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy770So=
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/linuxsuren/go-fake-runtime v0.0.0-20230426144714-1a7a0d160d3f h1:TfAzkLxq/agwMBbccTx/f/dlmFWIBLWRGCWjI4IOlK8=
github.com/linuxsuren/go-fake-runtime v0.0.0-20230426144714-1a7a0d160d3f/go.mod h1:zmh6J78hSnWZo68faMA2eKOdaEp8eFbERHi3ZB9xHCQ=
github.com/linuxsuren/unstructured v0.0.1 h1:ilUA8MUYbR6l9ebo/YPV2bKqlf62bzQursDSE+j00iU=
@ -53,6 +58,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/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=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@ -92,8 +99,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
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.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
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=
@ -104,8 +111,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@ -113,24 +120,27 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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-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=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e h1:Ao9GzfUMPH3zjVfzXG5rlWlk+Q8MXWKwWpwVQE1MXfw=
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.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=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v2 v2.2.2/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=

View File

@ -1 +1,9 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@ -4,13 +4,15 @@ import (
"os"
"github.com/linuxsuren/api-testing/cmd"
"github.com/linuxsuren/api-testing/pkg/server"
exec "github.com/linuxsuren/go-fake-runtime"
"google.golang.org/grpc"
)
func main() {
gRPCServer := grpc.NewServer()
c := cmd.NewRootCmd(exec.DefaultExecer{}, gRPCServer)
c := cmd.NewRootCmd(exec.DefaultExecer{}, gRPCServer,
server.NewDefaultHTTPServer())
if err := c.Execute(); err != nil {
os.Exit(1)
}

47
pkg/server/http.go Normal file
View File

@ -0,0 +1,47 @@
package server
import (
"net"
"net/http"
)
// HTTPServer is an interface for serving HTTP requests
type HTTPServer interface {
Serve(lis net.Listener) error
WithHandler(handler http.Handler)
}
type defaultHTTPServer struct {
handler http.Handler
}
// NewDefaultHTTPServer creates a default HTTP server
func NewDefaultHTTPServer() HTTPServer {
return &defaultHTTPServer{}
}
func (s *defaultHTTPServer) Serve(lis net.Listener) (err error) {
server := &http.Server{Handler: s.handler}
err = server.Serve(lis)
return
}
func (s *defaultHTTPServer) WithHandler(h http.Handler) {
s.handler = h
}
type fakeHandler struct{}
// NewFakeHTTPServer creates a fake HTTP server
func NewFakeHTTPServer() HTTPServer {
return &fakeHandler{}
}
func (s *fakeHandler) Serve(lis net.Listener) (err error) {
// do nothing due to this is a fake method
return
}
func (s *fakeHandler) WithHandler(h http.Handler) {
// do nothing due to this is a fake method
}

21
pkg/server/http_test.go Normal file
View File

@ -0,0 +1,21 @@
package server_test
import (
"net"
"testing"
"github.com/linuxsuren/api-testing/pkg/server"
"github.com/stretchr/testify/assert"
)
func TestHTTPServer(t *testing.T) {
lis, err := net.Listen("tcp", ":0")
assert.Nil(t, err)
fake := server.NewFakeHTTPServer()
fake.WithHandler(nil)
fake.Serve(lis)
defaultHTTPServer := server.NewDefaultHTTPServer()
defaultHTTPServer.WithHandler(nil)
}

View File

@ -14,15 +14,17 @@ import (
"github.com/linuxsuren/api-testing/pkg/testing"
"github.com/linuxsuren/api-testing/pkg/version"
"github.com/linuxsuren/api-testing/sample"
"gopkg.in/yaml.v3"
)
type server struct {
UnimplementedRunnerServer
loader testing.Loader
}
// NewRemoteServer creates a remote server instance
func NewRemoteServer() RunnerServer {
return &server{}
func NewRemoteServer(loader testing.Loader) RunnerServer {
return &server{loader: loader}
}
func withDefaultValue(old, defVal any) any {
@ -141,6 +143,154 @@ func (s *server) GetVersion(ctx context.Context, in *Empty) (reply *HelloReply,
return
}
func (s *server) GetSuites(ctx context.Context, in *Empty) (reply *Suites, err error) {
defer func() {
s.loader.Reset()
}()
reply = &Suites{
Data: make(map[string]*Items),
}
for s.loader.HasMore() {
var data []byte
if data, err = s.loader.Load(); err != nil {
continue
}
var testSuite *testing.TestSuite
if testSuite, err = testing.Parse(data); err != nil {
return
}
items := &Items{}
for _, item := range testSuite.Items {
items.Data = append(items.Data, item.Name)
}
reply.Data[testSuite.Name] = items
}
return
}
func (s *server) GetTestCase(ctx context.Context, in *TestCaseIdentity) (reply *TestCase, err error) {
defer func() {
s.loader.Reset()
}()
for s.loader.HasMore() {
var data []byte
if data, err = s.loader.Load(); err != nil {
continue
}
var testSuite *testing.TestSuite
if testSuite, err = testing.Parse(data); err != nil {
return
}
if testSuite.Name != in.Suite {
continue
}
for _, testCase := range testSuite.Items {
if testCase.Name != in.Testcase {
continue
}
req := &Request{
Api: testCase.Request.API,
Method: testCase.Request.Method,
Header: mapToPair(testCase.Request.Header),
Query: mapToPair(testCase.Request.Query),
Form: mapToPair(testCase.Request.Form),
Body: testCase.Request.Body,
}
resp := &Response{
StatusCode: int32(testCase.Expect.StatusCode),
Body: testCase.Expect.Body,
Header: mapToPair(testCase.Expect.Header),
BodyFieldsExpect: mapInterToPair(testCase.Expect.BodyFieldsExpect),
Verify: testCase.Expect.Verify,
Schema: testCase.Expect.Schema,
}
reply = &TestCase{
Name: testCase.Name,
Request: req,
Response: resp,
}
break
}
}
return
}
func (s *server) RunTestCase(ctx context.Context, in *TestCaseIdentity) (result *TestCaseResult, err error) {
defer func() {
s.loader.Reset()
}()
var targetTestSuite *testing.TestSuite
for s.loader.HasMore() {
var data []byte
if data, err = s.loader.Load(); err != nil {
continue
}
var testSuite *testing.TestSuite
if testSuite, err = testing.Parse(data); err != nil {
continue
}
if testSuite.Name == in.Suite {
targetTestSuite = testSuite
break
}
}
if targetTestSuite != nil {
var data []byte
if data, err = yaml.Marshal(targetTestSuite); err == nil {
task := &TestTask{
Kind: "testcaseInSuite",
Data: string(data),
CaseName: in.Testcase,
Level: "debug",
}
var reply *HelloReply
if reply, err = s.Run(ctx, task); err == nil {
result = &TestCaseResult{
Body: reply.Message,
}
}
}
}
return
}
func mapInterToPair(data map[string]interface{}) (pairs []*Pair) {
pairs = make([]*Pair, 0)
for k, v := range data {
pairs = append(pairs, &Pair{
Key: k,
Value: fmt.Sprintf("%v", v),
})
}
return
}
func mapToPair(data map[string]string) (pairs []*Pair) {
pairs = make([]*Pair, 0)
for k, v := range data {
pairs = append(pairs, &Pair{
Key: k,
Value: v,
})
}
return
}
// Sample returns a sample of the test task
func (s *server) Sample(ctx context.Context, in *Empty) (reply *HelloReply, err error) {
reply = &HelloReply{Message: sample.TestSuiteGitLab}

View File

@ -18,7 +18,9 @@ const (
)
func TestRemoteServer(t *testing.T) {
server := NewRemoteServer()
loader := atesting.NewFileLoader()
loader.Put("testdata/simple.yaml")
server := NewRemoteServer(loader)
_, err := server.Run(context.TODO(), &TestTask{
Kind: "fake",
})
@ -66,6 +68,42 @@ func TestRemoteServer(t *testing.T) {
ver, err = server.Sample(context.TODO(), &Empty{})
assert.Nil(t, err)
assert.Equal(t, sample.TestSuiteGitLab, ver.Message)
var suites *Suites
suites, err = server.GetSuites(context.TODO(), &Empty{})
assert.Nil(t, err)
assert.Equal(t, suites, &Suites{Data: map[string]*Items{
"simple": {
Data: []string{"get", "query"},
},
}})
var testCase *TestCase
testCase, err = server.GetTestCase(context.TODO(), &TestCaseIdentity{
Suite: "simple",
Testcase: "get",
})
assert.Nil(t, err)
assert.Equal(t, "get", testCase.Name)
assert.Equal(t, "http://foo", testCase.Request.Api)
}
func TestRunTestCase(t *testing.T) {
loader := atesting.NewFileLoader()
loader.Put("testdata/simple.yaml")
server := NewRemoteServer(loader)
defer gock.Clean()
gock.New(urlFoo).Get("/").MatchHeader("key", "value").
Reply(http.StatusOK).
BodyString(`{"message": "hello"}`)
result, err := server.RunTestCase(context.TODO(), &TestCaseIdentity{
Suite: "simple",
Testcase: "get",
})
assert.NoError(t, err)
assert.Contains(t, result.Body, "start to run: 'get'\nstart to send request to http://foo\nresponse body:")
}
func TestFindParentTestCases(t *testing.T) {
@ -195,6 +233,11 @@ func TestWithDefaultValue(t *testing.T) {
assert.Equal(t, withDefaultValue(map[string]string{"key": "val"}, map[string]string{"key": "value"}), map[string]string{"key": "val"})
}
func TestMapInterToPair(t *testing.T) {
assert.Equal(t, []*Pair{{Key: "key", Value: "val"}},
mapInterToPair(map[string]interface{}{"key": "val"}))
}
//go:embed testdata/simple.yaml
var simpleSuite string

File diff suppressed because it is too large Load Diff

596
pkg/server/server.pb.gw.go Normal file
View File

@ -0,0 +1,596 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: pkg/server/server.proto
/*
Package server is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package server
import (
"context"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = metadata.Join
func request_Runner_Run_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TestTask
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Run(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Runner_Run_0(ctx context.Context, marshaler runtime.Marshaler, server RunnerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TestTask
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Run(ctx, &protoReq)
return msg, metadata, err
}
func request_Runner_Sample_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq Empty
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Sample(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Runner_Sample_0(ctx context.Context, marshaler runtime.Marshaler, server RunnerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq Empty
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Sample(ctx, &protoReq)
return msg, metadata, err
}
func request_Runner_GetVersion_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq Empty
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetVersion(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Runner_GetVersion_0(ctx context.Context, marshaler runtime.Marshaler, server RunnerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq Empty
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetVersion(ctx, &protoReq)
return msg, metadata, err
}
func request_Runner_GetSuites_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq Empty
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetSuites(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Runner_GetSuites_0(ctx context.Context, marshaler runtime.Marshaler, server RunnerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq Empty
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetSuites(ctx, &protoReq)
return msg, metadata, err
}
func request_Runner_RunTestCase_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TestCaseIdentity
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.RunTestCase(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Runner_RunTestCase_0(ctx context.Context, marshaler runtime.Marshaler, server RunnerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TestCaseIdentity
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RunTestCase(ctx, &protoReq)
return msg, metadata, err
}
func request_Runner_GetTestCase_0(ctx context.Context, marshaler runtime.Marshaler, client RunnerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TestCaseIdentity
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetTestCase(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Runner_GetTestCase_0(ctx context.Context, marshaler runtime.Marshaler, server RunnerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq TestCaseIdentity
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetTestCase(ctx, &protoReq)
return msg, metadata, err
}
// RegisterRunnerHandlerServer registers the http handlers for service Runner to "mux".
// UnaryRPC :call RunnerServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterRunnerHandlerFromEndpoint instead.
func RegisterRunnerHandlerServer(ctx context.Context, mux *runtime.ServeMux, server RunnerServer) error {
mux.Handle("POST", pattern_Runner_Run_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/server.Runner/Run", runtime.WithHTTPPathPattern("/server.Runner/Run"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Runner_Run_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_Run_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_Sample_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/server.Runner/Sample", runtime.WithHTTPPathPattern("/server.Runner/Sample"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Runner_Sample_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_Sample_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_GetVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/server.Runner/GetVersion", runtime.WithHTTPPathPattern("/server.Runner/GetVersion"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Runner_GetVersion_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_GetVersion_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_GetSuites_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/server.Runner/GetSuites", runtime.WithHTTPPathPattern("/server.Runner/GetSuites"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Runner_GetSuites_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_GetSuites_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_RunTestCase_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/server.Runner/RunTestCase", runtime.WithHTTPPathPattern("/server.Runner/RunTestCase"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Runner_RunTestCase_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_RunTestCase_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_GetTestCase_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/server.Runner/GetTestCase", runtime.WithHTTPPathPattern("/server.Runner/GetTestCase"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Runner_GetTestCase_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_GetTestCase_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterRunnerHandlerFromEndpoint is same as RegisterRunnerHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterRunnerHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.DialContext(ctx, endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterRunnerHandler(ctx, mux, conn)
}
// RegisterRunnerHandler registers the http handlers for service Runner to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterRunnerHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterRunnerHandlerClient(ctx, mux, NewRunnerClient(conn))
}
// RegisterRunnerHandlerClient registers the http handlers for service Runner
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "RunnerClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "RunnerClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "RunnerClient" to call the correct interceptors.
func RegisterRunnerHandlerClient(ctx context.Context, mux *runtime.ServeMux, client RunnerClient) error {
mux.Handle("POST", pattern_Runner_Run_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/server.Runner/Run", runtime.WithHTTPPathPattern("/server.Runner/Run"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Runner_Run_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_Run_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_Sample_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/server.Runner/Sample", runtime.WithHTTPPathPattern("/server.Runner/Sample"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Runner_Sample_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_Sample_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_GetVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/server.Runner/GetVersion", runtime.WithHTTPPathPattern("/server.Runner/GetVersion"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Runner_GetVersion_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_GetVersion_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_GetSuites_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/server.Runner/GetSuites", runtime.WithHTTPPathPattern("/server.Runner/GetSuites"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Runner_GetSuites_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_GetSuites_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_RunTestCase_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/server.Runner/RunTestCase", runtime.WithHTTPPathPattern("/server.Runner/RunTestCase"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Runner_RunTestCase_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_RunTestCase_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Runner_GetTestCase_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/server.Runner/GetTestCase", runtime.WithHTTPPathPattern("/server.Runner/GetTestCase"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Runner_GetTestCase_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Runner_GetTestCase_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Runner_Run_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"server.Runner", "Run"}, ""))
pattern_Runner_Sample_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"server.Runner", "Sample"}, ""))
pattern_Runner_GetVersion_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"server.Runner", "GetVersion"}, ""))
pattern_Runner_GetSuites_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"server.Runner", "GetSuites"}, ""))
pattern_Runner_RunTestCase_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"server.Runner", "RunTestCase"}, ""))
pattern_Runner_GetTestCase_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"server.Runner", "GetTestCase"}, ""))
)
var (
forward_Runner_Run_0 = runtime.ForwardResponseMessage
forward_Runner_Sample_0 = runtime.ForwardResponseMessage
forward_Runner_GetVersion_0 = runtime.ForwardResponseMessage
forward_Runner_GetSuites_0 = runtime.ForwardResponseMessage
forward_Runner_RunTestCase_0 = runtime.ForwardResponseMessage
forward_Runner_GetTestCase_0 = runtime.ForwardResponseMessage
)

View File

@ -8,6 +8,22 @@ service Runner {
rpc Run (TestTask) returns (HelloReply) {}
rpc Sample(Empty) returns (HelloReply) {}
rpc GetVersion(Empty) returns (HelloReply) {}
rpc GetSuites(Empty) returns (Suites) {}
rpc RunTestCase(TestCaseIdentity) returns (TestCaseResult) {}
rpc GetTestCase(TestCaseIdentity) returns (TestCase) {}
}
message Suites {
map<string, Items> data = 1;
}
message Items {
repeated string data = 1;
}
message TestCaseIdentity {
string suite = 1;
string testcase = 2;
}
message TestTask {
@ -23,5 +39,46 @@ message HelloReply {
string error = 2;
}
message Suite {
string name = 1;
string api = 2;
repeated TestCase items = 3;
}
message TestCase {
string name = 1;
Request request = 2;
Response response = 3;
}
message Request {
string api = 1;
string method = 2;
repeated Pair header = 3;
repeated Pair query = 4;
repeated Pair form = 5;
string body = 6;
}
message TestCaseResult {
int32 statusCode = 1;
string body = 2;
repeated Pair header = 3;
}
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

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.12.4
// - protoc v4.22.2
// source: pkg/server/server.proto
package server
@ -25,6 +25,9 @@ type RunnerClient interface {
Run(ctx context.Context, in *TestTask, opts ...grpc.CallOption) (*HelloReply, 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)
RunTestCase(ctx context.Context, in *TestCaseIdentity, opts ...grpc.CallOption) (*TestCaseResult, error)
GetTestCase(ctx context.Context, in *TestCaseIdentity, opts ...grpc.CallOption) (*TestCase, error)
}
type runnerClient struct {
@ -62,6 +65,33 @@ func (c *runnerClient) GetVersion(ctx context.Context, in *Empty, opts ...grpc.C
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...)
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...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runnerClient) GetTestCase(ctx context.Context, in *TestCaseIdentity, opts ...grpc.CallOption) (*TestCase, error) {
out := new(TestCase)
err := c.cc.Invoke(ctx, "/server.Runner/GetTestCase", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RunnerServer is the server API for Runner service.
// All implementations must embed UnimplementedRunnerServer
// for forward compatibility
@ -69,6 +99,9 @@ type RunnerServer interface {
Run(context.Context, *TestTask) (*HelloReply, error)
Sample(context.Context, *Empty) (*HelloReply, error)
GetVersion(context.Context, *Empty) (*HelloReply, error)
GetSuites(context.Context, *Empty) (*Suites, error)
RunTestCase(context.Context, *TestCaseIdentity) (*TestCaseResult, error)
GetTestCase(context.Context, *TestCaseIdentity) (*TestCase, error)
mustEmbedUnimplementedRunnerServer()
}
@ -85,6 +118,15 @@ func (UnimplementedRunnerServer) Sample(context.Context, *Empty) (*HelloReply, e
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")
}
func (UnimplementedRunnerServer) RunTestCase(context.Context, *TestCaseIdentity) (*TestCaseResult, error) {
return nil, status.Errorf(codes.Unimplemented, "method RunTestCase not implemented")
}
func (UnimplementedRunnerServer) GetTestCase(context.Context, *TestCaseIdentity) (*TestCase, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetTestCase not implemented")
}
func (UnimplementedRunnerServer) mustEmbedUnimplementedRunnerServer() {}
// UnsafeRunnerServer may be embedded to opt out of forward compatibility for this service.
@ -152,6 +194,60 @@ func _Runner_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(i
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 {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).GetSuites(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/GetSuites",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).GetSuites(ctx, req.(*Empty))
}
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 {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).RunTestCase(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/RunTestCase",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).RunTestCase(ctx, req.(*TestCaseIdentity))
}
return interceptor(ctx, in, info, handler)
}
func _Runner_GetTestCase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestCaseIdentity)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RunnerServer).GetTestCase(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/server.Runner/GetTestCase",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RunnerServer).GetTestCase(ctx, req.(*TestCaseIdentity))
}
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)
@ -171,6 +267,18 @@ var Runner_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetVersion",
Handler: _Runner_GetVersion_Handler,
},
{
MethodName: "GetSuites",
Handler: _Runner_GetSuites_Handler,
},
{
MethodName: "RunTestCase",
Handler: _Runner_RunTestCase_Handler,
},
{
MethodName: "GetTestCase",
Handler: _Runner_GetTestCase_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "pkg/server/server.proto",

View File

@ -4,6 +4,8 @@ items:
- name: get
request:
api: http://foo
header:
key: value
- name: query
request:
api: /

View File

@ -7,4 +7,5 @@ type Loader interface {
Put(string) (err error)
GetContext() string
GetCount() int
Reset()
}

View File

@ -50,3 +50,8 @@ func (l *fileLoader) GetContext() string {
func (l *fileLoader) GetCount() int {
return len(l.paths)
}
// Reset resets the index
func (l *fileLoader) Reset() {
l.index = -1
}

View File

@ -17,7 +17,7 @@ func TestFileLoader(t *testing.T) {
items: []string{},
verify: func(t *testing.T, loader atest.Loader) {
assert.False(t, loader.HasMore())
assert.Empty(t, loader.GetCount())
assert.Equal(t, 0, loader.GetCount())
},
}, {
name: "brace expansion path",
@ -53,4 +53,6 @@ func defaultVerify(t *testing.T, loader atest.Loader) {
assert.Equal(t, "testdata", loader.GetContext())
assert.False(t, loader.HasMore())
loader.Reset()
assert.True(t, loader.HasMore())
}

View File

@ -62,6 +62,13 @@ func TestDuplicatedNames(t *testing.T) {
}
func TestRequestRender(t *testing.T) {
validMap := map[string]string{
"key": "{{.Name}}",
}
invalidMap := map[string]string{
"key": "{{.name}}",
}
tests := []struct {
name string
request *atest.Request
@ -136,12 +143,26 @@ func TestRequestRender(t *testing.T) {
},
ctx: atest.TestCase{},
hasErr: true,
}, {
name: "failed with header render",
request: &atest.Request{
Header: map[string]string{
"key": "{{.name}}",
},
},
ctx: atest.TestCase{},
hasErr: true,
}, {
name: "failed with form render",
request: &atest.Request{
Form: invalidMap,
},
ctx: atest.TestCase{},
hasErr: true,
}, {
name: "form render",
request: &atest.Request{
Form: map[string]string{
"key": "{{.Name}}",
},
Form: validMap,
},
ctx: atest.TestCase{Name: "linuxsuren"},
verify: func(t *testing.T, req *atest.Request) {
@ -151,9 +172,7 @@ func TestRequestRender(t *testing.T) {
}, {
name: "header render",
request: &atest.Request{
Header: map[string]string{
"key": "{{.Name}}",
},
Header: validMap,
},
ctx: atest.TestCase{Name: "linuxsuren"},
verify: func(t *testing.T, req *atest.Request) {