github.com/kubeshop/testkube@v1.17.23/pkg/cloud/data/testresult/testresult.go (about) 1 package testresult 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "time" 8 9 "go.mongodb.org/mongo-driver/mongo" 10 "google.golang.org/grpc" 11 12 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 13 "github.com/kubeshop/testkube/pkg/cloud" 14 "github.com/kubeshop/testkube/pkg/cloud/data/executor" 15 "github.com/kubeshop/testkube/pkg/repository/testresult" 16 ) 17 18 var _ testresult.Repository = (*CloudRepository)(nil) 19 20 type CloudRepository struct { 21 executor executor.Executor 22 } 23 24 func NewCloudRepository(client cloud.TestKubeCloudAPIClient, grpcConn *grpc.ClientConn, apiKey string) *CloudRepository { 25 return &CloudRepository{executor: executor.NewCloudGRPCExecutor(client, grpcConn, apiKey)} 26 } 27 28 func (r *CloudRepository) Get(ctx context.Context, id string) (testkube.TestSuiteExecution, error) { 29 req := GetRequest{ID: id} 30 response, err := r.executor.Execute(ctx, CmdTestResultGet, req) 31 if err != nil { 32 return testkube.TestSuiteExecution{}, err 33 } 34 var commandResponse GetResponse 35 if err := json.Unmarshal(response, &commandResponse); err != nil { 36 return testkube.TestSuiteExecution{}, err 37 } 38 return commandResponse.TestSuiteExecution, nil 39 } 40 41 func (r *CloudRepository) GetByNameAndTestSuite(ctx context.Context, name, testSuiteName string) (testkube.TestSuiteExecution, error) { 42 req := GetByNameAndTestSuiteRequest{Name: name, TestSuiteName: testSuiteName} 43 response, err := r.executor.Execute(ctx, CmdTestResultGetByNameAndTestSuite, req) 44 if err != nil { 45 return testkube.TestSuiteExecution{}, err 46 } 47 var commandResponse GetByNameAndTestSuiteResponse 48 if err := json.Unmarshal(response, &commandResponse); err != nil { 49 return testkube.TestSuiteExecution{}, err 50 } 51 return commandResponse.TestSuiteExecution, nil 52 } 53 54 func (r *CloudRepository) getLatestByTestSuite(ctx context.Context, testSuiteName, sortField string) (testkube.TestSuiteExecution, error) { 55 req := GetLatestByTestSuiteRequest{TestSuiteName: testSuiteName, SortField: sortField} 56 response, err := r.executor.Execute(ctx, CmdTestResultGetLatestByTestSuite, req) 57 if err != nil { 58 return testkube.TestSuiteExecution{}, err 59 } 60 var commandResponse GetLatestByTestSuiteResponse 61 if err := json.Unmarshal(response, &commandResponse); err != nil { 62 return testkube.TestSuiteExecution{}, err 63 } 64 return commandResponse.TestSuiteExecution, nil 65 } 66 67 // TODO: When it will be implemented, replace with a new Cloud command, to avoid 2 calls with 2 sort fields 68 func (r *CloudRepository) GetLatestByTestSuite(ctx context.Context, testSuiteName string) (*testkube.TestSuiteExecution, error) { 69 startExecution, startErr := r.getLatestByTestSuite(ctx, testSuiteName, "starttime") 70 if startErr != nil && startErr != mongo.ErrNoDocuments { 71 return nil, startErr 72 } 73 endExecution, endErr := r.getLatestByTestSuite(ctx, testSuiteName, "endtime") 74 if endErr != nil && endErr != mongo.ErrNoDocuments { 75 return nil, endErr 76 } 77 78 if startErr == nil && endErr == nil { 79 if startExecution.StartTime.After(endExecution.EndTime) { 80 return &startExecution, nil 81 } else { 82 return &endExecution, nil 83 } 84 } else if startErr == nil { 85 return &startExecution, nil 86 } else if endErr == nil { 87 return &endExecution, nil 88 } 89 return nil, startErr 90 } 91 92 func (r *CloudRepository) getLatestByTestSuites(ctx context.Context, testSuiteNames []string, sortField string) (executions []testkube.TestSuiteExecution, err error) { 93 req := GetLatestByTestSuitesRequest{TestSuiteNames: testSuiteNames, SortField: sortField} 94 response, err := r.executor.Execute(ctx, CmdTestResultGetLatestByTestSuites, req) 95 if err != nil { 96 return nil, err 97 } 98 var commandResponse GetLatestByTestSuitesResponse 99 if err := json.Unmarshal(response, &commandResponse); err != nil { 100 return nil, err 101 } 102 return commandResponse.TestSuiteExecutions, nil 103 } 104 105 // TODO: When it will be implemented, replace with a new Cloud command, to avoid 2 calls with 2 sort fields 106 func (r *CloudRepository) GetLatestByTestSuites(ctx context.Context, testSuiteNames []string) ([]testkube.TestSuiteExecution, error) { 107 startExecutions, err := r.getLatestByTestSuites(ctx, testSuiteNames, "starttime") 108 if err != nil { 109 return nil, err 110 } 111 endExecutions, err := r.getLatestByTestSuites(ctx, testSuiteNames, "endtime") 112 if err != nil { 113 return nil, err 114 } 115 executionsCount := len(startExecutions) 116 if len(endExecutions) > executionsCount { 117 executionsCount = len(endExecutions) 118 } 119 executionsMap := make(map[string]*testkube.TestSuiteExecution, executionsCount) 120 for i := range startExecutions { 121 if startExecutions[i].TestSuite == nil { 122 continue 123 } 124 executionsMap[startExecutions[i].TestSuite.Name] = &startExecutions[i] 125 } 126 for i := range endExecutions { 127 if endExecutions[i].TestSuite == nil { 128 continue 129 } 130 startExecution, ok := executionsMap[endExecutions[i].TestSuite.Name] 131 if ok { 132 if endExecutions[i].EndTime.After(startExecution.StartTime) { 133 executionsMap[endExecutions[i].TestSuite.Name] = &endExecutions[i] 134 } 135 } else { 136 executionsMap[endExecutions[i].TestSuite.Name] = &endExecutions[i] 137 } 138 } 139 executions := make([]testkube.TestSuiteExecution, 0, executionsCount) 140 for _, value := range executionsMap { 141 executions = append(executions, *value) 142 } 143 return executions, nil 144 } 145 146 func (r *CloudRepository) GetExecutionsTotals(ctx context.Context, filters ...testresult.Filter) (totals testkube.ExecutionsTotals, err error) { 147 var filterImpls []*testresult.FilterImpl 148 for _, f := range filters { 149 filterImpl, ok := f.(*testresult.FilterImpl) 150 if !ok { 151 return testkube.ExecutionsTotals{}, errors.New("invalid filter") 152 } 153 filterImpls = append(filterImpls, filterImpl) 154 } 155 req := GetExecutionsTotalsRequest{Filter: filterImpls} 156 response, err := r.executor.Execute(ctx, CmdTestResultGetExecutionsTotals, req) 157 if err != nil { 158 return testkube.ExecutionsTotals{}, err 159 } 160 var commandResponse GetExecutionsTotalsResponse 161 if err := json.Unmarshal(response, &commandResponse); err != nil { 162 return testkube.ExecutionsTotals{}, err 163 } 164 return commandResponse.ExecutionsTotals, nil 165 } 166 167 func (r *CloudRepository) GetExecutions(ctx context.Context, filter testresult.Filter) ([]testkube.TestSuiteExecution, error) { 168 filterImpl, ok := filter.(*testresult.FilterImpl) 169 if !ok { 170 return nil, errors.New("invalid filter") 171 } 172 req := GetExecutionsRequest{Filter: filterImpl} 173 response, err := r.executor.Execute(ctx, CmdTestResultGetExecutions, req) 174 if err != nil { 175 return nil, err 176 } 177 var commandResponse GetExecutionsResponse 178 if err := json.Unmarshal(response, &commandResponse); err != nil { 179 return nil, err 180 } 181 return commandResponse.TestSuiteExecutions, nil 182 } 183 184 func (r *CloudRepository) Insert(ctx context.Context, result testkube.TestSuiteExecution) error { 185 req := InsertRequest{TestSuiteExecution: result} 186 _, err := r.executor.Execute(ctx, CmdTestResultInsert, req) 187 return err 188 } 189 190 func (r *CloudRepository) Update(ctx context.Context, result testkube.TestSuiteExecution) error { 191 req := UpdateRequest{TestSuiteExecution: result} 192 _, err := r.executor.Execute(ctx, CmdTestResultUpdate, req) 193 return err 194 } 195 196 func (r *CloudRepository) StartExecution(ctx context.Context, id string, startTime time.Time) error { 197 req := StartExecutionRequest{ID: id, StartTime: startTime} 198 _, err := r.executor.Execute(ctx, CmdTestResultStartExecution, req) 199 return err 200 } 201 202 func (r *CloudRepository) EndExecution(ctx context.Context, execution testkube.TestSuiteExecution) error { 203 req := EndExecutionRequest{Execution: execution} 204 _, err := r.executor.Execute(ctx, CmdTestResultEndExecution, req) 205 return err 206 } 207 208 func (r *CloudRepository) DeleteByTestSuite(ctx context.Context, testSuiteName string) error { 209 req := DeleteByTestSuiteRequest{TestSuiteName: testSuiteName} 210 _, err := r.executor.Execute(ctx, CmdTestResultDeleteByTestSuite, req) 211 return err 212 } 213 214 func (r *CloudRepository) DeleteAll(ctx context.Context) error { 215 _, err := r.executor.Execute(ctx, CmdTestResultDeleteAll, nil) 216 return err 217 } 218 219 func (r *CloudRepository) DeleteByTestSuites(ctx context.Context, testSuiteNames []string) error { 220 req := DeleteByTestSuitesRequest{TestSuiteNames: testSuiteNames} 221 _, err := r.executor.Execute(ctx, CmdTestResultDeleteByTestSuites, req) 222 return err 223 } 224 225 func (r *CloudRepository) GetTestSuiteMetrics(ctx context.Context, name string, limit, last int) (testkube.ExecutionsMetrics, error) { 226 req := GetTestSuiteMetricsRequest{Name: name, Limit: limit, Last: last} 227 response, err := r.executor.Execute(ctx, CmdTestResultGetTestSuiteMetrics, req) 228 if err != nil { 229 return testkube.ExecutionsMetrics{}, err 230 } 231 var commandResponse GetTestSuiteMetricsResponse 232 if err := json.Unmarshal(response, &commandResponse); err != nil { 233 return testkube.ExecutionsMetrics{}, err 234 } 235 return commandResponse.Metrics, nil 236 } 237 238 func (r *CloudRepository) Count(ctx context.Context, filter testresult.Filter) (int64, error) { 239 return 0, nil 240 }