package repository import ( "context" "testing" "carrotskin/internal/model" "carrotskin/internal/testutil" ) func TestUserRepository_BasicAndPoints(t *testing.T) { db := testutil.NewTestDB(t) repo := NewUserRepository(db) ctx := context.Background() user := &model.User{Username: "u1", Email: "e1@test.com", Password: "pwd", Status: 1} if err := repo.Create(ctx, user); err != nil { t.Fatalf("create user err: %v", err) } if u, err := repo.FindByID(ctx, user.ID); err != nil || u.Username != "u1" { t.Fatalf("FindByID mismatch: %v %+v", err, u) } if u, err := repo.FindByUsername(ctx, "u1"); err != nil || u.Email != "e1@test.com" { t.Fatalf("FindByUsername mismatch") } if u, err := repo.FindByEmail(ctx, "e1@test.com"); err != nil || u.ID != user.ID { t.Fatalf("FindByEmail mismatch") } if err := repo.UpdateFields(ctx, user.ID, map[string]interface{}{"avatar": "a.png"}); err != nil { t.Fatalf("UpdateFields err: %v", err) } if _, err := repo.BatchUpdate(ctx, []int64{user.ID}, map[string]interface{}{"status": 2}); err != nil { t.Fatalf("BatchUpdate err: %v", err) } // 积分增加 if err := repo.UpdatePoints(ctx, user.ID, 10, "add", "bonus"); err != nil { t.Fatalf("UpdatePoints add err: %v", err) } // 积分不足场景 if err := repo.UpdatePoints(ctx, user.ID, -100, "sub", "penalty"); err == nil { t.Fatalf("expected insufficient points error") } if list, err := repo.FindByIDs(ctx, []int64{user.ID}); err != nil || len(list) != 1 { t.Fatalf("FindByIDs mismatch: %v %d", err, len(list)) } if list, err := repo.FindByIDs(ctx, []int64{}); err != nil || len(list) != 0 { t.Fatalf("FindByIDs empty mismatch: %v %d", err, len(list)) } // 软删除 if err := repo.Delete(ctx, user.ID); err != nil { t.Fatalf("Delete err: %v", err) } deleted, _ := repo.FindByID(ctx, user.ID) if deleted != nil { t.Fatalf("expected deleted user filtered out") } // 批量操作边界 if _, err := repo.BatchUpdate(ctx, []int64{}, map[string]interface{}{"status": 1}); err != nil { t.Fatalf("BatchUpdate empty should not error: %v", err) } if _, err := repo.BatchDelete(ctx, []int64{}); err != nil { t.Fatalf("BatchDelete empty should not error: %v", err) } // 日志写入 _ = repo.CreateLoginLog(ctx, &model.UserLoginLog{UserID: user.ID, IPAddress: "127.0.0.1"}) _ = repo.CreatePointLog(ctx, &model.UserPointLog{UserID: user.ID, Amount: 1, ChangeType: "add"}) } func TestProfileRepository_Basic(t *testing.T) { db := testutil.NewTestDB(t) userRepo := NewUserRepository(db) profileRepo := NewProfileRepository(db) ctx := context.Background() u := &model.User{Username: "u2", Email: "u2@test.com", Password: "pwd", Status: 1} _ = userRepo.Create(ctx, u) p := &model.Profile{UUID: "p-uuid", UserID: u.ID, Name: "hero"} if err := profileRepo.Create(ctx, p); err != nil { t.Fatalf("create profile err: %v", err) } if got, err := profileRepo.FindByUUID(ctx, "p-uuid"); err != nil || got.Name != "hero" { t.Fatalf("FindByUUID mismatch: %v %+v", err, got) } if list, err := profileRepo.FindByUserID(ctx, u.ID); err != nil || len(list) != 1 { t.Fatalf("FindByUserID mismatch") } if count, err := profileRepo.CountByUserID(ctx, u.ID); err != nil || count != 1 { t.Fatalf("CountByUserID mismatch: %d err=%v", count, err) } if err := profileRepo.UpdateLastUsedAt(ctx, "p-uuid"); err != nil { t.Fatalf("UpdateLastUsedAt err: %v", err) } if got, err := profileRepo.FindByName(ctx, "hero"); err != nil || got == nil { t.Fatalf("FindByName mismatch") } if list, err := profileRepo.FindByUUIDs(ctx, []string{"p-uuid"}); err != nil || len(list) != 1 { t.Fatalf("FindByUUIDs mismatch") } if _, err := profileRepo.BatchUpdate(ctx, []string{"p-uuid"}, map[string]interface{}{"name": "hero2"}); err != nil { t.Fatalf("BatchUpdate profile err: %v", err) } if err := profileRepo.Delete(ctx, "p-uuid"); err != nil { t.Fatalf("Delete err: %v", err) } if _, err := profileRepo.BatchDelete(ctx, []string{}); err != nil { t.Fatalf("BatchDelete empty err: %v", err) } } func TestTextureRepository_Basic(t *testing.T) { db := testutil.NewTestDB(t) userRepo := NewUserRepository(db) textureRepo := NewTextureRepository(db) ctx := context.Background() u := &model.User{Username: "u3", Email: "u3@test.com", Password: "pwd", Status: 1} _ = userRepo.Create(ctx, u) tex := &model.Texture{ UploaderID: u.ID, Name: "tex", Hash: "hash1", URL: "url1", Type: model.TextureTypeSkin, IsPublic: true, Status: 1, } if err := textureRepo.Create(ctx, tex); err != nil { t.Fatalf("create texture err: %v", err) } if got, _ := textureRepo.FindByHash(ctx, "hash1"); got == nil || got.ID != tex.ID { t.Fatalf("FindByHash mismatch") } if got, _ := textureRepo.FindByHashAndUploaderID(ctx, "hash1", u.ID); got == nil { t.Fatalf("FindByHashAndUploaderID mismatch") } _ = textureRepo.IncrementFavoriteCount(ctx, tex.ID) _ = textureRepo.DecrementFavoriteCount(ctx, tex.ID) _ = textureRepo.IncrementDownloadCount(ctx, tex.ID) _ = textureRepo.CreateDownloadLog(ctx, &model.TextureDownloadLog{TextureID: tex.ID, UserID: &u.ID, IPAddress: "127.0.0.1"}) // 收藏 _ = textureRepo.AddFavorite(ctx, u.ID, tex.ID) if fav, err := textureRepo.IsFavorited(ctx, u.ID, tex.ID); err == nil { if !fav { t.Fatalf("IsFavorited expected true") } } else { t.Skipf("IsFavorited not supported by sqlite: %v", err) } _ = textureRepo.RemoveFavorite(ctx, u.ID, tex.ID) // 批量更新与删除 if affected, err := textureRepo.BatchUpdate(ctx, []int64{tex.ID}, map[string]interface{}{"name": "tex-new"}); err != nil || affected != 1 { t.Fatalf("BatchUpdate mismatch, affected=%d err=%v", affected, err) } if affected, err := textureRepo.BatchDelete(ctx, []int64{tex.ID}); err != nil || affected != 1 { t.Fatalf("BatchDelete mismatch, affected=%d err=%v", affected, err) } // 搜索与收藏列表 _ = textureRepo.Create(ctx, &model.Texture{ UploaderID: u.ID, Name: "search-me", Hash: "hash2", URL: "url2", Type: model.TextureTypeCape, IsPublic: true, Status: 1, }) if list, total, err := textureRepo.Search(ctx, "search", model.TextureTypeCape, true, 1, 10); err != nil || total == 0 || len(list) == 0 { t.Fatalf("Search mismatch, total=%d len=%d err=%v", total, len(list), err) } _ = textureRepo.AddFavorite(ctx, u.ID, tex.ID+1) if favList, total, err := textureRepo.GetUserFavorites(ctx, u.ID, 1, 10); err != nil || total == 0 || len(favList) == 0 { t.Fatalf("GetUserFavorites mismatch, total=%d len=%d err=%v", total, len(favList), err) } if _, total, err := textureRepo.Search(ctx, "", model.TextureTypeSkin, true, 1, 10); err != nil || total < 2 { t.Fatalf("Search fallback mismatch") } // 列表与计数 if _, total, err := textureRepo.FindByUploaderID(ctx, u.ID, 1, 10); err != nil || total != 1 { t.Fatalf("FindByUploaderID mismatch") } if cnt, err := textureRepo.CountByUploaderID(ctx, u.ID); err != nil || cnt != 1 { t.Fatalf("CountByUploaderID mismatch") } _ = textureRepo.Delete(ctx, tex.ID) } func TestClientRepository_Basic(t *testing.T) { db := testutil.NewTestDB(t) repo := NewClientRepository(db) ctx := context.Background() client := &model.Client{UUID: "c-uuid", ClientToken: "ct-1", UserID: 9, Version: 1} if err := repo.Create(ctx, client); err != nil { t.Fatalf("Create client err: %v", err) } if got, _ := repo.FindByClientToken(ctx, "ct-1"); got == nil || got.UUID != "c-uuid" { t.Fatalf("FindByClientToken mismatch") } if got, _ := repo.FindByUUID(ctx, "c-uuid"); got == nil || got.ClientToken != "ct-1" { t.Fatalf("FindByUUID mismatch") } if list, _ := repo.FindByUserID(ctx, 9); len(list) != 1 { t.Fatalf("FindByUserID mismatch") } _ = repo.IncrementVersion(ctx, "c-uuid") updated, _ := repo.FindByUUID(ctx, "c-uuid") if updated.Version != 2 { t.Fatalf("IncrementVersion not applied, got %d", updated.Version) } _ = repo.DeleteByClientToken(ctx, "ct-1") _ = repo.DeleteByUserID(ctx, 9) } func TestYggdrasilRepository_Basic(t *testing.T) { db := testutil.NewTestDB(t) userRepo := NewUserRepository(db) yggRepo := NewYggdrasilRepository(db) ctx := context.Background() user := &model.User{Username: "u-ygg", Email: "ygg@test.com", Password: "pwd", Status: 1} _ = userRepo.Create(ctx, user) // AfterCreate 会生成 yggdrasil 记录 pwd, err := yggRepo.GetPasswordByID(ctx, user.ID) if err != nil || pwd == "" { t.Fatalf("GetPasswordByID err=%v pwd=%s", err, pwd) } if err := yggRepo.ResetPassword(ctx, user.ID, "newpwd"); err != nil { t.Fatalf("ResetPassword err: %v", err) } }