@@ -350,6 +350,69 @@ defmodule Codebattle.Tournament.Entire.Top200Test do
350350 assert Tournament.Players . get_player ( tournament , 2 ) . score == 10
351351 assert Tournament.Players . get_player ( tournament , 9 ) . score == 50
352352 end
353+
354+ test "вылетевшие перенумеровываются в 9..N — нет дублей мест с топ-8 сетки" do
355+ # Регрессия: set_ranking нумерует ВСЁ поле (1..N) по сумме очков, поэтому вылетевшие
356+ # приходят в compute_final_standings с местами, пересекающимися с 1..8 сетки (на проде
357+ # это давало дубли мест 3..8 у топ-8 в лидерборде). Должны стать 9..N без коллизий.
358+ tournament = insert_top200_tournament ( )
359+ players_table = Tournament.Players . create_table ( tournament . id )
360+
361+ finals =
362+ inline_matches ( [
363+ % Match { id: 1 , player_ids: [ 1 , 2 ] , round_position: 7 , state: "game_over" } ,
364+ % Match { id: 2 , player_ids: [ 3 , 4 ] , round_position: 7 , state: "game_over" } ,
365+ % Match { id: 3 , player_ids: [ 5 , 6 ] , round_position: 7 , state: "game_over" } ,
366+ % Match { id: 4 , player_ids: [ 7 , 8 ] , round_position: 7 , state: "game_over" }
367+ ] )
368+
369+ tournament = % { tournament | players_table: players_table , current_round_position: 7 , matches: finals }
370+
371+ # Победители финалов по очкам раунда 7: 1, 3, 5, 7.
372+ record_scores ( tournament . id , 7 , [
373+ { 1 , 100 } ,
374+ { 2 , 10 } ,
375+ { 3 , 100 } ,
376+ { 4 , 10 } ,
377+ { 5 , 100 } ,
378+ { 6 , 10 } ,
379+ { 7 , 100 } ,
380+ { 8 , 10 }
381+ ] )
382+
383+ Enum . each ( 1 .. 8 , fn id ->
384+ Tournament.Players . put_player (
385+ tournament ,
386+ Player . new! ( % { id: id , name: "p#{ id } " , state: "active" , score: 0 , place: 0 } )
387+ )
388+ end )
389+
390+ # Вылетевшие 9..14: НАРОЧНО с местами 3..8 (как их пронумеровал set_ranking по полю) —
391+ # ровно те, что пересекаются с сеткой. Сумма очков убывает с id → ожидаемый порядок 9..14.
392+ Enum . each ( [ { 9 , 60 , 3 } , { 10 , 50 , 4 } , { 11 , 40 , 5 } , { 12 , 30 , 6 } , { 13 , 20 , 7 } , { 14 , 10 , 8 } ] , fn { id , score , place } ->
393+ Tournament.Players . put_player (
394+ tournament ,
395+ Player . new! ( % { id: id , name: "p#{ id } " , state: "active" , score: score , place: place } )
396+ )
397+ end )
398+
399+ Top200 . compute_final_standings ( tournament )
400+
401+ place_of = fn id -> Tournament.Players . get_player ( tournament , id ) . place end
402+
403+ # Топ-8 — по сетке финалов (победитель пары — лучшее место).
404+ assert place_of . ( 1 ) == 1
405+ assert place_of . ( 3 ) == 3
406+ assert place_of . ( 5 ) == 5
407+ assert place_of . ( 7 ) == 7
408+
409+ # Вылетевшие — строго 9..14 по убыванию суммы очков, БЕЗ пересечения с 1..8.
410+ assert Enum . map ( 9 .. 14 , place_of ) == [ 9 , 10 , 11 , 12 , 13 , 14 ]
411+
412+ # Главное: места всего поля уникальны и образуют ровно 1..14 — дублей нет.
413+ all_places = Enum . map ( 1 .. 14 , place_of )
414+ assert Enum . sort ( all_places ) == Enum . to_list ( 1 .. 14 )
415+ end
353416 end
354417
355418 describe "TournamentUserResult.upsert_results/1 — top200 (регрессия на потерю рейтинга)" do
0 commit comments