scottwillson/racing_on_rails

View on GitHub
test_ruby/models/competitions/calculations/calculator_test.rb

Summary

Maintainability
D
2 days
Test Coverage
# frozen_string_literal: true

require_relative "../../../../app/models/competitions/calculations/calculator"
require_relative "calculations_test"

# :stopdoc:
# TODO remove noisy member setup
module Competitions
  module Calculations
    class CalculatorTest < CalculationsTest
      def test_calculate_with_no_source_results
        assert_equal [], Calculator.calculate([])
      end

      def test_calculate_with_one_source_result
        source_results = [{ "event_id" => 1, "participant_id" => 1, "place" => "1", "member_from" => Date.new(2012), member_to: end_of_year, "year" => Date.today.year }]
        expected = [
          result(place: 1, participant_id: 1, points: 1, scores: [{ numeric_place: 1, participant_id: 1, points: 1, event_id: 1 }])
        ]
        actual = Calculator.calculate(source_results)
        assert_equal_results expected, actual
      end

      def test_calculate_with_many_source_results
        source_results = [
          { event_id: 1, participant_id: 1, place: 1, member_from: Date.new(2012), member_to: end_of_year, year: Date.today.year },
          { event_id: 1, participant_id: 2, place: 2, member_from: Date.new(2012), member_to: end_of_year, year: Date.today.year }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 1, scores: [{ numeric_place: 1, participant_id: 1, points: 1, event_id: 1 }]),
          result(place: 1, participant_id: 2, points: 1, scores: [{ numeric_place: 2, participant_id: 2, points: 1, event_id: 1 }])
        ]
        actual = Calculator.calculate(source_results)
        assert_equal_results expected, actual
      end

      def test_calculate_team_results
        source_results = [
          { race_id: 1, participant_id: 3, place: 1, member_from: Date.new(2012), member_to: end_of_year, year: Date.today.year },
          { race_id: 1, participant_id: 4, place: 1, member_from: Date.new(2012), member_to: end_of_year, year: Date.today.year },
          { race_id: 1, participant_id: 1, place: 2, member_from: Date.new(2012), member_to: end_of_year, year: Date.today.year },
          { race_id: 1, participant_id: 2, place: 2, member_from: Date.new(2012), member_to: end_of_year, year: Date.today.year }
        ]
        expected = [
          result(place: 3, participant_id: 1, points: 4, scores: [{ numeric_place: 2, participant_id: 1, points: 4 }]),
          result(place: 3, participant_id: 2, points: 4, scores: [{ numeric_place: 2, participant_id: 2, points: 4 }]),
          result(place: 1, participant_id: 3, points: 10, scores: [{ numeric_place: 1, participant_id: 3, points: 10 }]),
          result(place: 1, participant_id: 4, points: 10, scores: [{ numeric_place: 1, participant_id: 4, points: 10 }])
        ]
        actual = Calculator.calculate(source_results, point_schedule: [20, 8, 3])
        assert_equal_results expected, actual
      end

      def test_calculate_team_results_best_3_for_event
        source_results = [
          { event_id: 1, race_id: 1, participant_id: 1, place: 107 },
          { event_id: 1, race_id: 1, participant_id: 1, place: 7 },
          { event_id: 1, race_id: 1, participant_id: 2, place: 1 },
          { event_id: 1, race_id: 1, participant_id: 1, place: 2 },
          { event_id: 1, race_id: 1, participant_id: 2, place: 8 },
          { event_id: 1, race_id: 1, participant_id: 1, place: 3 },
          { event_id: 1, race_id: 1, participant_id: 1, place: 4 },
          { event_id: 2, race_id: 2, participant_id: 1, place: 1 }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 34, scores: [
                   { numeric_place: 1, participant_id: 1, points: 10, event_id: 2 },
                   { numeric_place: 2, participant_id: 1, points: 9, event_id: 1 },
                   { numeric_place: 3, participant_id: 1, points: 8, event_id: 1 },
                   { numeric_place: 4, participant_id: 1, points: 7, event_id: 1 }
                 ]),
          result(place: 2, participant_id: 2, points: 13, scores: [
                   { numeric_place: 1, participant_id: 2, points: 10, event_id: 1 },
                   { numeric_place: 8, participant_id: 2, points: 3, event_id: 1 }
                 ])
        ]
        actual = Calculator.calculate(
          source_results,
          point_schedule: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
          results_per_event: 3,
          results_per_race: Competitions::Calculations::Calculator::UNLIMITED,
          members_only: false
        )
        assert_equal_results expected, actual
      end

      def test_calculate_should_ignore_non_scoring_results
        source_results = [
          { event_id: 1, participant_id: 1, place: "", member_from: Date.new(2012), member_to: end_of_year },
          { event_id: 1, participant_id: 2, place: "DNS", member_from: Date.new(2012), member_to: end_of_year },
          { event_id: 1, participant_id: 3, place: "DQ", member_from: Date.new(2012), member_to: end_of_year },
          { event_id: 1, participant_id: 4, place: nil, member_from: Date.new(2012), member_to: end_of_year }
        ]
        expected = []
        actual = Calculator.calculate(source_results)
        assert_equal_results expected, actual
      end

      def test_calculate_ignore_non_starters
        source_results = [
          { event_id: 1, participant_id: 1, place: "", member_from: Date.new(2012), member_to: end_of_year },
          { event_id: 1, participant_id: 2, place: "DNS", member_from: Date.new(2012), member_to: end_of_year },
          { event_id: 1, participant_id: 3, place: "DQ", member_from: Date.new(2012), member_to: end_of_year },
          { event_id: 1, participant_id: 4, place: nil, member_from: Date.new(2012), member_to: end_of_year }
        ]
        expected = []
        actual = Calculator.calculate(source_results)
        assert_equal_results expected, actual
      end

      def test_calculate_with_multiple_events_and_people
        source_results = [
          { id: 1, event_id: 1, race_id: 1, participant_id: 1, place: 1, member_from: Date.new(2012), member_to: end_of_year, "year" => Date.today.year },
          { id: 2, event_id: 1, race_id: 1, participant_id: 2, place: 2, member_from: Date.new(2012), member_to: end_of_year, "year" => Date.today.year },
          { id: 3, event_id: 1, race_id: 1, participant_id: 2, place: 20, member_from: Date.new(2012), member_to: end_of_year, "year" => Date.today.year },
          { id: 4, event_id: 2, race_id: 2, participant_id: 1, place: "DNF", member_from: Date.new(2012), member_to: end_of_year, "year" => Date.today.year }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 2, scores: [
                   { numeric_place: 1, source_result_id: 1, points: 1, participant_id: 1, event_id: 1 },
                   { numeric_place: Float::INFINITY, source_result_id: 4, points: 1, participant_id: 1, event_id: 2 }
                 ]),
          result(place: 2, participant_id: 2, points: 1, scores: [
                   { numeric_place: 2, source_result_id: 2, points: 1, participant_id: 2, event_id: 1 }
                 ])
        ]
        actual = Calculator.calculate(source_results, dnf_points: 1)
        assert_equal_results expected, actual
      end

      # Cross Crusade team competition
      def test_calculate_most_points_win
        source_results = [
          { event_id: 1, participant_id: 1, place: 2, race_id: 1 },
          { event_id: 1, participant_id: 1, place: 4, race_id: 1 },
          { event_id: 1, participant_id: 2, place: 5, race_id: 1 }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 2.0, scores: [{ numeric_place: 2, participant_id: 1, points: 2.0, event_id: 1 }]),
          result(place: 2, participant_id: 2, points: 5.0, scores: [{ numeric_place: 5, participant_id: 2, points: 5.0, event_id: 1 }])
        ]
        actual = Calculator.calculate(
          source_results,
          members_only: false,
          most_points_win: false,
          point_schedule: [1, 2, 3, 4, 5],
          results_per_event: 10,
          results_per_race: 1
        )
        assert_equal_results expected, actual
      end

      # Cross Crusade team competition
      def test_missing_result_penalty
        source_results = [
          { event_id: 1, participant_id: 10, place: 2, race_id: 100 },
          { event_id: 1, participant_id: 10, place: 4, race_id: 100 },
          { event_id: 1, participant_id: 20, place: 5, race_id: 100 },
          { event_id: 1, participant_id: 10, place: 5, race_id: 200 }
        ]
        expected = [
          result(place: 1, participant_id: 10, points: 107.0, scores: [
                   { numeric_place: 2, participant_id: 10, points: 2.0, event_id: 1 },
                   { numeric_place: 5, participant_id: 10, points: 5.0, event_id: 1 },
                   { numeric_place: 100, participant_id: 10, points: 100.0 }
                 ]),
          result(place: 2, participant_id: 20, points: 205.0, scores: [
                   { numeric_place: 5, participant_id: 20, points: 5.0, event_id: 1 },
                   { numeric_place: 100, participant_id: 20, points: 200 }
                 ])
        ]
        actual = Calculator.calculate(
          source_results,
          completed_events: 1,
          members_only: false,
          missing_result_penalty: 100,
          most_points_win: false,
          point_schedule: [1, 2, 3, 4, 5],
          results_per_event: 3,
          results_per_race: 1
        )
        assert_equal_results expected, actual
      end

      def test_map_to_scores
        expected = [Struct::CalculatorScore.new(nil, 1, nil, 2, 3, 1, 5)]
        source_results = [result(
          date: nil,
          event_id: 1,
          place: 2,
          participant_id: 3,
          points: 4,
          id: 5
        )]
        actual = Calculator.map_to_scores(source_results, {})
        assert_equal expected, actual
      end

      def test_map_to_scores_empty
        expected = []
        actual = Calculator.map_to_scores([], {})
        assert_equal expected, actual
      end

      def test_map_to_results
        scores = [Struct::CalculatorScore.new(nil, 1, nil, 2, 3, 4, 5, 6, nil)]
        expected = [result(participant_id: 3, points: 4, scores: [
                             {
                               event_id: 1,
                               numeric_place: 2,
                               participant_id: 3,
                               points: 4,
                               source_result_id: 5,
                               team_size: 6
                             }
                           ])]
        actual = Calculator.map_to_results(scores, {})
        assert_equal_results expected, actual
      end

      def test_map_to_results_empty
        expected = []
        actual = Calculator.map_to_results([], {})
        assert_equal expected, actual
      end

      def test_map_hashes_to_results
        expected = [Struct::CalculatorResult.new.tap { |r| r.place = 3 }]
        actual = Calculator.map_hashes_to_results([{ place: 3 }])
        assert_equal expected, actual
      end

      def test_place_bonus_points
        assert_equal 6, Calculator.place_bonus_points(result(place: "1"), place_bonus: [6, 3, 1])
        assert_equal 1, Calculator.place_bonus_points(result(place: "3"), place_bonus: [6, 3, 1])
        assert_equal 0, Calculator.place_bonus_points(result(place: "4"), place_bonus: [6, 3, 1])
        assert_equal 0, Calculator.place_bonus_points(result(place: "DNF"), place_bonus: [6, 3, 1])
        assert_equal 0, Calculator.place_bonus_points(result(place: "DQ"), place_bonus: [6, 3, 1])
      end

      def test_source_events
        source_results = [
          { event_id: 1, participant_id: 1, place: 1 },
          { event_id: 2, participant_id: 2, place: 2 }
        ]
        expected = [
          result(place: 1, participant_id: 2, points: 1, scores: [{ numeric_place: 2, participant_id: 2, points: 1, event_id: 2 }])
        ]
        actual = Calculator.calculate(source_results, source_event_ids: [2], members_only: false)
        assert_equal_results expected, actual
      end

      def test_ignore_empty_source_events
        source_results = [
          { event_id: 1, participant_id: 1, place: 1 },
          { event_id: 2, participant_id: 2, place: 2 }
        ]
        actual = Calculator.calculate(source_results, source_event_ids: [], members_only: false)
        assert_equal [], actual
      end

      def test_set_preliminary
        rules = { minimum_events: 2, members_only: false, completed_events: 3 }
        source_results = [
          { "place" => "1", "participant_id" => 1, "event_id" => 1, "race_id" => 1 },
          { "place" => "2", "participant_id" => 2, "event_id" => 1, "race_id" => 1 },
          { "place" => "3", "participant_id" => 3, "event_id" => 1, "race_id" => 1 },
          { "place" => "1", "participant_id" => 1, "event_id" => 2, "race_id" => 2 },
          { "place" => "2", "participant_id" => 2, "event_id" => 2, "race_id" => 2 },
          { "place" => "3", "participant_id" => 1, "event_id" => 3, "race_id" => 3 }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 3, preliminary: false, scores: [
                   { numeric_place: 1, participant_id: 1, points: 1, event_id: 1 },
                   { numeric_place: 1, participant_id: 1, points: 1, event_id: 2 },
                   { numeric_place: 3, participant_id: 1, points: 1, event_id: 3 }
                 ]),
          result(place: 2, participant_id: 2, points: 2, preliminary: false, scores: [
                   { numeric_place: 2, participant_id: 2, points: 1, event_id: 1 },
                   { numeric_place: 2, participant_id: 2, points: 1, event_id: 2 }
                 ]),
          result(place: 3, participant_id: 3, points: 1, preliminary: true, scores: [
                   { numeric_place: 3, participant_id: 3, points: 1, event_id: 1 }
                 ])
        ]
        actual = Calculator.calculate(source_results, rules)
        assert_equal_results expected, actual
      end

      def test_set_preliminary_before_minimum_events
        rules = { minimum_events: 3, members_only: false, break_ties: true, completed_events: 2 }
        source_results = [
          { "place" => "1", "participant_id" => 1, "event_id" => 1, "race_id" => 1 },
          { "place" => "2", "participant_id" => 2, "event_id" => 1, "race_id" => 1 },
          { "place" => "3", "participant_id" => 3, "event_id" => 1, "race_id" => 1 },
          { "place" => "1", "participant_id" => 1, "event_id" => 2, "race_id" => 2 },
          { "place" => "2", "participant_id" => 2, "event_id" => 2, "race_id" => 2 }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 2, preliminary: nil, scores: [
                   { numeric_place: 1, participant_id: 1, points: 1, event_id: 1 },
                   { numeric_place: 1, participant_id: 1, points: 1, event_id: 2 }
                 ]),
          result(place: 2, participant_id: 2, points: 2, preliminary: nil, scores: [
                   { numeric_place: 2, participant_id: 2, points: 1, event_id: 1 },
                   { numeric_place: 2, participant_id: 2, points: 1, event_id: 2 }
                 ]),
          result(place: 3, participant_id: 3, points: 1, preliminary: nil, scores: [
                   { numeric_place: 3, participant_id: 3, points: 1, event_id: 1 }
                 ])
        ]
        actual = Calculator.calculate(source_results, rules)
        assert_equal_results expected, actual
      end

      def test_set_preliminary_when_event_complete
        rules = { minimum_events: 2, members_only: false, completed_events: 3, source_event_ids: [1, 2, 3] }
        source_results = [
          { "place" => "1", "participant_id" => 1, "event_id" => 1, "race_id" => 1 },
          { "place" => "2", "participant_id" => 2, "event_id" => 1, "race_id" => 1 },
          { "place" => "3", "participant_id" => 3, "event_id" => 1, "race_id" => 1 },
          { "place" => "1", "participant_id" => 1, "event_id" => 2, "race_id" => 2 },
          { "place" => "2", "participant_id" => 2, "event_id" => 2, "race_id" => 2 },
          { "place" => "3", "participant_id" => 1, "event_id" => 3, "race_id" => 3 }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 3, preliminary: nil, scores: [
                   { numeric_place: 1, participant_id: 1, points: 1, event_id: 1 },
                   { numeric_place: 1, participant_id: 1, points: 1, event_id: 2 },
                   { numeric_place: 3, participant_id: 1, points: 1, event_id: 3 }
                 ]),
          result(place: 2, participant_id: 2, points: 2, preliminary: nil, scores: [
                   { numeric_place: 2, participant_id: 2, points: 1, event_id: 1 },
                   { numeric_place: 2, participant_id: 2, points: 1, event_id: 2 }
                 ])
        ]
        actual = Calculator.calculate(source_results, rules)
        assert_equal_results expected, actual
      end

      def test_upgrade_points
        rules = { members_only: false, place_bonus: [6, 3, 1], points_schedule_from_field_size: true }
        source_results = [
          { "place" => "6", "participant_id" => 1, "event_id" => 1, "race_id" => 1, "field_size" => 51 },
          { "place" => "1", "participant_id" => 1, "event_id" => 2, "race_id" => 2, "field_size" => 22, "points" => 7, "upgrade" => true },
          { "place" => "3", "participant_id" => 2, "event_id" => 3, "race_id" => 3, "field_size" => 9 }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 49.5, scores: [
                   { numeric_place: 6, participant_id: 1, points: 46, event_id: 1 },
                   { numeric_place: 1, participant_id: 1, points: 3.5, event_id: 2, upgrade: true }
                 ]),
          result(place: 2, participant_id: 2, points: 8, scores: [
                   { numeric_place: 3, participant_id: 2, points: 8, event_id: 3 }
                 ])
        ]
        actual = Calculator.calculate(source_results, rules)
        assert_equal_results expected, actual
      end

      def test_maximum_upgrade_points
        rules = { members_only: false, place_bonus: [6, 3, 1], points_schedule_from_field_size: true, maximum_upgrade_points: 5 }
        source_results = [
          { "place" => "6", "participant_id" => 1, "event_id" => 1, "race_id" => 1, "field_size" => 51 },
          { "place" => "1", "participant_id" => 1, "event_id" => 2, "race_id" => 2, "field_size" => 22, "points" => 20, "upgrade" => true },
          { "place" => "3", "participant_id" => 2, "event_id" => 3, "race_id" => 3, "field_size" => 9 }
        ]
        expected = [
          result(place: 1, participant_id: 1, points: 51, scores: [
                   { numeric_place: 6, participant_id: 1, points: 46, event_id: 1 },
                   { numeric_place: 1, participant_id: 1, points: 5, event_id: 2, upgrade: true }
                 ]),
          result(place: 2, participant_id: 2, points: 8, scores: [
                   { numeric_place: 3, participant_id: 2, points: 8, event_id: 3 }
                 ])
        ]
        actual = Calculator.calculate(source_results, rules)
        assert_equal_results expected, actual
      end
    end
  end
end