Ctrl K
ComunidadGuías…

internal/domain/account_rating_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package domain

import (
	"testing"
	"time"
)

// TestAccountRatingLevelScale pins the server-local ladder: 100 levels, the
// top one at 250k score, and every threshold strictly increasing.
func TestAccountRatingLevelScale(t *testing.T) {
	if MaxAccountRatingLevel != 100 {
		t.Fatalf("MaxAccountRatingLevel = %d, want 100 (the configured local ceiling)", MaxAccountRatingLevel)
	}
	top := AccountRatingLevelThreshold(MaxAccountRatingLevel)
	if top != 250_000 {
		t.Fatalf("top level threshold = %d, want 250000", top)
	}
	previous := int64(0)
	for level := 1; level <= MaxAccountRatingLevel; level++ {
		threshold := AccountRatingLevelThreshold(level)
		if threshold <= previous {
			t.Fatalf("level %d threshold = %d, want above the previous %d", level, threshold, previous)
		}
		previous = threshold
	}
	// Beyond the top the curve is clamped rather than extrapolated: a level outside the
	// local policy must never leak into the wire projection.
	if got := AccountRatingLevelThreshold(MaxAccountRatingLevel + 10); got != top {
		t.Fatalf("over-max threshold = %d, want the clamped %d", got, top)
	}
	if got := AccountRatingLevelThreshold(0); got != 0 {
		t.Fatalf("level 0 threshold = %d, want 0", got)
	}
}

// TestAccountRatingLevelForStarsBoundary pins the exact-threshold behaviour the
// progress bar depends on: a score that lands on a threshold is already on that
// level, and the pair (current, next) always brackets it.
func TestAccountRatingLevelForStarsBoundary(t *testing.T) {
	cases := []struct {
		stars         int64
		level         int
		current, next int64
		hasNext       bool
	}{
		{stars: 0, level: 0, current: 0, next: 25, hasNext: true},
		{stars: 24, level: 0, current: 0, next: 25, hasNext: true},
		{stars: 25, level: 1, current: 25, next: 100, hasNext: true},
		{stars: 99, level: 1, current: 25, next: 100, hasNext: true},
		{stars: 100, level: 2, current: 100, next: 225, hasNext: true},
		{stars: 2_500, level: 10, current: 2_500, next: 3_025, hasNext: true},
		{stars: 10_000, level: 20, current: 10_000, next: 11_025, hasNext: true},
		{stars: 62_500, level: 50, current: 62_500, next: 65_025, hasNext: true},
		{stars: 249_999, level: 99, current: 245_025, next: 250_000, hasNext: true},
		{stars: 250_000, level: 100, current: 250_000, next: 0, hasNext: false},
		{stars: 10_000_000, level: 100, current: 250_000, next: 0, hasNext: false},
		{stars: -5, level: 0, current: 0, next: 25, hasNext: true},
	}
	for _, tc := range cases {
		level, current, next, hasNext := AccountRatingLevelForStars(tc.stars)
		if level != tc.level || current != tc.current || next != tc.next || hasNext != tc.hasNext {
			t.Fatalf("AccountRatingLevelForStars(%d) = %d/%d/%d/%v, want %d/%d/%d/%v",
				tc.stars, level, current, next, hasNext,
				tc.level, tc.current, tc.next, tc.hasNext)
		}
	}
	// No score below the top threshold may report itself as the top level, and no
	// score above one may report anything else: the ladder has exactly 100 rungs.
	for level := 1; level < MaxAccountRatingLevel; level++ {
		levelAt, _, _, _ := AccountRatingLevelForStars(AccountRatingLevelThreshold(level))
		if levelAt != level {
			t.Fatalf("threshold %d resolved to level %d", AccountRatingLevelThreshold(level), levelAt)
		}
		if below, _, _, _ := AccountRatingLevelForStars(AccountRatingLevelThreshold(level) - 1); below != level-1 {
			t.Fatalf("just below threshold %d resolved to level %d, want %d",
				AccountRatingLevelThreshold(level), below, level-1)
		}
	}
}

// TestComputeAccountRatingTopLevelDropsNextThreshold mirrors what the clients
// draw: no next_level_stars once the ceiling is reached, so the profile shows a
// max-level badge instead of an endless progress bar.
func TestComputeAccountRatingTopLevelDropsNextThreshold(t *testing.T) {
	top := AccountRatingLevelThreshold(MaxAccountRatingLevel)
	rating := ComputeAccountRating(
		AccountRatingSignals{UserID: 7, StarsReceived: top},
		DefaultAccountRatingWeights(),
		time.Unix(1_700_000_000, 0).UTC(),
	)
	if rating.Level != MaxAccountRatingLevel || rating.HasNextLevel || rating.NextLevelStars != 0 {
		t.Fatalf("top rating = %+v, want level %d without next", rating, MaxAccountRatingLevel)
	}
	if rating.CurrentLevelStars != top || rating.Stars != top {
		t.Fatalf("top rating thresholds = %d/%d, want %d/%d",
			rating.CurrentLevelStars, rating.Stars, top, top)
	}
	// A pending level is derived from the same curve, so it cannot report a
	// next_level_stars the visible level would not have either.
	pending := rating
	pending.PendingStars = 1
	pending.PendingDate = time.Unix(1_700_000_000, 0).UTC()
	if snapshot, ok := pending.PendingLevel(); !ok ||
		snapshot.Level != MaxAccountRatingLevel || snapshot.HasNextLevelStars {
		t.Fatalf("pending top snapshot = %+v/%v, want level %d without next",
			snapshot, ok, MaxAccountRatingLevel)
	}
}