nsfwapp/backend/rating_test.go
2026-06-13 23:22:15 +02:00

311 lines
9.5 KiB
Go

package main
import (
"encoding/json"
"testing"
)
func ratingTestSegment(label string, start, duration, confidence float64) aiSegmentMeta {
return aiSegmentMeta{
Label: label,
StartSeconds: start,
EndSeconds: start + duration,
DurationSeconds: duration,
Score: confidence,
}
}
func TestComputeHighlightRatingSpansAllStars(t *testing.T) {
tests := []struct {
name string
segments []aiSegmentMeta
want int
}{
{
name: "one weak standing detection",
segments: []aiSegmentMeta{
ratingTestSegment("position:standing", 10, 6, 0.45),
},
want: 1,
},
{
name: "one brief strong position",
segments: []aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 8, 0.72),
},
want: 2,
},
{
name: "two solid position segments",
segments: []aiSegmentMeta{
ratingTestSegment("position:doggy", 20, 18, 0.78),
ratingTestSegment("position:cowgirl", 120, 18, 0.78),
},
want: 3,
},
{
name: "several long high quality positions",
segments: []aiSegmentMeta{
ratingTestSegment("position:doggy", 20, 25, 0.82),
ratingTestSegment("position:cowgirl", 120, 25, 0.82),
ratingTestSegment("position:missionary", 220, 25, 0.82),
ratingTestSegment("position:blowjob", 320, 25, 0.82),
},
want: 4,
},
{
name: "exceptional sustained and varied positions",
segments: []aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 40, 0.90),
ratingTestSegment("position:cowgirl", 100, 40, 0.90),
ratingTestSegment("position:missionary", 190, 40, 0.90),
ratingTestSegment("position:blowjob", 280, 40, 0.90),
ratingTestSegment("position:cunnilingus", 370, 40, 0.90),
ratingTestSegment("position:reverse_cowgirl", 460, 40, 0.90),
},
want: 5,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := computeHighlightRating(tt.segments, 600)
if got.Stars != tt.want {
t.Fatalf("stars = %d, want %d (score %.1f)", got.Stars, tt.want, got.Score)
}
})
}
}
func TestComputeHighlightRatingRewardsPositionDuration(t *testing.T) {
short := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 8, 0.80),
}, 600)
medium := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 35, 0.80),
}, 600)
long := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 120, 0.80),
}, 600)
if !(short.Score < medium.Score && medium.Score < long.Score) {
t.Fatalf(
"position duration should increase score: short=%.1f medium=%.1f long=%.1f",
short.Score,
medium.Score,
long.Score,
)
}
}
func TestComputeHighlightRatingActionPrefersSustainedActivity(t *testing.T) {
briefPeak := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 8, 0.95),
}, 600)
sustained := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 45, 0.55),
}, 600)
if sustained.ActionScore <= briefPeak.ActionScore {
t.Fatalf(
"sustained action should outrank a brief peak: sustained=%.1f brief=%.1f",
sustained.ActionScore,
briefPeak.ActionScore,
)
}
if sustained.Score <= briefPeak.Score {
t.Fatalf(
"rating should follow sustained action: sustained=%.1f brief=%.1f",
sustained.Score,
briefPeak.Score,
)
}
}
func TestComputeHighlightRatingRatesContextWithoutPosition(t *testing.T) {
clothingOnly := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("clothing:lingerie", 10, 60, 0.90),
ratingTestSegment("clothing:lingerie", 120, 60, 0.90),
ratingTestSegment("clothing:lingerie", 230, 60, 0.90),
}, 600)
richContext := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("combo:body:breasts+clothing:lingerie", 10, 45, 0.90),
ratingTestSegment("combo:object:vibrator+clothing:lingerie", 120, 60, 0.90),
ratingTestSegment("combo:body:penis+object:dildo", 240, 45, 0.90),
}, 600)
if clothingOnly.Stars != 2 {
t.Fatalf(
"clothing-only stars = %d, want 2 (score %.1f)",
clothingOnly.Stars,
clothingOnly.Score,
)
}
if richContext.Stars != 3 {
t.Fatalf(
"rich context stars = %d, want 3 (score %.1f)",
richContext.Stars,
richContext.Score,
)
}
if richContext.Stars > 3 {
t.Fatalf("context rating must stay below 4 stars: %#v", richContext)
}
}
func TestComputeHighlightRatingPositionsOutrankContext(t *testing.T) {
context := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("combo:body:breasts+clothing:lingerie+object:vibrator", 10, 240, 0.95),
}, 600)
positions := computeHighlightRating([]aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 40, 0.90),
ratingTestSegment("position:cowgirl", 100, 40, 0.90),
ratingTestSegment("position:missionary", 190, 40, 0.90),
ratingTestSegment("position:blowjob", 280, 40, 0.90),
}, 600)
if positions.Score <= context.Score || positions.Stars <= context.Stars {
t.Fatalf(
"positions should outrank context: positions=%.1f/%d context=%.1f/%d",
positions.Score,
positions.Stars,
context.Score,
context.Stars,
)
}
}
func TestComputeHighlightRatingExplicitSignalsBoostPositions(t *testing.T) {
positionSegments := []aiSegmentMeta{
ratingTestSegment("position:doggy", 20, 18, 0.78),
ratingTestSegment("position:cowgirl", 120, 18, 0.78),
}
base := computeHighlightRating(positionSegments, 600)
withObject := computeHighlightRating(append(
append([]aiSegmentMeta{}, positionSegments...),
ratingTestSegment("object:dildo", 220, 60, 0.90),
), 600)
withClothing := computeHighlightRating(append(
append([]aiSegmentMeta{}, positionSegments...),
ratingTestSegment("clothing:lingerie", 220, 60, 0.90),
), 600)
withBody := computeHighlightRating(append(
append([]aiSegmentMeta{}, positionSegments...),
ratingTestSegment("body:pussy", 220, 60, 0.90),
), 600)
if !(base.Score < withObject.Score &&
withObject.Score < withClothing.Score &&
withClothing.Score < withBody.Score) {
t.Fatalf(
"explicit signal priority is wrong: base=%.1f object=%.1f clothing=%.1f body=%.1f",
base.Score,
withObject.Score,
withClothing.Score,
withBody.Score,
)
}
}
func TestComputeHighlightRatingExplicitContextCanElevateStrongPositions(t *testing.T) {
positions := []aiSegmentMeta{
ratingTestSegment("position:doggy", 20, 25, 0.82),
ratingTestSegment("position:cowgirl", 120, 25, 0.82),
ratingTestSegment("position:missionary", 220, 25, 0.82),
ratingTestSegment("position:blowjob", 320, 25, 0.82),
}
positionOnly := computeHighlightRating(positions, 600)
withExplicitContext := computeHighlightRating(append(
append([]aiSegmentMeta{}, positions...),
ratingTestSegment("body:pussy", 400, 90, 0.92),
ratingTestSegment("clothing:lingerie", 500, 90, 0.92),
), 600)
if positionOnly.Stars != 4 {
t.Fatalf("position-only stars = %d, want 4 (score %.1f)", positionOnly.Stars, positionOnly.Score)
}
if withExplicitContext.Stars != 5 {
t.Fatalf(
"positions with explicit context stars = %d, want 5 (score %.1f)",
withExplicitContext.Stars,
withExplicitContext.Score,
)
}
}
func TestComputeHighlightRatingLongMixedContextRegression(t *testing.T) {
segments := []aiSegmentMeta{
ratingTestSegment("object:dildo", 24.5, 5, 0.792783260345459),
ratingTestSegment("object:dildo", 43.5, 8, 0.923585057258606),
ratingTestSegment("clothing:lingerie", 94.5, 21, 0.9069388088058022),
ratingTestSegment("clothing:lingerie", 120.5, 33, 0.887133002281189),
ratingTestSegment("object:vibrator", 215.5, 6, 0.40453797578811646),
ratingTestSegment("combo:body:breasts+clothing:lingerie", 298.5, 8, 0.6898824721574783),
ratingTestSegment("object:vibrator", 330.5, 5, 0.6836381554603577),
ratingTestSegment("clothing:lingerie", 348.5, 59, 0.9197490215301514),
ratingTestSegment("combo:object:vibrator+object:collar+clothing:lingerie", 416.5, 36, 0.8156423893044976),
ratingTestSegment("clothing:lingerie", 465.5, 36, 0.8007044196128845),
}
got := computeHighlightRating(segments, 504)
if got.Stars != 3 {
t.Fatalf("stars = %d, want 3 (score %.1f)", got.Stars, got.Score)
}
encoded, err := json.Marshal(got)
if err != nil {
t.Fatal(err)
}
t.Logf("rating=%s", encoded)
}
func TestPrepareAIRatingSegmentsKeepsDifferentPositionsSeparate(t *testing.T) {
segments := prepareAIRatingSegments([]aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 12, 0.85),
ratingTestSegment("position:cowgirl", 23, 12, 0.85),
})
if len(segments) != 2 {
t.Fatalf("segments = %d, want 2: %#v", len(segments), segments)
}
if segments[0].Position == segments[1].Position {
t.Fatalf("different positions were collapsed: %#v", segments)
}
}
func TestPrepareAIRatingSegmentsMergesSamePositionAcrossShortGap(t *testing.T) {
segments := prepareAIRatingSegments([]aiSegmentMeta{
ratingTestSegment("position:doggy", 10, 12, 0.85),
ratingTestSegment("position:doggy", 23, 12, 0.85),
})
if len(segments) != 1 {
t.Fatalf("segments = %d, want 1: %#v", len(segments), segments)
}
if segments[0].Position != "doggy" {
t.Fatalf("position = %q, want doggy", segments[0].Position)
}
if segments[0].DurationSeconds != 24 {
t.Fatalf(
"duration = %.1f, want 24 seconds of actual position evidence",
segments[0].DurationSeconds,
)
}
if segments[0].RatingIntensity <= 0 {
t.Fatalf("rating intensity = %.3f, want a positive heatcurve value", segments[0].RatingIntensity)
}
wantIntensity := ratingClamp01(
ratingSegmentSeverity(segments[0].Label) *
ratingConfidenceWeight(segments[0].Score),
)
if diff := segments[0].RatingIntensity - wantIntensity; diff < -0.0001 || diff > 0.0001 {
t.Fatalf(
"rating intensity = %.4f, want %.4f",
segments[0].RatingIntensity,
wantIntensity,
)
}
}