feat: AI-game refinements (GCG, your_turn, admin, metrics)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 49s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m7s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 49s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m7s
Follow-ups on the honest-AI game, same PR: - GCG export labels the robot seat "AI" instead of its pool name (ExportGCG overrides via accounts.IsRobot); the in-app 🤖 is unchanged. - vs_ai games emit no your_turn (the robot replies instantly, so it would be redundant); opponent_moved still advances the UI. - Admin console shows the AI flag: a 🤖 column in /games and an "AI game" line on the game card (GameRow/GameDetailView gain VsAI). - games_started_total / games_abandoned_total gain a vs_ai attribute; the Grafana Game-domain dashboard splits started/abandoned into human and AI panels. Tests: metrics unit (vs_ai split); integration (no your_turn, GCG "AI").
This commit is contained in:
@@ -20,10 +20,12 @@ func TestGameMetrics(t *testing.T) {
|
||||
meter := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)).Meter("test")
|
||||
m := newGameMetrics(meter)
|
||||
|
||||
m.recordStarted(ctx, engine.VariantEnglish)
|
||||
m.recordStarted(ctx, engine.VariantEnglish)
|
||||
m.recordStarted(ctx, engine.VariantRussianScrabble)
|
||||
m.recordAbandoned(ctx, engine.VariantErudit)
|
||||
m.recordStarted(ctx, engine.VariantEnglish, false)
|
||||
m.recordStarted(ctx, engine.VariantEnglish, false)
|
||||
m.recordStarted(ctx, engine.VariantRussianScrabble, false)
|
||||
m.recordStarted(ctx, engine.VariantEnglish, true) // an honest-AI game
|
||||
m.recordAbandoned(ctx, engine.VariantErudit, false)
|
||||
m.recordAbandoned(ctx, engine.VariantEnglish, true) // an AI game abandoned (the 7-day rule)
|
||||
m.recordReplay(ctx, engine.VariantEnglish, time.Now().Add(-time.Millisecond))
|
||||
m.recordValidate(ctx, engine.VariantRussianScrabble, time.Now().Add(-time.Millisecond))
|
||||
m.recordMoveDuration(ctx, engine.VariantEnglish, 3, 5*time.Second)
|
||||
@@ -35,11 +37,15 @@ func TestGameMetrics(t *testing.T) {
|
||||
}
|
||||
|
||||
started := counterByAttr(t, rm, "games_started_total", "variant")
|
||||
if started["scrabble_en"] != 2 || started["scrabble_ru"] != 1 {
|
||||
t.Errorf("games_started_total = %v, want scrabble_en:2 scrabble_ru:1", started)
|
||||
if started["scrabble_en"] != 3 || started["scrabble_ru"] != 1 {
|
||||
t.Errorf("games_started_total = %v, want scrabble_en:3 scrabble_ru:1", started)
|
||||
}
|
||||
if abandoned := counterByAttr(t, rm, "games_abandoned_total", "variant"); abandoned["erudit_ru"] != 1 {
|
||||
t.Errorf("games_abandoned_total = %v, want erudit_ru:1", abandoned)
|
||||
// The vs_ai attribute splits AI from human games for the per-kind Grafana panels.
|
||||
if byKind := counterByAttr(t, rm, "games_started_total", "vs_ai"); byKind["false"] != 3 || byKind["true"] != 1 {
|
||||
t.Errorf("games_started_total by vs_ai = %v, want false:3 true:1", byKind)
|
||||
}
|
||||
if abandoned := counterByAttr(t, rm, "games_abandoned_total", "vs_ai"); abandoned["false"] != 1 || abandoned["true"] != 1 {
|
||||
t.Errorf("games_abandoned_total by vs_ai = %v, want false:1 true:1", abandoned)
|
||||
}
|
||||
if c := histogramCount(t, rm, "game_replay_duration"); c != 1 {
|
||||
t.Errorf("game_replay_duration observations = %d, want 1", c)
|
||||
@@ -78,7 +84,7 @@ func counterByAttr(t *testing.T, rm metricdata.ResourceMetrics, name, attr strin
|
||||
}
|
||||
for _, dp := range sum.DataPoints {
|
||||
v, _ := dp.Attributes.Value(attribute.Key(attr))
|
||||
out[v.AsString()] += dp.Value
|
||||
out[v.Emit()] += dp.Value // Emit renders any attribute type (string or bool) to a key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user