From 24d4c0a2531bdb53f90596c31ceace1916e13100 Mon Sep 17 00:00:00 2001 From: Malaber Date: Wed, 15 Apr 2026 14:35:00 +0200 Subject: [PATCH] Hide live tie metadata and stream CI logs --- app/services/group_stage_service.rb | 5 +++ spec/services/group_stage_service_spec.rb | 34 +++------------ tasks.py | 52 +++++++++++++++++------ 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/app/services/group_stage_service.rb b/app/services/group_stage_service.rb index 9003131..7480bbc 100644 --- a/app/services/group_stage_service.rb +++ b/app/services/group_stage_service.rb @@ -282,6 +282,11 @@ class GroupStageService compared_group_score = sorted_group_scores[comparison_index] comparison_reason = group_score.comparison_reason_against(compared_group_score) + if comparison_reason[:tied] && !group_ready_for_decider_check + decisions[group_score_cache_key(group_score)] = nil + next + end + decisions[group_score_cache_key(group_score)] = { resolved_by: comparison_reason[:resolved_by].to_s, compared_with_team_id: compared_group_score.team.id, diff --git a/spec/services/group_stage_service_spec.rb b/spec/services/group_stage_service_spec.rb index 1b3a46b..6801da3 100644 --- a/spec/services/group_stage_service_spec.rb +++ b/spec/services/group_stage_service_spec.rb @@ -300,7 +300,7 @@ RSpec.describe GroupStageService do ) end - it 'does not request a decider while regular matches in the group are still unfinished' do + it 'does not expose unresolved tie metadata while regular matches in the group are still unfinished' do tournament.update!(playoff_teams_amount: 1, instant_finalists_amount: 1, intermediate_round_participants_amount: 0) create(:group_match, group: group, state: :in_progress).tap do |match| @@ -315,23 +315,11 @@ RSpec.describe GroupStageService do GroupStageService.send(:recalculate_position_of_group_scores!, [score_a, score_b, score_c, score_d]).each(&:save!) - expect(GroupStageService.ranking_decision_for(score_a)).to include( - resolved_by: 'tie', - direct_comparison_result: 'draw', - hidden_points_result: 'not_played', - tied: true, - needs_decider_match: false - ) - expect(GroupStageService.ranking_decision_for(score_b)).to include( - resolved_by: 'tie', - direct_comparison_result: 'draw', - hidden_points_result: 'not_played', - tied: true, - needs_decider_match: false - ) + expect(GroupStageService.ranking_decision_for(score_a)).to be_nil + expect(GroupStageService.ranking_decision_for(score_b)).to be_nil end - it 'marks unresolved ties outside the advancing cutoff without requesting a decider' do + it 'does not expose unresolved tie metadata outside the advancing cutoff before the group is finished' do tournament.update!(playoff_teams_amount: 2, instant_finalists_amount: 2, intermediate_round_participants_amount: 0) score_a = create(:group_score, group: group, team: team_a, group_points: 9, scored_points: 5, received_points: 1, position: 1) @@ -342,18 +330,8 @@ RSpec.describe GroupStageService do GroupStageService.send(:recalculate_position_of_group_scores!, [score_a, score_b, score_c, score_d]).each(&:save!) expect([score_a.position, score_b.position, score_c.position, score_d.position]).to eq([1, 2, 3, 3]) - expect(GroupStageService.ranking_decision_for(score_c)).to include( - resolved_by: 'tie', - hidden_points_result: 'not_played', - tied: true, - needs_decider_match: false - ) - expect(GroupStageService.ranking_decision_for(score_d)).to include( - resolved_by: 'tie', - hidden_points_result: 'not_played', - tied: true, - needs_decider_match: false - ) + expect(GroupStageService.ranking_decision_for(score_c)).to be_nil + expect(GroupStageService.ranking_decision_for(score_d)).to be_nil end it 'uses the decider hidden points to resolve a cutoff tie' do diff --git a/tasks.py b/tasks.py index 8bf0909..fe96111 100644 --- a/tasks.py +++ b/tasks.py @@ -131,6 +131,31 @@ def _run_subprocess(command, env=None, capture_output=False, check=True): return completed +def _stream_subprocess(command, env=None, check=True): + process = subprocess.Popen( + command, + env=env, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + bufsize=1, + ) + + output_chunks = [] + assert process.stdout is not None + for line in process.stdout: + print(line, end="") + output_chunks.append(line) + + process.wait() + output = "".join(output_chunks) + + if check and process.returncode != 0: + raise subprocess.CalledProcessError(process.returncode, command, output=output, stderr=None) + + return subprocess.CompletedProcess(command, process.returncode, stdout=output, stderr="") + + def _compose_env( host_port=SERVER_PORT, app_image=PRODUCTION_TAG, @@ -164,6 +189,15 @@ def _format_command(command): return " ".join(shlex.quote(part) for part in command) +def _rspec_command(*paths): + command = ["bundle", "exec", "rspec", *paths] + if os.environ.get("CI"): + command.extend(["--format", "documentation", "--no-color"]) + else: + command.extend(["--format", "progress", "--tty"]) + return command + + def _compose_run(compose_args, env, capture_output=False, check=True): command = _docker_compose_cmd() + compose_args return _run_subprocess(command, env=env, capture_output=capture_output, check=check) @@ -283,13 +317,9 @@ def _run_blackbox_rspec(base_url, email, password, username, alt_email=E2E_ALT_E TURNIERE_E2E_ALT_PASSWORD=alt_password, TURNIERE_E2E_ALT_USERNAME=alt_username, ) - command = ["bundle", "exec", "rspec", "spec/e2e/http", "--format", "documentation"] + command = _rspec_command("spec/e2e/http") print(_format_command(command)) - result = _run_subprocess(command, env=env, capture_output=True, check=False) - if result.stdout: - print(result.stdout.rstrip()) - if result.stderr: - print(result.stderr.rstrip()) + result = _stream_subprocess(command, env=env, check=False) report = _parse_rspec_report(f"{result.stdout}\n{result.stderr}") _print_header("Blackbox report") @@ -419,7 +449,7 @@ def start_test_local( @task def test(c): """Run the normal RSpec suite.""" - c.run(_bundle_exec("rspec", "--format", "progress", "--tty"), pty=True) + _stream_subprocess(_rspec_command(), env=_env()) @task(name="test-shard") @@ -451,11 +481,9 @@ def test_shard(c, node_index=1, node_total=8, dry_run=False): if not files: return - c.run( - _bundle_exec("rspec", "--format", "progress", "--tty") + - " --require ./spec/rails_helper " + - " ".join(shlex.quote(path) for path in files), - pty=True, + _stream_subprocess( + _rspec_command("--require", "./spec/rails_helper", *files), + env=_env(), )