github.com/octohelm/storage@v0.0.0-20240516030302-1ac2cc1ea347/pkg/dal/session_test.go (about) 1 package dal 2 3 import ( 4 "context" 5 "sync" 6 "testing" 7 8 "github.com/google/uuid" 9 "github.com/octohelm/storage/internal/testutil" 10 "github.com/octohelm/storage/pkg/dberr" 11 "github.com/octohelm/storage/pkg/sqlbuilder" 12 "github.com/octohelm/storage/testdata/model" 13 ) 14 15 type UserParam struct { 16 Age []int64 `name:"age" in:"query" ` 17 } 18 19 func (i UserParam) Apply(q Querier) Querier { 20 if q.ExistsTable(model.UserT) { 21 if len(i.Age) > 0 { 22 q = q.WhereAnd(model.UserT.Age.V(sqlbuilder.In(i.Age...))) 23 } 24 } 25 26 return q 27 } 28 29 func TestCRUD(t *testing.T) { 30 ctxs := []context.Context{ 31 ContextWithDatabase(t, "dal_sql_crud", ""), 32 ContextWithDatabase(t, "dal_sql_crud", "postgres://postgres@localhost?sslmode=disable"), 33 } 34 35 for i := range ctxs { 36 ctx := ctxs[i] 37 38 t.Run("Save one user", func(t *testing.T) { 39 usr := &model.User{ 40 Name: uuid.New().String(), 41 Age: 100, 42 } 43 err := Prepare(usr).IncludesZero(model.UserT.Nickname). 44 Returning(model.UserT.ID).Scan(usr). 45 Save(ctx) 46 47 testutil.Expect(t, err, testutil.Be[error](nil)) 48 testutil.Expect(t, usr.ID, testutil.Not(testutil.Be(uint64(0)))) 49 50 t.Run("Save same user agent, should conflict", func(t *testing.T) { 51 usr2 := &model.User{ 52 Name: usr.Name, 53 } 54 err := Prepare(usr2).Save(ctx) 55 testutil.Expect(t, dberr.IsErrConflict(err), testutil.Be(true)) 56 }) 57 58 t.Run("Save same user again, when set ignore should not clause conflict", func(t *testing.T) { 59 usr2 := &model.User{ 60 Name: usr.Name, 61 Nickname: "test", 62 } 63 64 err := Prepare(usr2). 65 OnConflict(model.UserT.I.IName).DoNothing(). 66 Returning(model.UserT.ID, model.UserT.Age).Scan(usr2). 67 Save(ctx) 68 69 testutil.Expect(t, err, testutil.Be[error](nil)) 70 }) 71 72 t.Run("Save same user again, when set ignore should not clause conflict", func(t *testing.T) { 73 usr2 := &model.User{ 74 Name: usr.Name, 75 Nickname: "test", 76 } 77 78 err := Prepare(usr2). 79 OnConflict(model.UserT.I.IName).DoUpdateSet(model.UserT.Nickname). 80 Returning(model.UserT.ID, model.UserT.Age, model.UserT.Username).Scan(usr2). 81 Save(ctx) 82 83 testutil.Expect(t, err, testutil.Be[error](nil)) 84 testutil.Expect(t, usr2.ID, testutil.Be(usr.ID)) 85 testutil.Expect(t, usr2.Age, testutil.Be(usr.Age)) 86 }) 87 88 t.Run("Update", func(t *testing.T) { 89 usr2 := &model.User{ 90 Nickname: "test test", 91 } 92 update := Prepare(usr2).Where(model.UserT.ID.V(sqlbuilder.Eq[uint64](100))) 93 94 err := update.Save(ctx) 95 testutil.Expect(t, err, testutil.Be[error](nil)) 96 }) 97 98 t.Run("SoftDelete", func(t *testing.T) { 99 deletedUser := &model.User{} 100 update := Prepare(&model.User{}).ForDelete(). 101 Returning().Scan(deletedUser). 102 Where(model.UserT.ID.V(sqlbuilder.Eq(usr.ID))) 103 104 err := update.Save(ctx) 105 testutil.Expect(t, err, testutil.Be[error](nil)) 106 testutil.Expect(t, deletedUser.ID, testutil.Be(usr.ID)) 107 testutil.Expect(t, deletedUser.ID, testutil.Be(usr.ID)) 108 }) 109 110 t.Run("Delete", func(t *testing.T) { 111 deletedUser := &model.User{} 112 113 update := Prepare(&model.User{}).ForDelete(HardDelete()). 114 Returning().Scan(deletedUser). 115 Where(model.UserT.ID.V(sqlbuilder.Eq(usr.ID))) 116 117 err := update.Save(ctx) 118 testutil.Expect(t, err, testutil.Be[error](nil)) 119 testutil.Expect(t, deletedUser.ID, testutil.Be(usr.ID)) 120 }) 121 }) 122 123 t.Run("Insert multi Users and Orgs", func(t *testing.T) { 124 err := Tx(ctx, &model.Org{}, func(ctx context.Context) error { 125 for i := 0; i < 2; i++ { 126 org := &model.Org{ 127 Name: uuid.New().String(), 128 } 129 if err := Prepare(org).Returning(model.OrgT.ID).Scan(org).Save(ctx); err != nil { 130 return err 131 } 132 } 133 134 for i := 0; i < 110; i++ { 135 usr := &model.User{ 136 Name: uuid.New().String(), 137 Age: int64(i), 138 } 139 140 err := Prepare(usr).IncludesZero(model.UserT.Nickname). 141 Returning(model.UserT.ID).Scan(usr). 142 Save(ctx) 143 if err != nil { 144 return err 145 } 146 147 if i >= 100 { 148 if err := Prepare(usr).ForDelete().Where( 149 model.UserT.Age.V(sqlbuilder.Eq[int64](usr.Age)), 150 ).Save(ctx); err != nil { 151 return err 152 } 153 } 154 155 orgUsr := &model.OrgUser{ 156 UserID: usr.ID, 157 OrgID: usr.ID%2 + 1, 158 } 159 if err := Prepare(orgUsr).Save(ctx); err != nil { 160 return err 161 } 162 } 163 164 return nil 165 }) 166 167 testutil.Expect(t, err, testutil.Be[error](nil)) 168 169 t.Run("Then Queries", func(t *testing.T) { 170 t.Run("Count", func(t *testing.T) { 171 c, err := From(model.UserT).Count(ctx) 172 173 testutil.Expect(t, err, testutil.Be[error](nil)) 174 testutil.Expect(t, c, testutil.Be(100)) 175 }) 176 177 t.Run("List all", func(t *testing.T) { 178 users := make([]model.User, 0) 179 180 err := From(model.UserT). 181 Scan(&users). 182 Find(ctx) 183 184 testutil.Expect(t, err, testutil.Be[error](nil)) 185 testutil.Expect(t, len(users), testutil.Be(100)) 186 }) 187 188 t.Run("List partial with cancel", func(t *testing.T) { 189 users := make([]*model.User, 0) 190 191 ctx, cancel := context.WithCancel(ctx) 192 193 err := From(model.UserT). 194 Scan(Recv(func(user *model.User) error { 195 users = append(users, user) 196 197 if len(users) >= 10 { 198 cancel() 199 } 200 201 return nil 202 })). 203 Find(ctx) 204 205 testutil.Expect(t, err, testutil.Be[error](nil)) 206 testutil.Expect(t, len(users), testutil.Be(10)) 207 }) 208 209 t.Run("List all", func(t *testing.T) { 210 users := make([]model.User, 0) 211 212 err := From(model.UserT, IncludeAllRecord()). 213 Scan(&users). 214 Find(ctx) 215 216 testutil.Expect(t, err, testutil.Be[error](nil)) 217 testutil.Expect(t, len(users), testutil.Be(110)) 218 }) 219 220 t.Run("List all limit 10", func(t *testing.T) { 221 users := make([]model.User, 0) 222 223 err := From(model.UserT). 224 Limit(10). 225 Scan(&users). 226 Find(ctx) 227 228 testutil.Expect(t, err, testutil.Be[error](nil)) 229 testutil.Expect(t, len(users), testutil.Be(10)) 230 }) 231 232 t.Run("List all offset limit 10", func(t *testing.T) { 233 users := make([]model.User, 0) 234 235 err := From(model.UserT). 236 Offset(10).Limit(10). 237 Scan(&users). 238 Find(ctx) 239 240 testutil.Expect(t, err, testutil.Be[error](nil)) 241 testutil.Expect(t, len(users), testutil.Be(10)) 242 testutil.Expect(t, users[0].ID > 1, testutil.Be(true)) 243 }) 244 245 t.Run("List desc order by", func(t *testing.T) { 246 users := make([]model.User, 0) 247 248 err := From(model.UserT). 249 OrderBy(sqlbuilder.DescOrder(model.UserT.ID)). 250 Offset(10).Limit(10). 251 Scan(&users). 252 Find(ctx) 253 254 testutil.Expect(t, err, testutil.Be[error](nil)) 255 testutil.Expect(t, len(users), testutil.Be(10)) 256 testutil.Expect(t, users[0].ID > users[1].ID, testutil.Be(true)) 257 }) 258 259 t.Run("List where", func(t *testing.T) { 260 users := make([]model.User, 0) 261 262 err := From(model.UserT). 263 Apply(UserParam{ 264 Age: []int64{10}, 265 }). 266 Scan(&users). 267 Find(ctx) 268 269 testutil.Expect(t, err, testutil.Be[error](nil)) 270 testutil.Expect(t, len(users), testutil.Be(1)) 271 }) 272 273 t.Run("List where with in", func(t *testing.T) { 274 orgUsers := make([]model.OrgUser, 0) 275 276 err := From(model.OrgUserT). 277 Where(model.OrgUserT.UserID.V(InSelect( 278 model.UserT.ID, 279 From(model.UserT).Where(model.UserT.Age.V(sqlbuilder.Eq(int64(10)))), 280 ))). 281 Scan(&orgUsers). 282 Find(ctx) 283 284 testutil.Expect(t, err, testutil.Be[error](nil)) 285 testutil.Expect(t, len(orgUsers), testutil.Be(1)) 286 }) 287 288 t.Run("List where join", func(t *testing.T) { 289 users := make([]struct { 290 model.User 291 Org model.Org 292 }, 0) 293 294 err := From(model.UserT). 295 Join(model.OrgUserT, model.OrgUserT.UserID.V(sqlbuilder.EqCol(model.UserT.ID))). 296 Join(model.OrgT, model.OrgT.ID.V(sqlbuilder.EqCol(model.OrgUserT.OrgID))). 297 Where(model.UserT.Age.V(sqlbuilder.Eq(int64(10)))). 298 Scan(&users). 299 Find(ctx) 300 301 testutil.Expect(t, err, testutil.Be[error](nil)) 302 testutil.Expect(t, len(users), testutil.Be(1)) 303 testutil.Expect(t, users[0].Org.Name, testutil.Not(testutil.Be(""))) 304 }) 305 }) 306 }) 307 } 308 } 309 310 func TestMultipleTxLockedWithSqlite(t *testing.T) { 311 ctx := ContextWithDatabase(t, "sql_test", "") 312 313 t.Run("concurrent insert && query", func(t *testing.T) { 314 usr2 := &model.User{ 315 Name: "test", 316 Nickname: "test", 317 } 318 319 wg := &sync.WaitGroup{} 320 321 for i := 0; i < 2; i++ { 322 wg.Add(1) 323 go func() { 324 defer wg.Done() 325 326 err := Prepare(usr2). 327 OnConflict(model.UserT.I.IName).DoUpdateSet(model.UserT.Nickname). 328 Save(ctx) 329 330 testutil.Expect(t, err, testutil.Be[error](nil)) 331 }() 332 } 333 334 for i := 0; i < 2; i++ { 335 wg.Add(1) 336 go func() { 337 defer wg.Done() 338 339 //err := Tx(ctx, usr2, func(ctx context.Context) error { 340 // return Prepare(usr2). 341 // OnConflict(model.UserT.I.IName).DoUpdateSet(model.UserT.Nickname). 342 // Save(ctx) 343 //}) 344 // 345 //testutil.Expect(t, err, testutil.Be[error](nil)) 346 }() 347 } 348 349 for i := 0; i < 4; i++ { 350 wg.Add(1) 351 go func() { 352 defer wg.Done() 353 354 err := From(model.UserT). 355 Scan(Recv(func(v *model.User) error { 356 return nil 357 })). 358 Find(ctx) 359 testutil.Expect(t, err, testutil.Be[error](nil)) 360 }() 361 } 362 363 wg.Wait() 364 }) 365 } 366 367 func ContextWithDatabase(t testing.TB, name string, endpoint string) context.Context { 368 t.Helper() 369 ctx := testutil.NewContext(t) 370 371 cat := &sqlbuilder.Tables{} 372 cat.Add(model.UserT) 373 cat.Add(model.OrgT) 374 cat.Add(model.OrgUserT) 375 376 db := &Database{ 377 Endpoint: endpoint, 378 EnableMigrate: true, 379 } 380 381 db.ApplyCatalog(name, cat) 382 db.SetDefaults() 383 err := db.Init(ctx) 384 testutil.Expect(t, err, testutil.Be[error](nil)) 385 386 ctx = db.InjectContext(ctx) 387 388 err = db.Run(ctx) 389 testutil.Expect(t, err, testutil.Be[error](nil)) 390 391 t.Cleanup(func() { 392 a := SessionFor(ctx, name).Adapter() 393 394 cat.Range(func(table sqlbuilder.Table, idx int) bool { 395 _, e := a.Exec(ctx, a.Dialect().DropTable(table)) 396 testutil.Expect(t, e, testutil.Be[error](nil)) 397 return true 398 }) 399 400 err := a.Close() 401 testutil.Expect(t, err, testutil.Be[error](nil)) 402 }) 403 404 return ctx 405 }