86 lines
3.2 KiB
Go
86 lines
3.2 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"telesrv/internal/domain"
|
|
)
|
|
|
|
func TestClaimOfficialUsernameDisplacesOrdinaryUserAtomically(t *testing.T) {
|
|
pool := testPool(t)
|
|
ctx := context.Background()
|
|
users := NewUserStore(pool)
|
|
|
|
officialBefore, found, err := users.ByID(ctx, domain.OfficialSystemUserID)
|
|
if err != nil || !found {
|
|
t.Fatalf("load official user: found=%v err=%v", found, err)
|
|
}
|
|
suffix := time.Now().UnixNano()
|
|
target := fmt.Sprintf("brand_%d", suffix)
|
|
holder := createTestUser(t, ctx, users, fmt.Sprintf("+18881%d", suffix), "Brand", "Holder")
|
|
t.Cleanup(func() {
|
|
_, _ = users.ClaimOfficialUsername(ctx, officialBefore.Username)
|
|
_, _ = pool.Exec(ctx, `DELETE FROM users WHERE id = $1`, holder.ID)
|
|
})
|
|
if _, err := users.UpdateUsername(ctx, holder.ID, target); err != nil {
|
|
t.Fatalf("occupy target username: %v", err)
|
|
}
|
|
|
|
claim, err := users.ClaimOfficialUsername(ctx, target)
|
|
if err != nil {
|
|
t.Fatalf("claim official username: %v", err)
|
|
}
|
|
if !claim.Changed || claim.DisplacedUserID != holder.ID || claim.Official.ID != domain.OfficialSystemUserID || claim.Official.Username != target {
|
|
t.Fatalf("claim = %+v, want changed claim displacing %d", claim, holder.ID)
|
|
}
|
|
displaced, found, err := users.ByID(ctx, holder.ID)
|
|
if err != nil || !found || displaced.Username != "" {
|
|
t.Fatalf("displaced user = %+v found=%v err=%v, want empty username", displaced, found, err)
|
|
}
|
|
resolved, found, err := users.ByUsername(ctx, target)
|
|
if err != nil || !found || resolved.ID != domain.OfficialSystemUserID {
|
|
t.Fatalf("resolve claimed username = %+v found=%v err=%v", resolved, found, err)
|
|
}
|
|
owner, found, err := getPeerUsernameOwner(ctx, pool, target, false)
|
|
if err != nil || !found || !owner.matches(peerUsernameTypeUser, domain.OfficialSystemUserID) || !owner.editable || owner.collectible {
|
|
t.Fatalf("registry owner = %+v found=%v err=%v", owner, found, err)
|
|
}
|
|
|
|
again, err := users.ClaimOfficialUsername(ctx, target)
|
|
if err != nil {
|
|
t.Fatalf("repeat official username claim: %v", err)
|
|
}
|
|
if again.Changed || again.DisplacedUserID != 0 || again.Official.Username != target {
|
|
t.Fatalf("repeat claim = %+v, want idempotent no-op", again)
|
|
}
|
|
}
|
|
|
|
func TestClaimOfficialUsernameDoesNotDisplaceBot(t *testing.T) {
|
|
pool := testPool(t)
|
|
ctx := context.Background()
|
|
users := NewUserStore(pool)
|
|
suffix := time.Now().UnixNano()
|
|
target := fmt.Sprintf("botbrand_%d", suffix)
|
|
holder := createTestUser(t, ctx, users, fmt.Sprintf("+18882%d", suffix), "Protected", "Bot")
|
|
t.Cleanup(func() {
|
|
_, _ = pool.Exec(ctx, `DELETE FROM users WHERE id = $1`, holder.ID)
|
|
})
|
|
if _, err := users.UpdateUsername(ctx, holder.ID, target); err != nil {
|
|
t.Fatalf("occupy target username: %v", err)
|
|
}
|
|
if _, err := pool.Exec(ctx, `UPDATE users SET is_bot = true WHERE id = $1`, holder.ID); err != nil {
|
|
t.Fatalf("mark protected holder as bot: %v", err)
|
|
}
|
|
|
|
if _, err := users.ClaimOfficialUsername(ctx, target); !errors.Is(err, domain.ErrUsernameOccupied) {
|
|
t.Fatalf("claim bot username error = %v, want ErrUsernameOccupied", err)
|
|
}
|
|
protected, found, err := users.ByID(ctx, holder.ID)
|
|
if err != nil || !found || protected.Username != target {
|
|
t.Fatalf("protected holder = %+v found=%v err=%v, want username unchanged", protected, found, err)
|
|
}
|
|
}
|