github.com/matrixorigin/matrixone@v1.2.0/pkg/taskservice/mysql_task_storage_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 taskservice 16 17 import ( 18 "context" 19 "fmt" 20 "github.com/DATA-DOG/go-sqlmock" 21 "github.com/matrixorigin/matrixone/pkg/pb/task" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 "slices" 25 "strings" 26 "testing" 27 ) 28 29 func TestBuildWhereClause(t *testing.T) { 30 cases := []struct { 31 condition conditions 32 33 expected string 34 }{ 35 { 36 condition: conditions(map[condCode]condition{CondTaskID: &taskIDCond{op: EQ, taskID: 1}}), 37 expected: " AND task_id=1", 38 }, 39 { 40 condition: conditions( 41 map[condCode]condition{ 42 CondTaskID: &taskIDCond{op: EQ, taskID: 1}, 43 CondTaskRunner: &taskRunnerCond{op: EQ, taskRunner: "abc"}, 44 CondTaskStatus: &taskStatusCond{op: IN, taskStatus: []task.TaskStatus{task.TaskStatus_Created}}, 45 CondTaskEpoch: &taskEpochCond{op: LE, taskEpoch: 100}, 46 CondTaskParentTaskID: &taskParentTaskIDCond{op: GE, taskParentTaskID: "ab"}, 47 CondTaskExecutor: &taskExecutorCond{op: GE, taskExecutor: 1}, 48 }, 49 ), 50 expected: " AND task_id=1 AND task_runner='abc' AND task_status IN (0) AND task_epoch<=100 AND task_parent_id>='ab' AND task_metadata_executor>=1", 51 }, 52 { 53 condition: conditions(map[condCode]condition{ 54 CondTaskRunner: &taskRunnerCond{op: EQ, taskRunner: "abc"}, 55 CondTaskStatus: &taskStatusCond{op: IN, taskStatus: []task.TaskStatus{task.TaskStatus_Created}}, 56 CondTaskParentTaskID: &taskParentTaskIDCond{op: GE, taskParentTaskID: "ab"}, 57 CondTaskExecutor: &taskExecutorCond{op: LE, taskExecutor: 1}, 58 }, 59 ), 60 expected: " AND task_runner='abc' AND task_status IN (0) AND task_parent_id>='ab' AND task_metadata_executor<=1", 61 }, 62 } 63 64 for _, c := range cases { 65 result := buildWhereClause(&c.condition) 66 actual := strings.Split(result, " AND ") 67 expected := strings.Split(c.expected, " AND ") 68 slices.Sort(actual) 69 slices.Sort(expected) 70 assert.Equal(t, expected, actual) 71 } 72 } 73 74 const ( 75 useDB = "use mo_task" 76 setTrace = "set session disable_txn_trace=1" 77 ) 78 79 var ( 80 asyncRows = []string{ 81 "task_id", 82 "task_metadata_id", 83 "task_metadata_executor", 84 "task_metadata_context", 85 "task_metadata_option", 86 "task_parent_id", 87 "task_status", 88 "task_runner", 89 "task_epoch", 90 "last_heartbeat", 91 "result_code", 92 "error_msg", 93 "create_at", 94 "end_at"} 95 96 cronRows = []string{ 97 "task_id", 98 "task_metadata_id", 99 "task_metadata_executor", 100 "task_metadata_context", 101 "task_metadata_option", 102 "cron_expr", 103 "next_time", 104 "trigger_times", 105 "create_at", 106 "update_at", 107 } 108 109 expectUseDB = func(mock sqlmock.Sqlmock) { 110 mock.ExpectExec(useDB).WillReturnResult(sqlmock.NewResult(0, 1)) 111 mock.ExpectExec(setTrace).WillReturnResult(sqlmock.NewResult(0, 1)) 112 } 113 ) 114 115 func TestAsyncTaskInSqlMock(t *testing.T) { 116 storage, mock := newMockStorage(t, "sqlmock", "mo_task") 117 expectUseDB(mock) 118 mock.ExpectPrepare(fmt.Sprintf(insertAsyncTask, storage.dbname) + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") 119 mock.ExpectExec(fmt.Sprintf(insertAsyncTask, storage.dbname)+"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"). 120 WithArgs("a", 0, []byte(nil), "{}", "", 0, "", 0, 0, sqlmock.AnyArg(), 0). 121 WillReturnResult(sqlmock.NewResult(1, 1)) 122 123 affected, err := storage.AddAsyncTask(context.Background(), newTaskFromMetadata(task.TaskMetadata{ID: "a"})) 124 assert.NoError(t, err) 125 assert.Equal(t, 1, affected) 126 127 expectUseDB(mock) 128 mock.ExpectQuery(fmt.Sprintf(selectAsyncTask, storage.dbname) + " AND task_id=1 order by task_id"). 129 WillReturnRows(sqlmock.NewRows(asyncRows). 130 AddRow(1, "a", 0, []byte(nil), "{}", "", 0, "", 0, 0, 0, "", 0, 0)) 131 asyncTask, err := storage.QueryAsyncTask(context.Background(), WithTaskIDCond(EQ, 1)) 132 assert.NoError(t, err) 133 assert.Equal(t, 1, len(asyncTask)) 134 assert.Equal(t, "a", asyncTask[0].Metadata.ID) 135 136 expectUseDB(mock) 137 mock.ExpectBegin() 138 mock.ExpectPrepare(fmt.Sprintf(updateAsyncTask, storage.dbname) + " AND task_epoch=0") 139 mock.ExpectExec(fmt.Sprintf(updateAsyncTask, storage.dbname)+" AND task_epoch=0"). 140 WithArgs(0, []byte(nil), "{}", "", 0, "c1", 0, 0, 0, "", 0, 0, 1). 141 WillReturnResult(sqlmock.NewResult(0, 1)) 142 mock.ExpectCommit() 143 asyncTask[0].TaskRunner = "c1" 144 affected, err = storage.UpdateAsyncTask(context.Background(), []task.AsyncTask{asyncTask[0]}, WithTaskEpochCond(EQ, 0)) 145 assert.NoError(t, err) 146 assert.Equal(t, 1, affected) 147 148 mock.ExpectClose() 149 require.NoError(t, storage.Close()) 150 } 151 152 func TestCronTaskInSqlMock(t *testing.T) { 153 storage, mock := newMockStorage(t, "sqlmock", "mo_task") 154 expectUseDB(mock) 155 mock.ExpectPrepare(fmt.Sprintf(insertCronTask, storage.dbname) + "(?, ?, ?, ?, ?, ?, ?, ?, ?)") 156 mock.ExpectExec(fmt.Sprintf(insertCronTask, storage.dbname)+"(?, ?, ?, ?, ?, ?, ?, ?, ?)"). 157 WithArgs("a", 0, []byte(nil), "{}", "mock_cron_expr", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()). 158 WillReturnResult(sqlmock.NewResult(1, 1)) 159 160 affected, err := storage.AddCronTask(context.Background(), newTestCronTask("a", "mock_cron_expr")) 161 assert.NoError(t, err) 162 assert.Equal(t, 1, affected) 163 164 expectUseDB(mock) 165 mock.ExpectQuery(fmt.Sprintf(selectCronTask, storage.dbname) + " AND cron_task_id=1"). 166 WillReturnRows(sqlmock.NewRows(cronRows). 167 AddRow(1, "a", 0, []byte(nil), "{}", "mock_cron_expr", 0, 0, 0, 0)) 168 cronTask, err := storage.QueryCronTask(context.Background(), WithCronTaskId(EQ, 1)) 169 assert.NoError(t, err) 170 assert.Equal(t, 1, len(cronTask)) 171 assert.Equal(t, "a", cronTask[0].Metadata.ID) 172 173 mock.ExpectClose() 174 require.NoError(t, storage.Close()) 175 } 176 177 func newMockStorage(t *testing.T, dsn, dbname string) (*mysqlTaskStorage, sqlmock.Sqlmock) { 178 db, mock, err := sqlmock.NewWithDSN(dsn, sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) 179 require.NoError(t, err) 180 return &mysqlTaskStorage{ 181 dsn: dsn, 182 db: db, 183 dbname: dbname, 184 }, mock 185 }