0904
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
// Copyright 2026 tamakyi. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tamakyi/TamaBox/internal/dbutil"
|
||||
)
|
||||
|
||||
func TestVotes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, cleanup := newTestDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
store := NewVotesStore(db)
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
test func(t *testing.T, ctx context.Context, db *votes)
|
||||
}{
|
||||
{"Create", testVotesCreate},
|
||||
{"GetByID", testVotesGetByID},
|
||||
{"GetByToken", testVotesGetByToken},
|
||||
{"ListByUserID", testVotesListByUserID},
|
||||
{"Update", testVotesUpdate},
|
||||
{"UpdateStatus", testVotesUpdateStatus},
|
||||
{"DeleteByID", testVotesDeleteByID},
|
||||
{"CountByUserID", testVotesCountByUserID},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
if err := cleanup("votes"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
tc.test(t, ctx, store.(*votes))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testVotesCreate(t *testing.T, ctx context.Context, db *votes) {
|
||||
got, err := db.Create(ctx, CreateVoteOptions{
|
||||
UserID: 1,
|
||||
Title: "今天穿什么?",
|
||||
Description: "单选题",
|
||||
Type: VoteTypeSingle,
|
||||
Visibility: VoteVisibilityPublic,
|
||||
VoterScope: VoteVoterScopeAnyone,
|
||||
ShowResult: VoteShowResultAfterVote,
|
||||
MaxChoices: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, uint(1), got.ID)
|
||||
require.NotEmpty(t, got.LinkToken, "LinkToken 应自动生成")
|
||||
require.Equal(t, VoteStatusDraft, got.Status, "默认状态应为草稿")
|
||||
// RequireCaptcha 不再被 gorm:"default:true" 隐式默认(零值陷阱已修复):Create 未显式传入时按 false 落库。
|
||||
// 业务层在创建/编辑投票时始终强制 requireCaptcha=true(决策 #3),真实投票行为不受影响。
|
||||
require.False(t, got.RequireCaptcha, "底层 Create 不再隐式默认开启验证码")
|
||||
}
|
||||
|
||||
func testVotesGetByID(t *testing.T, ctx context.Context, db *votes) {
|
||||
_, err := db.Create(ctx, CreateVoteOptions{
|
||||
UserID: 1,
|
||||
Title: "Title - 1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
got, err := db.GetByID(ctx, 1)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "Title - 1", got.Title)
|
||||
require.Equal(t, uint(1), got.UserID)
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
_, err := db.GetByID(ctx, 404)
|
||||
require.Equal(t, ErrVoteNotExist, err)
|
||||
})
|
||||
}
|
||||
|
||||
func testVotesGetByToken(t *testing.T, ctx context.Context, db *votes) {
|
||||
created, err := db.Create(ctx, CreateVoteOptions{
|
||||
UserID: 1,
|
||||
Title: "Title - 1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
got, err := db.GetByToken(ctx, created.LinkToken)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, created.ID, got.ID)
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
_, err := db.GetByToken(ctx, "no-such-token")
|
||||
require.Equal(t, ErrVoteNotExist, err)
|
||||
})
|
||||
}
|
||||
|
||||
func testVotesListByUserID(t *testing.T, ctx context.Context, db *votes) {
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err := db.Create(ctx, CreateVoteOptions{
|
||||
UserID: 1,
|
||||
Title: "Vote - 1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
}
|
||||
_, err := db.Create(ctx, CreateVoteOptions{
|
||||
UserID: 2,
|
||||
Title: "Vote - 2",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("all", func(t *testing.T) {
|
||||
got, err := db.ListByUserID(ctx, 1, ListVotesOptions{})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, got, 3)
|
||||
})
|
||||
|
||||
t.Run("status filter", func(t *testing.T) {
|
||||
err := db.UpdateStatus(ctx, 1, VoteStatusPublished)
|
||||
require.Nil(t, err)
|
||||
|
||||
got, err := db.ListByUserID(ctx, 1, ListVotesOptions{Status: VoteStatusPublished})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, got, 1)
|
||||
require.Equal(t, uint(1), got[0].ID)
|
||||
})
|
||||
|
||||
t.Run("cursor", func(t *testing.T) {
|
||||
got, err := db.ListByUserID(ctx, 1, ListVotesOptions{
|
||||
Cursor: &dbutil.Cursor{Value: 3, PageSize: 10},
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, got, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func testVotesUpdate(t *testing.T, ctx context.Context, db *votes) {
|
||||
_, err := db.Create(ctx, CreateVoteOptions{
|
||||
UserID: 1,
|
||||
Title: "Title - 1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
vt := VoteTypeMultiple
|
||||
mc := 3
|
||||
err := db.Update(ctx, 1, UpdateVoteOptions{
|
||||
Title: "Title - updated",
|
||||
Description: "desc",
|
||||
Type: &vt,
|
||||
Visibility: VoteVisibilityLinkOnly,
|
||||
VoterScope: VoteVoterScopeRegisteredOnly,
|
||||
ShowResult: VoteShowResultAfterEnd,
|
||||
MaxChoices: &mc,
|
||||
RequireCaptcha: false,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
got, err := db.GetByID(ctx, 1)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "Title - updated", got.Title)
|
||||
require.Equal(t, VoteTypeMultiple, got.Type)
|
||||
require.Equal(t, VoteVisibilityLinkOnly, got.Visibility)
|
||||
require.Equal(t, VoteVoterScopeRegisteredOnly, got.VoterScope)
|
||||
require.Equal(t, VoteShowResultAfterEnd, got.ShowResult)
|
||||
require.Equal(t, 3, got.MaxChoices)
|
||||
require.False(t, got.RequireCaptcha)
|
||||
})
|
||||
|
||||
// 回归:调用方漏传 Type/MaxChoices(nil 指针)时,db.Update 必须跳过写入,
|
||||
// 不得把 type 写成 ""(曾导致发布后编辑使公开页报「未知的投票类型」)。
|
||||
t.Run("omit type preserves existing", func(t *testing.T) {
|
||||
err := db.Update(ctx, 1, UpdateVoteOptions{Title: "Title - meta only"})
|
||||
require.Nil(t, err)
|
||||
|
||||
got, err := db.GetByID(ctx, 1)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "Title - meta only", got.Title)
|
||||
require.Equal(t, VoteTypeMultiple, got.Type, "type 不应被清零")
|
||||
require.Equal(t, 3, got.MaxChoices, "max_choices 不应被清零")
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
err := db.Update(ctx, 404, UpdateVoteOptions{Title: "x"})
|
||||
require.Equal(t, ErrVoteNotExist, err)
|
||||
})
|
||||
}
|
||||
|
||||
func testVotesUpdateStatus(t *testing.T, ctx context.Context, db *votes) {
|
||||
_, err := db.Create(ctx, CreateVoteOptions{
|
||||
UserID: 1,
|
||||
Title: "Title - 1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
require.Nil(t, db.UpdateStatus(ctx, 1, VoteStatusPublished))
|
||||
got, err := db.GetByID(ctx, 1)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, VoteStatusPublished, got.Status)
|
||||
|
||||
require.Nil(t, db.UpdateStatus(ctx, 1, VoteStatusEnded))
|
||||
got, err = db.GetByID(ctx, 1)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, VoteStatusEnded, got.Status)
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
require.Equal(t, ErrVoteNotExist, db.UpdateStatus(ctx, 404, VoteStatusEnded))
|
||||
})
|
||||
}
|
||||
|
||||
func testVotesDeleteByID(t *testing.T, ctx context.Context, db *votes) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
_, err := db.Create(ctx, CreateVoteOptions{UserID: 1, Title: "Title - 1"})
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Nil(t, db.DeleteByID(ctx, 1))
|
||||
_, err = db.GetByID(ctx, 1)
|
||||
require.Equal(t, ErrVoteNotExist, err)
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
require.Equal(t, ErrVoteNotExist, db.DeleteByID(ctx, 404))
|
||||
})
|
||||
}
|
||||
|
||||
func testVotesCountByUserID(t *testing.T, ctx context.Context, db *votes) {
|
||||
_, err := db.Create(ctx, CreateVoteOptions{UserID: 1, Title: "A"})
|
||||
require.Nil(t, err)
|
||||
_, err = db.Create(ctx, CreateVoteOptions{UserID: 1, Title: "B"})
|
||||
require.Nil(t, err)
|
||||
_, err = db.Create(ctx, CreateVoteOptions{UserID: 2, Title: "C"})
|
||||
require.Nil(t, err)
|
||||
|
||||
got, err := db.CountByUserID(ctx, 1)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(2), got)
|
||||
|
||||
got, err = db.CountByUserID(ctx, 404)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(0), got)
|
||||
}
|
||||
Reference in New Issue
Block a user