github.com/matrixorigin/matrixone@v0.7.0/pkg/util/trace/impl/motrace/report_statement_test.go (about)

     1  // Copyright 2022 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package motrace
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"github.com/google/uuid"
    22  	"github.com/prashantv/gostub"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  	"testing"
    26  	"time"
    27  )
    28  
    29  func TestStatementInfo_Report_EndStatement(t *testing.T) {
    30  	type fields struct {
    31  		StatementID          [16]byte
    32  		TransactionID        [16]byte
    33  		SessionID            [16]byte
    34  		Account              string
    35  		User                 string
    36  		Host                 string
    37  		Database             string
    38  		Statement            string
    39  		StatementFingerprint string
    40  		StatementTag         string
    41  		RequestAt            time.Time
    42  		ExecPlan             any
    43  		Status               StatementInfoStatus
    44  		Error                error
    45  		ResponseAt           time.Time
    46  		Duration             time.Duration
    47  
    48  		doReport bool
    49  		doExport bool
    50  	}
    51  	type args struct {
    52  		ctx context.Context
    53  		err error
    54  	}
    55  
    56  	tests := []struct {
    57  		name          string
    58  		fields        fields
    59  		args          args
    60  		wantReportCnt int
    61  	}{
    62  		{
    63  			name: "Report_Export_EndStatement",
    64  			fields: fields{
    65  				doReport: true,
    66  				doExport: true,
    67  			},
    68  			args: args{
    69  				ctx: context.Background(),
    70  				err: nil,
    71  			},
    72  			wantReportCnt: 2,
    73  		},
    74  		{
    75  			name: "Report_EndStatement",
    76  			fields: fields{
    77  				doReport: true,
    78  				doExport: false,
    79  			},
    80  			args: args{
    81  				ctx: context.Background(),
    82  				err: nil,
    83  			},
    84  			wantReportCnt: 1,
    85  		},
    86  		{
    87  			name: "just_EndStatement",
    88  			fields: fields{
    89  				doReport: false,
    90  				doExport: false,
    91  			},
    92  			args: args{
    93  				ctx: context.Background(),
    94  				err: nil,
    95  			},
    96  			wantReportCnt: 1,
    97  		},
    98  	}
    99  
   100  	gotCnt := 0
   101  	dummyReportStmFunc := func(ctx context.Context, s *StatementInfo) error {
   102  		gotCnt++
   103  		return nil
   104  	}
   105  	s := gostub.Stub(&ReportStatement, dummyReportStmFunc)
   106  	defer s.Reset()
   107  
   108  	dummyExport := func(s *StatementInfo) {
   109  		s.mux.Lock()
   110  		s.exported = true
   111  		s.mux.Unlock()
   112  	}
   113  
   114  	for _, tt := range tests {
   115  		t.Run(tt.name, func(t *testing.T) {
   116  			gotCnt = 0
   117  			s := &StatementInfo{
   118  				StatementID:          tt.fields.StatementID,
   119  				TransactionID:        tt.fields.TransactionID,
   120  				SessionID:            tt.fields.SessionID,
   121  				Account:              tt.fields.Account,
   122  				User:                 tt.fields.User,
   123  				Host:                 tt.fields.Host,
   124  				Database:             tt.fields.Database,
   125  				Statement:            tt.fields.Statement,
   126  				StatementFingerprint: tt.fields.StatementFingerprint,
   127  				StatementTag:         tt.fields.StatementTag,
   128  				RequestAt:            tt.fields.RequestAt,
   129  				ExecPlan:             tt.fields.ExecPlan,
   130  				Status:               tt.fields.Status,
   131  				Error:                tt.fields.Error,
   132  				ResponseAt:           tt.fields.ResponseAt,
   133  				Duration:             tt.fields.Duration,
   134  			}
   135  			if tt.fields.doExport && !tt.fields.doReport {
   136  				t.Errorf("export(%v) need report(%v) first.", tt.fields.doExport, tt.fields.doReport)
   137  			}
   138  			if tt.fields.doReport {
   139  				s.Report(tt.args.ctx)
   140  			}
   141  			if tt.fields.doExport {
   142  				dummyExport(s)
   143  			}
   144  			require.Equal(t, tt.fields.doReport, s.reported)
   145  			require.Equal(t, tt.fields.doExport, s.exported)
   146  
   147  			stmCtx := ContextWithStatement(tt.args.ctx, s)
   148  			EndStatement(stmCtx, tt.args.err)
   149  			require.Equal(t, tt.wantReportCnt, gotCnt)
   150  		})
   151  	}
   152  }
   153  
   154  var realNoExecPlanJsonResult = `{"code":200,"message":"NO ExecPlan Serialize function","steps":null,"success":false,"uuid":"00000000-0000-0000-0000-000000000000"}`
   155  var dummyNoExecPlanJsonResult = `{"code":200,"message":"no exec plan"}`
   156  var dummyNoExecPlanJsonResult2 = `{"func":"dummy2","code":200,"message":"no exec plan"}`
   157  
   158  var dummySerializeExecPlan = func(_ context.Context, plan any, _ uuid.UUID) ([]byte, []byte, Statistic) {
   159  	if plan == nil {
   160  		return []byte(dummyNoExecPlanJsonResult), []byte{}, Statistic{}
   161  	}
   162  	json, err := json.Marshal(plan)
   163  	if err != nil {
   164  		return []byte(fmt.Sprintf(`{"err": %q}`, err.Error())), []byte{}, Statistic{}
   165  	}
   166  	return json, []byte{}, Statistic{RowsRead: 1, BytesScan: 1}
   167  }
   168  
   169  var dummySerializeExecPlan2 = func(_ context.Context, plan any, _ uuid.UUID) ([]byte, []byte, Statistic) {
   170  	if plan == nil {
   171  		return []byte(dummyNoExecPlanJsonResult2), []byte{}, Statistic{}
   172  	}
   173  	json, err := json.Marshal(plan)
   174  	if err != nil {
   175  		return []byte(fmt.Sprintf(`{"func":"dymmy2","err": %q}`, err.Error())), []byte{}, Statistic{}
   176  	}
   177  	val := fmt.Sprintf(`{"func":"dummy2","result":%s}`, json)
   178  	return []byte(val), []byte{}, Statistic{}
   179  }
   180  
   181  func TestStatementInfo_ExecPlan2Json(t *testing.T) {
   182  	type args struct {
   183  		setDefault        func()
   184  		ExecPlan          any
   185  		SerializeExecPlan SerializeExecPlanFunc
   186  	}
   187  
   188  	dummyExecPlan := map[string]any{"key": "val", "int": 1}
   189  	defaultEPJson := `{"int":1,"key":"val"}`
   190  	dummyEPJson := `{"func":"dummy2","result":{"int":1,"key":"val"}}`
   191  
   192  	var setDefaultNil = func() {
   193  		SetDefaultSerializeExecPlan(nil)
   194  	}
   195  	var setDefaultDummy = func() {
   196  		SetDefaultSerializeExecPlan(dummySerializeExecPlan)
   197  	}
   198  
   199  	tests := []struct {
   200  		name string
   201  		args args
   202  		want string
   203  	}{
   204  		{
   205  			name: "nil",
   206  			args: args{
   207  				setDefault:        setDefaultNil,
   208  				ExecPlan:          nil,
   209  				SerializeExecPlan: nil,
   210  			},
   211  			want: realNoExecPlanJsonResult,
   212  		},
   213  		{
   214  			name: "nil_ep_nil",
   215  			args: args{
   216  				setDefault:        setDefaultNil,
   217  				ExecPlan:          dummyExecPlan,
   218  				SerializeExecPlan: nil,
   219  			},
   220  			want: realNoExecPlanJsonResult,
   221  		},
   222  		{
   223  			name: "dummyDefault_nil_nil",
   224  			args: args{
   225  				setDefault:        setDefaultDummy,
   226  				ExecPlan:          nil,
   227  				SerializeExecPlan: nil,
   228  			},
   229  			want: dummyNoExecPlanJsonResult,
   230  		},
   231  		{
   232  			name: "dummyDefault_ep_nil",
   233  			args: args{
   234  				setDefault:        setDefaultDummy,
   235  				ExecPlan:          dummyExecPlan,
   236  				SerializeExecPlan: nil,
   237  			},
   238  			want: defaultEPJson,
   239  		},
   240  		{
   241  			name: "dummyDefault_ep_Serialize",
   242  			args: args{
   243  				setDefault:        setDefaultDummy,
   244  				ExecPlan:          dummyExecPlan,
   245  				SerializeExecPlan: dummySerializeExecPlan2,
   246  			},
   247  			want: dummyEPJson,
   248  		},
   249  		{
   250  			name: "nil_ep_Serialize",
   251  			args: args{
   252  				setDefault:        setDefaultNil,
   253  				ExecPlan:          dummyExecPlan,
   254  				SerializeExecPlan: dummySerializeExecPlan2,
   255  			},
   256  			want: dummyEPJson,
   257  		},
   258  		{
   259  			name: "dummyDefault_nil_Serialize",
   260  			args: args{
   261  				setDefault:        setDefaultDummy,
   262  				ExecPlan:          nil,
   263  				SerializeExecPlan: dummySerializeExecPlan2,
   264  			},
   265  			want: dummyNoExecPlanJsonResult2,
   266  		},
   267  		{
   268  			name: "nil_nil_Serialize",
   269  			args: args{
   270  				setDefault:        setDefaultNil,
   271  				ExecPlan:          nil,
   272  				SerializeExecPlan: dummySerializeExecPlan2,
   273  			},
   274  			want: dummyNoExecPlanJsonResult2,
   275  		},
   276  	}
   277  
   278  	ctx := DefaultContext()
   279  	for _, tt := range tests {
   280  		t.Run(tt.name, func(t *testing.T) {
   281  			tt.args.setDefault()
   282  			s := &StatementInfo{}
   283  			s.SetExecPlan(tt.args.ExecPlan, tt.args.SerializeExecPlan)
   284  			got, _ := s.ExecPlan2Json(ctx)
   285  			assert.Equalf(t, tt.want, got, "ExecPlan2Json()")
   286  
   287  			mapper := new(map[string]any)
   288  			err := json.Unmarshal([]byte(got), mapper)
   289  			require.Nil(t, err, "jons.Unmarshal failed")
   290  		})
   291  	}
   292  }