Skip to content

Commit a941923

Browse files
authored
feat: support to set the log level (#33)
1 parent 19a07a2 commit a941923

File tree

13 files changed

+327
-38
lines changed

13 files changed

+327
-38
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.17 as builder
1+
FROM golang:1.18 as builder
22

33
WORKDIR /workspace
44
COPY . .

cmd/run.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type runOption struct {
3333
reportWriter runner.ReportResultWriter
3434
report string
3535
reportIgnore bool
36+
level string
3637
}
3738

3839
func newDefaultRunOption() *runOption {
@@ -66,6 +67,7 @@ See also https://github.com/LinuxSuRen/api-testing/tree/master/sample`,
6667
flags := cmd.Flags()
6768
flags.StringVarP(&opt.pattern, "pattern", "p", "test-suite-*.yaml",
6869
"The file pattern which try to execute the test cases")
70+
flags.StringVarP(&opt.level, "level", "l", "info", "Set the output log level")
6971
flags.DurationVarP(&opt.duration, "duration", "", 0, "Running duration")
7072
flags.DurationVarP(&opt.requestTimeout, "request-timeout", "", time.Minute, "Timeout for per request")
7173
flags.BoolVarP(&opt.requestIgnoreError, "request-ignore-error", "", false, "Indicate if ignore the request error")

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/linuxsuren/api-testing
22

3-
go 1.17
3+
go 1.18
44

55
require (
66
github.com/Masterminds/sprig/v3 v3.2.3

pkg/runner/simple.go

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,68 @@ import (
2222
unstructured "github.com/linuxsuren/unstructured/pkg"
2323
)
2424

25+
// LevelWriter represents a writer with level
26+
type LevelWriter interface {
27+
Info(format string, a ...any)
28+
Debug(format string, a ...any)
29+
}
30+
31+
// FormatPrinter represents a formart printer with level
32+
type FormatPrinter interface {
33+
Fprintf(w io.Writer, level, format string, a ...any) (n int, err error)
34+
}
35+
36+
type defaultLevelWriter struct {
37+
level int
38+
io.Writer
39+
FormatPrinter
40+
}
41+
42+
// NewDefaultLevelWriter creates a default LevelWriter instance
43+
func NewDefaultLevelWriter(level string, writer io.Writer) LevelWriter {
44+
result := &defaultLevelWriter{
45+
Writer: writer,
46+
}
47+
switch level {
48+
case "debug":
49+
result.level = 7
50+
case "info":
51+
result.level = 3
52+
}
53+
return result
54+
}
55+
56+
// Fprintf implements interface FormatPrinter
57+
func (w *defaultLevelWriter) Fprintf(writer io.Writer, level int, format string, a ...any) (n int, err error) {
58+
if level <= w.level {
59+
return fmt.Fprintf(writer, format, a...)
60+
}
61+
return
62+
}
63+
64+
// Info writes the info level message
65+
func (w *defaultLevelWriter) Info(format string, a ...any) {
66+
w.Fprintf(w.Writer, 3, format, a...)
67+
}
68+
69+
// Debug writes the debug level message
70+
func (w *defaultLevelWriter) Debug(format string, a ...any) {
71+
w.Fprintf(w.Writer, 7, format, a...)
72+
}
73+
74+
// TestCaseRunner represents a test case runner
2575
type TestCaseRunner interface {
2676
RunTestCase(testcase *testing.TestCase, dataContext interface{}, ctx context.Context) (output interface{}, err error)
2777
WithOutputWriter(io.Writer) TestCaseRunner
78+
WithWriteLevel(level string) TestCaseRunner
2879
WithTestReporter(TestReporter) TestCaseRunner
2980
}
3081

3182
// ReportRecord represents the raw data of a HTTP request
3283
type ReportRecord struct {
3384
Method string
3485
API string
86+
Body string
3587
BeginTime time.Time
3688
EndTime time.Time
3789
Error error
@@ -103,17 +155,20 @@ type TestReporter interface {
103155
type simpleTestCaseRunner struct {
104156
testReporter TestReporter
105157
writer io.Writer
158+
log LevelWriter
106159
}
107160

108161
// NewSimpleTestCaseRunner creates the instance of the simple test case runner
109162
func NewSimpleTestCaseRunner() TestCaseRunner {
110163
runner := &simpleTestCaseRunner{}
111-
return runner.WithOutputWriter(io.Discard).WithTestReporter(NewDiscardTestReporter())
164+
return runner.WithOutputWriter(io.Discard).
165+
WithWriteLevel("info").
166+
WithTestReporter(NewDiscardTestReporter())
112167
}
113168

114169
// RunTestCase is the main entry point of a test case
115170
func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataContext interface{}, ctx context.Context) (output interface{}, err error) {
116-
fmt.Fprintf(r.writer, "start to run: '%s'\n", testcase.Name)
171+
r.log.Info("start to run: '%s'\n", testcase.Name)
117172
record := NewReportRecord()
118173
defer func(rr *ReportRecord) {
119174
rr.EndTime = time.Now()
@@ -182,7 +237,7 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte
182237
request.Header.Add(key, val)
183238
}
184239

185-
fmt.Fprintf(r.writer, "start to send request to %s\n", testcase.Request.API)
240+
r.log.Info("start to send request to %s\n", testcase.Request.API)
186241

187242
// send the HTTP request
188243
var resp *http.Response
@@ -194,6 +249,8 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte
194249
if responseBodyData, err = io.ReadAll(resp.Body); err != nil {
195250
return
196251
}
252+
record.Body = string(responseBodyData)
253+
r.log.Debug("response body: %s\n", record.Body)
197254

198255
if err = testcase.Expect.Render(nil); err != nil {
199256
return
@@ -218,6 +275,7 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte
218275
}
219276
}
220277

278+
var bodyMap map[string]interface{}
221279
mapOutput := map[string]interface{}{}
222280
if err = json.Unmarshal(responseBodyData, &mapOutput); err != nil {
223281
switch b := err.(type) {
@@ -231,17 +289,22 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte
231289
return
232290
}
233291
output = arrayOutput
292+
mapOutput["data"] = arrayOutput
234293
default:
235294
return
236295
}
237296
} else {
297+
bodyMap = mapOutput
238298
output = mapOutput
299+
mapOutput = map[string]interface{}{
300+
"data": bodyMap,
301+
}
239302
}
240303

241304
for key, expectVal := range testcase.Expect.BodyFieldsExpect {
242305
var val interface{}
243306
var ok bool
244-
if val, ok, err = unstructured.NestedField(mapOutput, strings.Split(key, "/")...); err != nil {
307+
if val, ok, err = unstructured.NestedField(bodyMap, strings.Split(key, "/")...); err != nil {
245308
err = fmt.Errorf("failed to get field: %s, %v", key, err)
246309
return
247310
} else if !ok {
@@ -260,12 +323,12 @@ func (r *simpleTestCaseRunner) RunTestCase(testcase *testing.TestCase, dataConte
260323

261324
for _, verify := range testcase.Expect.Verify {
262325
var program *vm.Program
263-
if program, err = expr.Compile(verify, expr.Env(output), expr.AsBool()); err != nil {
326+
if program, err = expr.Compile(verify, expr.Env(mapOutput), expr.AsBool()); err != nil {
264327
return
265328
}
266329

267330
var result interface{}
268-
if result, err = expr.Run(program, output); err != nil {
331+
if result, err = expr.Run(program, mapOutput); err != nil {
269332
return
270333
}
271334

@@ -283,6 +346,14 @@ func (r *simpleTestCaseRunner) WithOutputWriter(writer io.Writer) TestCaseRunner
283346
return r
284347
}
285348

349+
// WithWriteLevel sets the level writer
350+
func (r *simpleTestCaseRunner) WithWriteLevel(level string) TestCaseRunner {
351+
if level != "" {
352+
r.log = NewDefaultLevelWriter(level, r.writer)
353+
}
354+
return r
355+
}
356+
286357
// WithTestReporter sets the TestReporter
287358
func (r *simpleTestCaseRunner) WithTestReporter(reporter TestReporter) TestCaseRunner {
288359
r.testReporter = reporter

pkg/runner/simple_test.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package runner
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
67
"net/http"
@@ -40,7 +41,7 @@ func TestTestCase(t *testing.T) {
4041
"type": "generic",
4142
},
4243
Verify: []string{
43-
`name == "linuxsuren"`,
44+
`data.name == "linuxsuren"`,
4445
},
4546
},
4647
},
@@ -242,7 +243,7 @@ func TestTestCase(t *testing.T) {
242243
},
243244
Expect: atest.Response{
244245
Verify: []string{
245-
"len(items) > 0",
246+
"len(data.items) > 0",
246247
},
247248
},
248249
},
@@ -372,5 +373,33 @@ func TestTestCase(t *testing.T) {
372373
}
373374
}
374375

376+
func TestLevelWriter(t *testing.T) {
377+
tests := []struct {
378+
name string
379+
buf *bytes.Buffer
380+
level string
381+
expect string
382+
}{{
383+
name: "debug",
384+
buf: new(bytes.Buffer),
385+
level: "debug",
386+
expect: "debuginfo",
387+
}, {
388+
name: "info",
389+
buf: new(bytes.Buffer),
390+
level: "info",
391+
expect: "info",
392+
}}
393+
for _, tt := range tests {
394+
writer := NewDefaultLevelWriter(tt.level, tt.buf)
395+
if assert.NotNil(t, writer) {
396+
writer.Debug("debug")
397+
writer.Info("info")
398+
399+
assert.Equal(t, tt.expect, tt.buf.String())
400+
}
401+
}
402+
}
403+
375404
//go:embed testdata/generic_response.json
376405
var genericBody string

pkg/server/fake_server.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package server
2+
3+
import (
4+
context "context"
5+
"log"
6+
"net"
7+
8+
grpc "google.golang.org/grpc"
9+
"google.golang.org/grpc/credentials/insecure"
10+
"google.golang.org/grpc/test/bufconn"
11+
)
12+
13+
type fakeServer struct {
14+
UnimplementedRunnerServer
15+
version string
16+
err error
17+
}
18+
19+
// NewServer creates a fake server
20+
func NewServer(version string, err error) RunnerServer {
21+
t := &fakeServer{
22+
version: version,
23+
err: err,
24+
}
25+
return t
26+
}
27+
28+
// Run runs the task
29+
func (s *fakeServer) Run(ctx context.Context, in *TestTask) (*HelloReply, error) {
30+
return &HelloReply{}, s.err
31+
}
32+
33+
// GetVersion returns the version
34+
func (s *fakeServer) GetVersion(ctx context.Context, in *Empty) (reply *HelloReply, err error) {
35+
reply = &HelloReply{
36+
Message: s.version,
37+
}
38+
err = s.err
39+
return
40+
}
41+
42+
// NewFakeClient creates a fake client
43+
func NewFakeClient(ctx context.Context, version string, err error) (RunnerClient, func()) {
44+
buffer := 101024 * 1024
45+
lis := bufconn.Listen(buffer)
46+
47+
baseServer := grpc.NewServer()
48+
RegisterRunnerServer(baseServer, NewServer(version, err))
49+
go func() {
50+
if err := baseServer.Serve(lis); err != nil {
51+
log.Printf("error serving server: %v", err)
52+
}
53+
}()
54+
55+
conn, err := grpc.DialContext(ctx, "",
56+
grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
57+
return lis.Dial()
58+
}), grpc.WithTransportCredentials(insecure.NewCredentials()))
59+
if err != nil {
60+
log.Printf("error connecting to server: %v", err)
61+
}
62+
63+
closer := func() {
64+
err := lis.Close()
65+
if err != nil {
66+
log.Printf("error closing listener: %v", err)
67+
}
68+
baseServer.Stop()
69+
}
70+
71+
client := NewRunnerClient(conn)
72+
return client, closer
73+
}

pkg/server/remote_server.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ func NewRemoteServer() RunnerServer {
2626

2727
// Run start to run the test task
2828
func (s *server) Run(ctx context.Context, task *TestTask) (reply *HelloReply, err error) {
29+
if task.Level == "" {
30+
task.Level = "info"
31+
}
32+
2933
var suite *testing.TestSuite
3034
if task.Env == nil {
3135
task.Env = map[string]string{}
@@ -88,7 +92,8 @@ func (s *server) Run(ctx context.Context, task *TestTask) (reply *HelloReply, er
8892
return
8993
}
9094

91-
fmt.Println("prepare to run:", suite.Name)
95+
fmt.Printf("prepare to run: %s, with level: %s\n", suite.Name, task.Level)
96+
fmt.Printf("task kind: %s, %d to run\n", task.Kind, len(suite.Items))
9297
dataContext := map[string]interface{}{}
9398

9499
var result string
@@ -104,6 +109,7 @@ func (s *server) Run(ctx context.Context, task *TestTask) (reply *HelloReply, er
104109
for _, testCase := range suite.Items {
105110
simpleRunner := runner.NewSimpleTestCaseRunner()
106111
simpleRunner.WithOutputWriter(buf)
112+
simpleRunner.WithWriteLevel(task.Level)
107113

108114
// reuse the API prefix
109115
if strings.HasPrefix(testCase.Request.API, "/") {
@@ -128,20 +134,26 @@ func (s *server) GetVersion(ctx context.Context, in *Empty) (reply *HelloReply,
128134
}
129135

130136
func findParentTestCases(testcase *testing.TestCase, suite *testing.TestSuite) (testcases []testing.TestCase) {
131-
reg, matchErr := regexp.Compile(`.*\{\{\.\w*\..*}\}.*`)
132-
targetReg, targetErr := regexp.Compile(`\{\{\.\w*\.`)
137+
reg, matchErr := regexp.Compile(`.*\{\{.*\.\w*.*}\}.*`)
138+
targetReg, targetErr := regexp.Compile(`\.\w*`)
133139

134140
if matchErr == nil && targetErr == nil {
135141
expectName := ""
136142
for _, val := range testcase.Request.Header {
137143
if matched := reg.MatchString(val); matched {
138144
expectName = targetReg.FindString(val)
139-
expectName = strings.TrimPrefix(expectName, "{{.")
140-
expectName = strings.TrimSuffix(expectName, ".")
145+
expectName = strings.TrimPrefix(expectName, ".")
141146
break
142147
}
143148
}
144149

150+
if expectName == "" {
151+
if mached := reg.MatchString(testcase.Request.API); mached {
152+
expectName = targetReg.FindString(testcase.Request.API)
153+
expectName = strings.TrimPrefix(expectName, ".")
154+
}
155+
}
156+
145157
for _, item := range suite.Items {
146158
if item.Name == expectName {
147159
testcases = append(testcases, item)

0 commit comments

Comments
 (0)