owpengram-server/internal/store/postgres/official_username_claim_integration_test.go
2026-09-01 12:06:31 +03:00

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)
}
}