Files
olons_server/internal/store/portal_notice_integration_test.go
T
richjohnrambo 8ce67648b1
Deploy olons-server production / deploy (push) Successful in 3m12s
feat: add global system notices and Beijing order time display
2026-10-22 21:56:07 +08:00

86 lines
3.2 KiB
Go

package store
import (
"context"
"errors"
"os"
"testing"
"time"
"github.com/jmoiron/sqlx"
)
func TestPendingNoticeAudienceAndReadAcknowledgement(t *testing.T) {
dsn := os.Getenv("OLONS_TEST_MYSQL_DSN")
if dsn == "" {
t.Skip("set OLONS_TEST_MYSQL_DSN to run the notice integration test")
}
db, err := sqlx.Connect("mysql", dsn)
if err != nil {
t.Fatal(err)
}
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(1)
t.Cleanup(func() { _ = db.Close() })
// Connection-local temporary tables shadow the real tables without changing application data.
for _, statement := range []string{
`CREATE TEMPORARY TABLE hg_app_member_notice (
id BIGINT PRIMARY KEY, tenant_id BIGINT, member_id BIGINT,
title VARCHAR(120), content TEXT, source_type INT, notice_status INT,
start_at DATETIME(3), end_at DATETIME(3), created_at DATETIME(3))`,
`CREATE TEMPORARY TABLE hg_app_member_notice_read (
id BIGINT PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT, notice_id BIGINT,
member_id BIGINT, read_at DATETIME(3), created_at DATETIME(3),
UNIQUE KEY (tenant_id, notice_id, member_id))`,
} {
if _, err := db.Exec(statement); err != nil {
t.Fatal(err)
}
}
now := time.Now()
for _, notice := range []struct {
id, tenantID, memberID int64
status int
start, end time.Time
}{
{1, 0, 9, 1, now.Add(-2 * time.Hour), now.Add(time.Hour)},
{2, 0, 9, 1, now.Add(-time.Hour), now.Add(time.Hour)},
{3, 0, 10, 1, now.Add(-time.Hour), now.Add(time.Hour)},
{4, 1, 9, 1, now.Add(-time.Hour), now.Add(time.Hour)},
{5, 0, 0, 1, now.Add(-time.Hour), now.Add(time.Hour)},
{6, 0, 9, 2, now.Add(-time.Hour), now.Add(time.Hour)},
{7, 0, 9, 1, now.Add(-2 * time.Hour), now.Add(-time.Hour)},
{8, 0, 9, 1, now.Add(time.Hour), now.Add(2 * time.Hour)},
} {
_, err := db.Exec(`INSERT INTO hg_app_member_notice
(id,tenant_id,member_id,title,content,source_type,notice_status,start_at,end_at,created_at)
VALUES (?,?,?,'系统通知','通知内容',1,?,?,?,?)`, notice.id, notice.tenantID, notice.memberID,
notice.status, formatNoticeBusinessTime(notice.start), formatNoticeBusinessTime(notice.end), formatNoticeBusinessTime(now))
if err != nil {
t.Fatal(err)
}
}
ctx := context.Background()
s := &Store{db: db}
for _, expectedID := range []int64{1, 2} {
notice, err := s.NextUnreadMemberNotice(ctx, 0, 9)
if err != nil || notice == nil || notice.ID != expectedID {
t.Fatalf("pending notice = %+v, error = %v, want id %d", notice, err, expectedID)
}
if err := s.MarkMemberNoticeRead(ctx, 0, 9, expectedID); err != nil {
t.Fatal(err)
}
}
if notice, err := s.NextUnreadMemberNotice(ctx, 0, 9); err != nil || notice != nil {
t.Fatalf("read, expired, revoked, foreign and broadcast notices must not pop up: %+v, %v", notice, err)
}
for _, forbiddenID := range []int64{3, 4} {
if err := s.MarkMemberNoticeRead(ctx, 0, 9, forbiddenID); !errors.Is(err, ErrResourceNotFound) {
t.Fatalf("reading foreign notice %d: error = %v", forbiddenID, err)
}
}
if notice, err := s.NextUnreadMemberNotice(ctx, 0, 10); err != nil || notice == nil || notice.ID != 3 {
t.Fatalf("other recipient must retain their unread notice: %+v, %v", notice, err)
}
}