github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/pkg/localdb/shared/test_utils.go (about) 1 //nolint:all 2 package shared 3 4 import ( 5 "context" 6 "database/sql" 7 "fmt" 8 "log" 9 "time" 10 11 "github.com/filecoin-project/bacalhau/pkg/localdb" 12 "github.com/filecoin-project/bacalhau/pkg/logger" 13 _ "github.com/filecoin-project/bacalhau/pkg/logger" 14 model "github.com/filecoin-project/bacalhau/pkg/model/v1beta1" 15 "github.com/filecoin-project/bacalhau/pkg/system" 16 "github.com/stretchr/testify/require" 17 "github.com/stretchr/testify/suite" 18 ) 19 20 type GenericSQLSuite struct { 21 suite.Suite 22 SetupHandler func() *GenericSQLDatastore 23 TeardownHandler func() 24 datastore *GenericSQLDatastore 25 db *sql.DB 26 } 27 28 func (suite *GenericSQLSuite) SetupTest() { 29 suite.Require().NoError(system.InitConfigForTesting(suite.T())) 30 logger.ConfigureTestLogging(suite.T()) 31 datastore := suite.SetupHandler() 32 suite.datastore = datastore 33 suite.db = datastore.GetDB() 34 } 35 36 func (suite *GenericSQLSuite) TearDownSuite() { 37 if suite.TeardownHandler != nil { 38 suite.TeardownHandler() 39 } 40 } 41 42 func (suite *GenericSQLSuite) TestSQLiteMigrations() { 43 _, err := suite.db.Exec(` 44 insert into job (id) values ('123'); 45 `) 46 require.NoError(suite.T(), err) 47 var id string 48 rows, err := suite.db.Query(` 49 select id from job; 50 `) 51 require.NoError(suite.T(), err) 52 defer rows.Close() 53 for rows.Next() { 54 err = rows.Scan(&id) 55 if err != nil { 56 log.Fatal(err) 57 } 58 log.Println(id) 59 } 60 err = rows.Err() 61 require.NoError(suite.T(), err) 62 require.Equal(suite.T(), id, "123") 63 } 64 65 func (suite *GenericSQLSuite) TestRoundtripJob() { 66 job := &model.Job{ 67 Metadata: model.Metadata{ 68 ID: "hellojob", 69 }, 70 } 71 err := suite.datastore.AddJob(context.Background(), job) 72 require.NoError(suite.T(), err) 73 loadedJob, err := suite.datastore.GetJob(context.Background(), job.Metadata.ID) 74 require.NoError(suite.T(), err) 75 require.Equal(suite.T(), job.Metadata.ID, loadedJob.Metadata.ID) 76 } 77 78 func (suite *GenericSQLSuite) TestAddingTwoJobs() { 79 err := suite.datastore.AddJob(context.Background(), &model.Job{ 80 Metadata: model.Metadata{ 81 ID: "hellojob1", 82 }, 83 }) 84 require.NoError(suite.T(), err) 85 err = suite.datastore.AddJob(context.Background(), &model.Job{ 86 Metadata: model.Metadata{ 87 ID: "hellojob2", 88 }, 89 }) 90 require.NoError(suite.T(), err) 91 } 92 93 //nolint:funlen 94 func (suite *GenericSQLSuite) TestGetJobs() { 95 jobCount := 100 96 dateString := "2021-11-22" 97 date, err := time.Parse("2006-01-02", dateString) 98 require.NoError(suite.T(), err) 99 100 for i := 0; i < jobCount; i++ { 101 date = date.Add(time.Hour * 1) 102 annotations := []string{"apples", fmt.Sprintf("oranges%d", i)} 103 if i < jobCount/2 { 104 annotations = append(annotations, "bananas") 105 } 106 job := &model.Job{ 107 Metadata: model.Metadata{ 108 ID: fmt.Sprintf("hellojob%d", i), 109 CreatedAt: date, 110 ClientID: fmt.Sprintf("testclient%d", i), 111 }, 112 Spec: model.Spec{ 113 Annotations: annotations, 114 }, 115 } 116 err = suite.datastore.AddJob(context.Background(), job) 117 require.NoError(suite.T(), err) 118 } 119 120 // sanity check that we can see all jobs 121 allJobs, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{}) 122 require.NoError(suite.T(), err) 123 require.Equal(suite.T(), jobCount, len(allJobs)) 124 125 // sort by date asc and check first id 126 sortedAsc, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 127 SortBy: "created_at", 128 SortReverse: false, 129 }) 130 require.NoError(suite.T(), err) 131 require.Equal(suite.T(), jobCount, len(sortedAsc)) 132 require.Equal(suite.T(), "hellojob0", sortedAsc[0].Metadata.ID) 133 134 // sort by date desc and check first id 135 sortedDesc, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 136 SortBy: "created_at", 137 SortReverse: true, 138 }) 139 require.NoError(suite.T(), err) 140 require.Equal(suite.T(), jobCount, len(sortedDesc)) 141 require.Equal(suite.T(), "hellojob99", sortedDesc[0].Metadata.ID) 142 143 // check basic limit 144 const limit = 10 145 tenJobs, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 146 Limit: limit, 147 }) 148 require.NoError(suite.T(), err) 149 require.Equal(suite.T(), limit, len(tenJobs)) 150 151 // pagination 152 tenJobsSecondPage, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 153 Limit: limit, 154 Offset: 10, 155 }) 156 require.NoError(suite.T(), err) 157 require.Equal(suite.T(), limit, len(tenJobsSecondPage)) 158 require.Equal(suite.T(), "hellojob10", tenJobsSecondPage[0].Metadata.ID) 159 160 loadedJobCount, err := suite.datastore.GetJobsCount(context.Background(), localdb.JobQuery{ 161 IncludeTags: []model.IncludedTag{"bananas"}, 162 Limit: 1, 163 SortBy: "created_at", 164 SortReverse: true, 165 }) 166 require.NoError(suite.T(), err) 167 require.Equal(suite.T(), jobCount/2, loadedJobCount) 168 169 sortedWithLimit, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 170 SortBy: "created_at", 171 SortReverse: true, 172 Limit: limit, 173 }) 174 require.NoError(suite.T(), err) 175 require.Equal(suite.T(), limit, len(sortedWithLimit)) 176 177 // a label they all have 178 withAppleLabel, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 179 IncludeTags: []model.IncludedTag{"apples"}, 180 }) 181 require.NoError(suite.T(), err) 182 require.Equal(suite.T(), jobCount, len(withAppleLabel)) 183 184 // a label only half of them have 185 withBananasLabel, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 186 IncludeTags: []model.IncludedTag{"bananas"}, 187 }) 188 require.NoError(suite.T(), err) 189 require.Equal(suite.T(), jobCount/2, len(withBananasLabel)) 190 191 // a label only half of them have plus a second label 192 withAppleAndBananaLabel, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 193 IncludeTags: []model.IncludedTag{"apples", "bananas"}, 194 }) 195 require.NoError(suite.T(), err) 196 require.Equal(suite.T(), jobCount/2, len(withAppleAndBananaLabel)) 197 198 // combine three labels - only 1 result 199 withAppleAndBananaAndIDLabel, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 200 IncludeTags: []model.IncludedTag{"apples", "bananas", "oranges17"}, 201 }) 202 require.NoError(suite.T(), err) 203 require.Equal(suite.T(), 1, len(withAppleAndBananaAndIDLabel)) 204 205 // exclude with a single id 206 basicExclude, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 207 ExcludeTags: []model.ExcludedTag{"oranges17"}, 208 }) 209 require.NoError(suite.T(), err) 210 require.Equal(suite.T(), jobCount-1, len(basicExclude)) 211 212 // exclude half 213 halfExclude, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 214 ExcludeTags: []model.ExcludedTag{"bananas"}, 215 }) 216 require.NoError(suite.T(), err) 217 require.Equal(suite.T(), jobCount/2, len(halfExclude)) 218 219 // include and exclude 220 includeAndExclude, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 221 IncludeTags: []model.IncludedTag{"apples", "bananas"}, 222 ExcludeTags: []model.ExcludedTag{"oranges17"}, 223 }) 224 require.NoError(suite.T(), err) 225 require.Equal(suite.T(), jobCount/2-1, len(includeAndExclude)) 226 227 // load jobs from one client 228 singleClient, err := suite.datastore.GetJobs(context.Background(), localdb.JobQuery{ 229 ClientID: "testclient17", 230 }) 231 require.NoError(suite.T(), err) 232 require.Equal(suite.T(), 1, len(singleClient)) 233 } 234 235 func (suite *GenericSQLSuite) TestJobEvents() { 236 eventCount := 5 237 job := &model.Job{ 238 Metadata: model.Metadata{ 239 ID: "hellojob", 240 }, 241 } 242 err := suite.datastore.AddJob(context.Background(), job) 243 require.NoError(suite.T(), err) 244 245 dateString := "2021-11-22" 246 date, err := time.Parse("2006-01-02", dateString) 247 require.NoError(suite.T(), err) 248 249 for i := 0; i < eventCount; i++ { 250 date = date.Add(time.Minute * 1) 251 eventName := model.JobEventBid 252 localEventName := model.JobLocalEventBid 253 if i == 0 { 254 eventName = model.JobEventCreated 255 localEventName = model.JobLocalEventSelected 256 } 257 ev := model.JobEvent{ 258 JobID: job.Metadata.ID, 259 EventName: eventName, 260 EventTime: date, 261 } 262 err = suite.datastore.AddEvent(context.Background(), job.Metadata.ID, ev) 263 require.NoError(suite.T(), err) 264 localEvent := model.JobLocalEvent{ 265 JobID: job.Metadata.ID, 266 EventName: localEventName, 267 } 268 err = suite.datastore.AddLocalEvent(context.Background(), job.Metadata.ID, localEvent) 269 require.NoError(suite.T(), err) 270 } 271 272 jobEvents, err := suite.datastore.GetJobEvents(context.Background(), job.Metadata.ID) 273 require.NoError(suite.T(), err) 274 require.Equal(suite.T(), eventCount, len(jobEvents)) 275 // check that the EventName of the first event is the created one 276 require.Equal(suite.T(), model.JobEventCreated, jobEvents[0].EventName) 277 278 // repeat the same event block above but for local events 279 localEvents, err := suite.datastore.GetJobLocalEvents(context.Background(), job.Metadata.ID) 280 require.NoError(suite.T(), err) 281 require.Equal(suite.T(), eventCount, len(localEvents)) 282 // check that the EventName of the first event is the created one 283 require.Equal(suite.T(), model.JobLocalEventSelected, localEvents[0].EventName) 284 } 285 286 func (suite *GenericSQLSuite) TestJobState() { 287 job := &model.Job{ 288 Metadata: model.Metadata{ 289 ID: "hellojob", 290 }, 291 } 292 err := suite.datastore.AddJob(context.Background(), job) 293 require.NoError(suite.T(), err) 294 err = suite.datastore.UpdateShardState( 295 context.Background(), 296 job.Metadata.ID, 297 "node-test", 298 0, 299 model.JobShardState{ 300 NodeID: "node-test", 301 ShardIndex: 0, 302 State: model.JobStateRunning, 303 }, 304 ) 305 require.NoError(suite.T(), err) 306 state, err := suite.datastore.GetJobState(context.Background(), job.Metadata.ID) 307 require.NoError(suite.T(), err) 308 node, ok := state.Nodes["node-test"] 309 require.True(suite.T(), ok) 310 shard, ok := node.Shards[0] 311 require.True(suite.T(), ok) 312 require.Equal(suite.T(), "node-test", shard.NodeID) 313 require.Equal(suite.T(), model.JobStateRunning, shard.State) 314 } 315 316 func (suite *GenericSQLSuite) TestUpdateDeal() { 317 job := &model.Job{ 318 Metadata: model.Metadata{ 319 ID: "hellojob", 320 }, 321 Spec: model.Spec{ 322 Deal: model.Deal{ 323 Concurrency: 1, 324 }, 325 }, 326 } 327 err := suite.datastore.AddJob(context.Background(), job) 328 require.NoError(suite.T(), err) 329 err = suite.datastore.UpdateJobDeal( 330 context.Background(), 331 job.Metadata.ID, 332 model.Deal{ 333 Concurrency: 3, 334 }, 335 ) 336 require.NoError(suite.T(), err) 337 updatedJob, err := suite.datastore.GetJob(context.Background(), job.Metadata.ID) 338 require.NoError(suite.T(), err) 339 require.Equal(suite.T(), 3, updatedJob.Spec.Deal.Concurrency) 340 }