88 lines
3.0 KiB
Go
88 lines
3.0 KiB
Go
// 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"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestReports 覆盖举报写入的幂等性与两种计数口径。
|
|
//
|
|
// 幂等必须由唯一索引 + isDuplicateKeyError 兜住:如果改成「先查后插」,
|
|
// 并发下仍会撞索引报 500;而如果直接把冲突当错误返回,举报按钮会变成
|
|
// 「这条内容是否存在」的探测接口(第二次举报报错 = 内容存在)。
|
|
func TestReports(t *testing.T) {
|
|
database, cleanup := newTestDB(t)
|
|
ctx := context.Background()
|
|
|
|
t.Cleanup(func() {
|
|
if err := cleanup("users", "questions", "content_reports"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
|
|
usersStore := NewUsersStore(database)
|
|
questionsStore := NewQuestionsStore(database)
|
|
reportsStore := NewReportsStore(database)
|
|
|
|
require.NoError(t, usersStore.Create(ctx, CreateUserOptions{
|
|
Name: "Box", Password: "super_secret", Email: "[email protected]", Domain: "rpbox",
|
|
}))
|
|
owner, err := usersStore.GetByDomain(ctx, "rpbox")
|
|
require.NoError(t, err)
|
|
|
|
question, err := questionsStore.Create(ctx, CreateQuestionOptions{
|
|
FromIP: "1.1.1.1", UserID: owner.ID, Content: "被举报的内容",
|
|
})
|
|
require.NoError(t, err)
|
|
require.NoError(t, questionsStore.AnswerByID(ctx, question.ID, "回答"))
|
|
|
|
// 首次举报写入成功。
|
|
created, err := reportsStore.Create(ctx, CreateReportOptions{
|
|
QuestionID: question.ID, ReporterIP: "2.2.2.2", Reason: ReportReasonHarassment,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, created)
|
|
|
|
// 同 IP 再报一次:幂等成功(不报错、不重复计数)。
|
|
created, err = reportsStore.Create(ctx, CreateReportOptions{
|
|
QuestionID: question.ID, ReporterIP: "2.2.2.2", Reason: ReportReasonDoxxing,
|
|
})
|
|
require.NoError(t, err)
|
|
require.False(t, created, "duplicate (question_id, reporter_ip) must be idempotent")
|
|
|
|
// 另一个 IP 正常计入。
|
|
created, err = reportsStore.Create(ctx, CreateReportOptions{
|
|
QuestionID: question.ID, ReporterIP: "3.3.3.3", Reason: ReportReasonDivisive,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, created)
|
|
|
|
counts, err := reportsStore.QuestionIDsByUser(ctx, owner.ID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, map[uint]int64{question.ID: 2}, counts)
|
|
|
|
count24, err := reportsStore.CountByUserSince(ctx, owner.ID, time.Now().Add(-24*time.Hour))
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, 2, count24)
|
|
|
|
// 窗口之外(未来时间点起算)应为 0,保证告警不会用错时间范围。
|
|
countFuture, err := reportsStore.CountByUserSince(ctx, owner.ID, time.Now().Add(time.Hour))
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, 0, countFuture)
|
|
|
|
// 已驳回的不计入箱主侧标记与告警。
|
|
require.NoError(t, database.Model(&ContentReport{}).
|
|
Where("question_id = ?", question.ID).
|
|
Update("status", ReportStatusRejected).Error)
|
|
counts, err = reportsStore.QuestionIDsByUser(ctx, owner.ID)
|
|
require.NoError(t, err)
|
|
require.Empty(t, counts)
|
|
}
|