131 lines
4.7 KiB
Go
131 lines
4.7 KiB
Go
package memory
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
|
|
"telesrv/internal/domain"
|
|
)
|
|
|
|
func albumIntent(label string) []byte {
|
|
sum := sha256.Sum256([]byte(label))
|
|
return sum[:]
|
|
}
|
|
|
|
func albumReq(sender int64, peer domain.Peer, groupedID int64, items ...domain.AlbumGroupReservationItem) domain.AlbumGroupReservationRequest {
|
|
return domain.AlbumGroupReservationRequest{
|
|
SenderUserID: sender,
|
|
Peer: peer,
|
|
Items: items,
|
|
ProposedGroupedID: groupedID,
|
|
}
|
|
}
|
|
|
|
func albumItem(randomID int64, label string) domain.AlbumGroupReservationItem {
|
|
return domain.AlbumGroupReservationItem{RandomID: randomID, IntentHash: albumIntent(label)}
|
|
}
|
|
|
|
func TestAlbumGroupReservationFullThenSubsetAndIntentConflict(t *testing.T) {
|
|
ctx := context.Background()
|
|
messages := NewMessageStore()
|
|
peer := domain.Peer{Type: domain.PeerTypeUser, ID: 2002}
|
|
full := []domain.AlbumGroupReservationItem{albumItem(1, "one"), albumItem(2, "two"), albumItem(3, "three")}
|
|
|
|
groupedID, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 101, full...))
|
|
if err != nil || groupedID != 101 {
|
|
t.Fatalf("reserve full = %d err=%v, want 101", groupedID, err)
|
|
}
|
|
replayed, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 202, full[1:]...))
|
|
if err != nil || replayed != groupedID {
|
|
t.Fatalf("reserve subset = %d err=%v, want original %d", replayed, err, groupedID)
|
|
}
|
|
for _, item := range full {
|
|
got, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 303, item))
|
|
if err != nil || got != groupedID {
|
|
t.Fatalf("single random_id %d = %d err=%v, want %d", item.RandomID, got, err, groupedID)
|
|
}
|
|
}
|
|
changed := albumItem(2, "changed payload")
|
|
if _, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 404, changed)); !errors.Is(err, domain.ErrMessageRandomIDDuplicate) {
|
|
t.Fatalf("changed intent err=%v, want ErrMessageRandomIDDuplicate", err)
|
|
}
|
|
}
|
|
|
|
func TestAlbumGroupReservationConcurrentOverlapConverges(t *testing.T) {
|
|
ctx := context.Background()
|
|
messages := NewMessageStore()
|
|
peer := domain.Peer{Type: domain.PeerTypeChannel, ID: 9001}
|
|
requests := []domain.AlbumGroupReservationRequest{
|
|
albumReq(1001, peer, 111, albumItem(11, "one"), albumItem(12, "shared")),
|
|
albumReq(1001, peer, 222, albumItem(12, "shared"), albumItem(13, "three")),
|
|
}
|
|
results := make([]int64, 2)
|
|
errs := make([]error, 2)
|
|
start := make(chan struct{})
|
|
var wg sync.WaitGroup
|
|
for i := range requests {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
<-start
|
|
results[i], errs[i] = messages.ReserveAlbumGroup(ctx, requests[i])
|
|
}(i)
|
|
}
|
|
close(start)
|
|
wg.Wait()
|
|
if errs[0] != nil || errs[1] != nil || results[0] == 0 || results[0] != results[1] {
|
|
t.Fatalf("concurrent results=%v errs=%v, want same non-zero group", results, errs)
|
|
}
|
|
for _, item := range []domain.AlbumGroupReservationItem{albumItem(11, "one"), albumItem(12, "shared"), albumItem(13, "three")} {
|
|
got, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 333, item))
|
|
if err != nil || got != results[0] {
|
|
t.Fatalf("converged random_id %d = %d err=%v, want %d", item.RandomID, got, err, results[0])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAlbumGroupReservationRejectsMixedOldGroupsAtomically(t *testing.T) {
|
|
ctx := context.Background()
|
|
messages := NewMessageStore()
|
|
peer := domain.Peer{Type: domain.PeerTypeUser, ID: 2002}
|
|
one := albumItem(21, "one")
|
|
two := albumItem(22, "two")
|
|
three := albumItem(23, "three")
|
|
if _, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 121, one)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 122, two)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 123, one, two, three)); !errors.Is(err, domain.ErrMessageRandomIDDuplicate) {
|
|
t.Fatalf("mixed old groups err=%v, want ErrMessageRandomIDDuplicate", err)
|
|
}
|
|
// 失败批次不能把尚未存在的 random_id 23 偷绑到任一旧组。
|
|
got, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, peer, 124, three))
|
|
if err != nil || got != 124 {
|
|
t.Fatalf("post-conflict unbound item = %d err=%v, want fresh 124", got, err)
|
|
}
|
|
}
|
|
|
|
func TestAlbumGroupReservationScopeIncludesPeer(t *testing.T) {
|
|
ctx := context.Background()
|
|
messages := NewMessageStore()
|
|
item := albumItem(31, "same intent")
|
|
tests := []struct {
|
|
peer domain.Peer
|
|
group int64
|
|
}{
|
|
{peer: domain.Peer{Type: domain.PeerTypeUser, ID: 2002}, group: 131},
|
|
{peer: domain.Peer{Type: domain.PeerTypeUser, ID: 2003}, group: 132},
|
|
{peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 2002}, group: 133},
|
|
}
|
|
for _, tc := range tests {
|
|
got, err := messages.ReserveAlbumGroup(ctx, albumReq(1001, tc.peer, tc.group, item))
|
|
if err != nil || got != tc.group {
|
|
t.Fatalf("peer %+v = %d err=%v, want isolated %d", tc.peer, got, err, tc.group)
|
|
}
|
|
}
|
|
}
|