diff --git a/app/controllers/tournaments_controller.rb b/app/controllers/tournaments_controller.rb index aaabd93..f12b169 100644 --- a/app/controllers/tournaments_controller.rb +++ b/app/controllers/tournaments_controller.rb @@ -173,14 +173,14 @@ class TournamentsController < ApplicationController def timer_end_params { - timer_end: params[:timer_end], + timer_end: params[:timer_end] || params[:timestamp], timer_mode: params[:timer_mode] } end def serialize_timer_state(tournament) { - timer_end: tournament.timer_end&.iso8601, + timestamp: tournament.timer_end&.iso8601, timer_mode: tournament.timer_mode } end @@ -333,14 +333,17 @@ class TournamentsController < ApplicationController end def validate_set_timer_end_params - timer_end = params[:timer_end] - timer_end_seconds = params[:timer_end_seconds] + timestamp = params[:timestamp] + timestamp_seconds = params[:timestamp_seconds] timer_mode = params[:timer_mode] clear_timer = ActiveModel::Type::Boolean.new.cast(params[:clear_timer]) + timestamp = params[:timer_end] if timestamp.blank? && params[:timer_end].present? + timestamp_seconds = params[:timer_end_seconds] if timestamp_seconds.blank? && params[:timer_end_seconds].present? + if clear_timer - if timer_end.present? || timer_end_seconds.present? || timer_mode.present? - return render json: { error: 'clear_timer cannot be combined with timer values or timer_mode' }, + if timestamp.present? || timestamp_seconds.present? || timer_mode.present? + return render json: { error: 'clear_timer cannot be combined with timestamp values or timer_mode' }, status: :unprocessable_entity end @@ -353,48 +356,48 @@ class TournamentsController < ApplicationController return render json: { error: 'Invalid timer_mode' }, status: :unprocessable_entity end - # throw error if both timer_end and timer_end_seconds are present - if timer_end.present? && timer_end_seconds.present? - return render json: { error: 'Only one of timer_end or timer_end_seconds is allowed' }, status: :unprocessable_entity + # throw error if both timestamp and timestamp_seconds are present + if timestamp.present? && timestamp_seconds.present? + return render json: { error: 'Only one of timestamp or timestamp_seconds is allowed' }, status: :unprocessable_entity end - if timer_mode == 'countup' && timer_end_seconds.present? - return render json: { error: 'timer_end_seconds is only allowed for countdown mode' }, status: :unprocessable_entity + if timer_mode == 'countup' && timestamp_seconds.present? + return render json: { error: 'timestamp_seconds is only allowed for countdown mode' }, status: :unprocessable_entity end - if timer_end_seconds.present? + if timestamp_seconds.present? begin - timer_end_seconds = Integer(timer_end_seconds) + timestamp_seconds = Integer(timestamp_seconds) rescue ArgumentError return render json: { error: 'Invalid seconds format' }, status: :unprocessable_entity end - return render json: { error: 'Timer end must be in the future' }, status: :unprocessable_entity if timer_end_seconds <= 0 + return render json: { error: 'Timestamp must be in the future for countdown mode' }, status: :unprocessable_entity if timestamp_seconds <= 0 - parsed_time = Time.zone.now + timer_end_seconds + parsed_time = Time.zone.now + timestamp_seconds params[:timer_end] = parsed_time params[:timer_mode] = 'countdown' - elsif timer_end.present? + elsif timestamp.present? begin - parsed_time = Time.zone.parse(timer_end) + parsed_time = Time.zone.parse(timestamp) if parsed_time.nil? - return render json: { error: 'Invalid timer_end format' }, status: :unprocessable_entity + return render json: { error: 'Invalid timestamp format' }, status: :unprocessable_entity elsif timer_mode != 'countup' && !parsed_time.future? - return render json: { error: 'Timer end must be in the future' }, status: :unprocessable_entity + return render json: { error: 'Timestamp must be in the future for countdown mode' }, status: :unprocessable_entity end params[:timer_end] = parsed_time params[:timer_mode] = timer_mode.presence || 'countdown' rescue ArgumentError - return render json: { error: 'Invalid timer_end format' }, status: :unprocessable_entity + return render json: { error: 'Invalid timestamp format' }, status: :unprocessable_entity end elsif timer_mode == 'countup' params[:timer_end] = Time.zone.now elsif timer_mode.present? - return render json: { error: 'timer_mode requires timer_end, timer_end_seconds, or clear_timer' }, + return render json: { error: 'timer_mode requires timestamp, timestamp_seconds, or clear_timer' }, status: :unprocessable_entity else - return render json: { error: 'timer_end, timer_end_seconds, or clear_timer is required' }, + return render json: { error: 'timestamp, timestamp_seconds, or clear_timer is required' }, status: :unprocessable_entity end end diff --git a/app/serializers/tournament_serializer.rb b/app/serializers/tournament_serializer.rb index abed8eb..e5d525a 100644 --- a/app/serializers/tournament_serializer.rb +++ b/app/serializers/tournament_serializer.rb @@ -11,7 +11,7 @@ class TournamentSerializer < SimpleTournamentSerializer has_many :stages has_many :team_action_lists - attribute :timer_end do + attribute :timestamp do object.timer_end&.iso8601 end diff --git a/app/services/tournament_snapshot_builder.rb b/app/services/tournament_snapshot_builder.rb index 03a5226..9932cc8 100644 --- a/app/services/tournament_snapshot_builder.rb +++ b/app/services/tournament_snapshot_builder.rb @@ -22,7 +22,7 @@ class TournamentSnapshotBuilder def build payload = tournament.attributes.symbolize_keys.slice(*ROOT_ATTRIBUTES) - payload[:timer_end] = tournament.timer_end&.iso8601 + payload[:timestamp] = tournament.timer_end&.iso8601 payload[:timer_mode] = tournament.timer_mode payload[:sync_generated_at] = Time.current.iso8601(6) payload[:teams] = teams_payload diff --git a/app/services/tournament_snapshot_importer.rb b/app/services/tournament_snapshot_importer.rb index 4841792..4e1af83 100644 --- a/app/services/tournament_snapshot_importer.rb +++ b/app/services/tournament_snapshot_importer.rb @@ -67,7 +67,7 @@ class TournamentSnapshotImporter def update_tournament! root_attrs = snapshot.slice(*ROOT_ATTRIBUTES) root_attrs[:code] = tournament.code if code_taken_by_another_tournament?(root_attrs[:code]) - root_attrs[:timer_end] = parse_time(snapshot[:timer_end]) + root_attrs[:timer_end] = parse_time(snapshot[:timestamp] || snapshot[:timer_end]) root_attrs[:timer_mode] = snapshot[:timer_mode] root_attrs[:sync_source_tournament_id] = snapshot[:id] root_attrs[:sync_last_imported_snapshot_at] = snapshot_generated_at diff --git a/app/services/tournament_timer_broadcast_payload.rb b/app/services/tournament_timer_broadcast_payload.rb index 5640165..97fa866 100644 --- a/app/services/tournament_timer_broadcast_payload.rb +++ b/app/services/tournament_timer_broadcast_payload.rb @@ -6,7 +6,7 @@ class TournamentTimerBroadcastPayload { type: 'timer.updated', tournament_id: tournament.id, - timer_end: tournament.timer_end&.iso8601, + timestamp: tournament.timer_end&.iso8601, timer_mode: tournament.timer_mode } end diff --git a/doc/timer_api.md b/doc/timer_api.md index 8e63d04..3f170fd 100644 --- a/doc/timer_api.md +++ b/doc/timer_api.md @@ -10,9 +10,9 @@ Backend timer supports: - live websocket updates - follower sync propagation -Frontend should treat timer state as small state machine: +Frontend should treat timer state as: -- no timer: `timer_end = null`, `timer_mode = null` +- no timer: `timestamp = null`, `timer_mode = null` - countdown: `timer_mode = "countdown"` - countup: `timer_mode = "countup"` @@ -30,7 +30,7 @@ Response shape: ```json { - "timer_end": "2026-04-24T10:15:00Z", + "timestamp": "2026-04-24T10:15:00Z", "timer_mode": "countdown" } ``` @@ -41,23 +41,23 @@ Possible values: - `timer_mode = "countup"` - `timer_mode = null` -## Meaning Of `timer_end` +## Meaning Of `timestamp` -Name old. Semantics now depend on mode. +Same field. Mode decides meaning. ### Countdown mode -`timer_end` means exact wall-clock timestamp when countdown reaches zero. +`timestamp` means exact wall-clock timestamp when countdown reaches zero. Frontend math: ```text -remaining_ms = max(0, timer_end - now) +remaining_ms = max(0, timestamp - now) ``` ### Countup mode -`timer_end` means anchor timestamp when countup started. +`timestamp` means anchor timestamp when countup started. It is **not** finish time in this mode. Think of it as: @@ -68,23 +68,23 @@ Think of it as: Frontend math: ```text -elapsed_ms = max(0, now - timer_end) +elapsed_ms = max(0, now - timestamp) ``` Practical example: - backend returns: - `timer_mode = "countup"` - - `timer_end = "2026-04-24T10:00:00Z"` + - `timestamp = "2026-04-24T10:00:00Z"` - current time: - - `2026-04-24T10:03:15Z"` + - `2026-04-24T10:03:15Z` Frontend should display about `03:15` elapsed. ### Unset timer - `timer_mode = null` -- `timer_end = null` +- `timestamp = null` Frontend should render no active timer. @@ -97,13 +97,16 @@ Endpoint: - `PATCH /tournaments/:id/set_timer_end` +Current route name still says `set_timer_end`. +Payload field name is now `timestamp`. + ### Start countdown by duration Request: ```json { - "timer_end_seconds": 120 + "timestamp_seconds": 120 } ``` @@ -112,7 +115,7 @@ Behavior: - backend computes `Time.zone.now + 120` - backend stores: - `timer_mode = "countdown"` - - `timer_end = computed future timestamp` + - `timestamp = computed future timestamp` ### Start countdown by explicit timestamp @@ -120,7 +123,7 @@ Request: ```json { - "timer_end": "2026-04-24T10:15:00Z" + "timestamp": "2026-04-24T10:15:00Z" } ``` @@ -128,7 +131,7 @@ Optional explicit mode: ```json { - "timer_end": "2026-04-24T10:15:00Z", + "timestamp": "2026-04-24T10:15:00Z", "timer_mode": "countdown" } ``` @@ -145,10 +148,10 @@ Request: Behavior: -- backend stores current server time into `timer_end` +- backend stores current server time into `timestamp` - backend stores `timer_mode = "countup"` -Frontend should interpret returned `timer_end` as start time anchor. +Frontend should interpret returned `timestamp` as start time anchor. ### Restore countup from explicit anchor @@ -156,7 +159,7 @@ Backend also accepts explicit timestamp with countup mode: ```json { - "timer_end": "2026-04-24T10:00:00Z", + "timestamp": "2026-04-24T10:00:00Z", "timer_mode": "countup" } ``` @@ -181,17 +184,17 @@ Behavior: - backend clears both fields - response returns: - - `timer_end = null` + - `timestamp = null` - `timer_mode = null` ## Validation Rules Backend rejects: -- both `timer_end` and `timer_end_seconds` +- both `timestamp` and `timestamp_seconds` - invalid `timer_mode` -- `timer_end_seconds <= 0` -- `timer_end_seconds` together with `timer_mode = "countup"` +- `timestamp_seconds <= 0` +- `timestamp_seconds` together with `timer_mode = "countup"` - `timer_mode` by itself unless it is `countup` - `clear_timer` mixed with timer values @@ -222,7 +225,7 @@ Payload: { "type": "timer.updated", "tournament_id": 123, - "timer_end": "2026-04-24T10:15:00Z", + "timestamp": "2026-04-24T10:15:00Z", "timer_mode": "countdown" } ``` @@ -243,35 +246,35 @@ Frontend rule: Recommended flow: 1. Load tournament via `GET /tournaments/:id` -2. Read `timer_mode` and `timer_end` +2. Read `timer_mode` and `timestamp` 3. Open ActionCable subscription for same tournament id 4. Replace local timer state whenever `timer.updated` arrives -5. Derive displayed time from `Date.now()` plus stored server timestamp +5. Derive displayed time from `Date.now()` plus stored backend timestamp Recommended client state: ```ts type TimerState = - | { mode: null; anchorIso: null } - | { mode: "countdown"; anchorIso: string } - | { mode: "countup"; anchorIso: string }; + | { mode: null; timestampIso: null } + | { mode: "countdown"; timestampIso: string } + | { mode: "countup"; timestampIso: string }; ``` Suggested derivation logic: ```ts function getTimerDisplay(state: TimerState, nowMs: number) { - if (state.mode === null || state.anchorIso === null) { + if (state.mode === null || state.timestampIso === null) { return { active: false, ms: 0 }; } - const anchorMs = new Date(state.anchorIso).getTime(); + const timestampMs = new Date(state.timestampIso).getTime(); if (state.mode === "countdown") { - return { active: true, ms: Math.max(0, anchorMs - nowMs) }; + return { active: true, ms: Math.max(0, timestampMs - nowMs) }; } - return { active: true, ms: Math.max(0, nowMs - anchorMs) }; + return { active: true, ms: Math.max(0, nowMs - timestampMs) }; } ``` @@ -284,13 +287,13 @@ Recommended rendering: Recommended local ticking: - keep backend state as timestamp + mode -- update rendered value with 250ms or 1s local interval +- update rendered value with local interval - do not poll backend every second Recommended write UX: - owner actions send explicit target request -- after successful response, update local state from response body +- after success, update local state from response body - websocket should converge all viewers to same state ## Follower Behavior @@ -300,7 +303,7 @@ Timer state is included in tournament sync snapshots. That means: - leader timer changes propagate to follower -- follower tournament exposes same `timer_end` / `timer_mode` +- follower tournament exposes same `timestamp` / `timer_mode` - follower websocket subscribers receive `timer.updated` - follower still rejects timer writes while read only @@ -322,4 +325,4 @@ Implemented coverage: Current note: - single-backend HTTP E2E timer specs pass -- follower blackbox task still has environment/network issue in existing Docker follow setup, so production-style follow stack verification is not green yet +- follower blackbox task still has existing Docker/network issue in follow setup, so production-style follow stack verification is not green yet diff --git a/e2e/lib/scenario_runner.rb b/e2e/lib/scenario_runner.rb index d6d3b24..9ad0682 100644 --- a/e2e/lib/scenario_runner.rb +++ b/e2e/lib/scenario_runner.rb @@ -30,9 +30,9 @@ module TurniereE2E timer_response = patch!( "/tournaments/#{tournament.fetch(:id)}/set_timer_end", - body: { timer_end_seconds: 600 } + body: { timestamp_seconds: 600 } ) - checkpoints[:timer_set] = { timer_end: timer_response.fetch(:timer_end) } + checkpoints[:timer_set] = { timestamp: timer_response.fetch(:timestamp) } return result(session: session, tournament: tournament, checkpoints: checkpoints, stop_at: stop_at) if stop_at == :timer_set upcoming_matches = get!( diff --git a/spec/controllers/tournaments_controller_spec.rb b/spec/controllers/tournaments_controller_spec.rb index 99fb5b7..f4c31cf 100644 --- a/spec/controllers/tournaments_controller_spec.rb +++ b/spec/controllers/tournaments_controller_spec.rb @@ -105,7 +105,7 @@ RSpec.describe TournamentsController, type: :controller do expected_keys = %i[ id name code public description playoff_teams_amount instant_finalists_amount intermediate_round_participants_amount - timer_end timer_mode owner_username stages teams team_action_lists + timestamp timer_mode owner_username stages teams team_action_lists ] expect(json.keys).to match_array(expected_keys) expect(json[:name]).to eq(@tournament.name) @@ -228,7 +228,7 @@ RSpec.describe TournamentsController, type: :controller do get :timer_end, params: { id: @tournament.to_param } expect(response).to be_successful - expect(deserialize_response(response)[:timer_end]).to eq(timer_end.iso8601) + expect(deserialize_response(response)[:timestamp]).to eq(timer_end.iso8601) expect(deserialize_response(response)[:timer_mode]).to eq('countdown') end end @@ -238,14 +238,14 @@ RSpec.describe TournamentsController, type: :controller do apply_authentication_headers_for @user end - it 'stores timezone-aware timer_end values without shifting them' do - timer_end = '2030-04-13T18:00:00+02:00' + it 'stores timezone-aware timestamp values without shifting them' do + timestamp = '2030-04-13T18:00:00+02:00' - patch :set_timer_end, params: { id: @tournament.to_param, timer_end: timer_end } + patch :set_timer_end, params: { id: @tournament.to_param, timestamp: timestamp } expect(response).to be_successful - expect(@tournament.reload.timer_end.iso8601).to eq(Time.iso8601(timer_end).utc.iso8601) - expect(deserialize_response(response)[:timer_end]).to eq(@tournament.timer_end.iso8601) + expect(@tournament.reload.timer_end.iso8601).to eq(Time.iso8601(timestamp).utc.iso8601) + expect(deserialize_response(response)[:timestamp]).to eq(@tournament.timer_end.iso8601) expect(deserialize_response(response)[:timer_mode]).to eq('countdown') end @@ -268,15 +268,15 @@ RSpec.describe TournamentsController, type: :controller do expect(response).to be_successful expect(@tournament.reload.timer_end).to be_nil expect(@tournament.timer_mode).to be_nil - expect(deserialize_response(response)[:timer_end]).to be_nil + expect(deserialize_response(response)[:timestamp]).to be_nil expect(deserialize_response(response)[:timer_mode]).to be_nil end - it 'rejects timer_end_seconds for countup mode' do - patch :set_timer_end, params: { id: @tournament.to_param, timer_mode: 'countup', timer_end_seconds: 120 } + it 'rejects timestamp_seconds for countup mode' do + patch :set_timer_end, params: { id: @tournament.to_param, timer_mode: 'countup', timestamp_seconds: 120 } expect(response).to have_http_status(:unprocessable_entity) - expect(deserialize_response(response)[:error]).to eq('timer_end_seconds is only allowed for countdown mode') + expect(deserialize_response(response)[:error]).to eq('timestamp_seconds is only allowed for countdown mode') end end diff --git a/spec/e2e/http/api_surface_spec.rb b/spec/e2e/http/api_surface_spec.rb index e6be9a3..7db4ad6 100644 --- a/spec/e2e/http/api_surface_spec.rb +++ b/spec/e2e/http/api_surface_spec.rb @@ -125,21 +125,21 @@ RSpec.describe 'Backend API surface HTTP E2E' do forbidden_update = other_client.patch("/tournaments/#{tournament.fetch(:id)}", body: { name: 'Not Allowed' }) unauthenticated_update = anonymous_client.patch("/tournaments/#{tournament.fetch(:id)}", body: { name: 'No Auth' }) - timer_seconds = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timer_end_seconds: 120 }) + timer_seconds = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timestamp_seconds: 120 }) timer_show = anonymous_client.get("/tournaments/#{tournament.fetch(:id)}/timer_end") timer_countup = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timer_mode: 'countup' }) timer_clear = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { clear_timer: true }) timer_double = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { - timer_end_seconds: 120, - timer_end: (Time.now.utc + 240).iso8601 + timestamp_seconds: 120, + timestamp: (Time.now.utc + 240).iso8601 }) - timer_invalid_seconds = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timer_end_seconds: 'nope' }) - timer_past = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timer_end_seconds: 0 }) + timer_invalid_seconds = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timestamp_seconds: 'nope' }) + timer_past = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timestamp_seconds: 0 }) timer_countup_seconds = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timer_mode: 'countup', - timer_end_seconds: 60 + timestamp_seconds: 60 }) - timer_forbidden = other_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timer_end_seconds: 60 }) + timer_forbidden = other_client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timestamp_seconds: 60 }) destroy_response = owner_client.delete("/tournaments/#{tournament.fetch(:id)}") after_destroy = anonymous_client.get("/tournaments/#{tournament.fetch(:id)}") @@ -160,26 +160,26 @@ RSpec.describe 'Backend API surface HTTP E2E' do expect(unauthenticated_update[:status]).to eq(401) expect(timer_seconds[:status]).to eq(200) - expect(timer_seconds.dig(:json, :timer_end)).to be_a(String) + expect(timer_seconds.dig(:json, :timestamp)).to be_a(String) expect(timer_seconds.dig(:json, :timer_mode)).to eq('countdown') expect(timer_show[:status]).to eq(200) - expect(timer_show.dig(:json, :timer_end)).to eq(timer_seconds.dig(:json, :timer_end)) + expect(timer_show.dig(:json, :timestamp)).to eq(timer_seconds.dig(:json, :timestamp)) expect(timer_show.dig(:json, :timer_mode)).to eq('countdown') expect(timer_countup[:status]).to eq(200) - expect(timer_countup.dig(:json, :timer_end)).to be_a(String) + expect(timer_countup.dig(:json, :timestamp)).to be_a(String) expect(timer_countup.dig(:json, :timer_mode)).to eq('countup') expect(timer_clear[:status]).to eq(200) - expect(timer_clear.dig(:json, :timer_end)).to be_nil + expect(timer_clear.dig(:json, :timestamp)).to be_nil expect(timer_clear.dig(:json, :timer_mode)).to be_nil expect(timer_double[:status]).to eq(422) - expect(timer_double.dig(:json, :error)).to eq('Only one of timer_end or timer_end_seconds is allowed') + expect(timer_double.dig(:json, :error)).to eq('Only one of timestamp or timestamp_seconds is allowed') expect(timer_invalid_seconds[:status]).to eq(422) expect(timer_invalid_seconds.dig(:json, :error)).to eq('Invalid seconds format') expect(timer_past[:status]).to eq(422) - expect(timer_past.dig(:json, :error)).to eq('Timer end must be in the future') + expect(timer_past.dig(:json, :error)).to eq('Timestamp must be in the future for countdown mode') expect(timer_countup_seconds[:status]).to eq(422) - expect(timer_countup_seconds.dig(:json, :error)).to eq('timer_end_seconds is only allowed for countdown mode') + expect(timer_countup_seconds.dig(:json, :error)).to eq('timestamp_seconds is only allowed for countdown mode') expect(timer_forbidden[:status]).to eq(403) expect([200, 204]).to include(destroy_response[:status]) diff --git a/spec/e2e/http/tournament_follow_sync_spec.rb b/spec/e2e/http/tournament_follow_sync_spec.rb index 1802516..90edc01 100644 --- a/spec/e2e/http/tournament_follow_sync_spec.rb +++ b/spec/e2e/http/tournament_follow_sync_spec.rb @@ -474,7 +474,7 @@ RSpec.describe 'Tournament follower sync HTTP E2E' do playoff_teams_amount: tournament.fetch(:playoff_teams_amount), instant_finalists_amount: tournament.fetch(:instant_finalists_amount), intermediate_round_participants_amount: tournament.fetch(:intermediate_round_participants_amount), - timer_end: tournament[:timer_end], + timestamp: tournament[:timestamp], teams: tournament.fetch(:teams).map { |team| team.slice(:name, :advancing_from_group_stage) }.sort_by { |team| team.fetch(:name) }, team_action_lists: tournament.fetch(:team_action_lists, []).sort_by { |list| list.fetch(:key) }.map do |list| { diff --git a/spec/e2e/http/tournament_timer_follow_sync_websocket_spec.rb b/spec/e2e/http/tournament_timer_follow_sync_websocket_spec.rb index 2511b83..c8e43db 100644 --- a/spec/e2e/http/tournament_timer_follow_sync_websocket_spec.rb +++ b/spec/e2e/http/tournament_timer_follow_sync_websocket_spec.rb @@ -38,26 +38,26 @@ RSpec.describe 'Tournament timer follower sync websocket HTTP E2E' do cable_client = TurniereE2E::ActionCableClient.new(base_url: follower_base_url) cable_client.subscribe_timer!(tournament_id: follower.fetch(:id)) initial_payload = cable_client.wait_for_timer_payload! - expect(initial_payload.fetch('timer_end')).to be_nil + expect(initial_payload.fetch('timestamp')).to be_nil expect(initial_payload.fetch('timer_mode')).to be_nil - countdown = source_client.patch("/tournaments/#{source.fetch(:id)}/set_timer_end", body: { timer_end_seconds: 120 }) + countdown = source_client.patch("/tournaments/#{source.fetch(:id)}/set_timer_end", body: { timestamp_seconds: 120 }) expect(countdown[:status]).to eq(200) countdown_payload = wait_for_timer_state!(cable_client, timer_mode: 'countdown') - expect(countdown_payload.fetch('timer_end')).to eq(countdown.dig(:json, :timer_end)) + expect(countdown_payload.fetch('timestamp')).to eq(countdown.dig(:json, :timestamp)) countup = source_client.patch("/tournaments/#{source.fetch(:id)}/set_timer_end", body: { timer_mode: 'countup' }) expect(countup[:status]).to eq(200) countup_payload = wait_for_timer_state!(cable_client, timer_mode: 'countup') - expect(countup_payload.fetch('timer_end')).to eq(countup.dig(:json, :timer_end)) + expect(countup_payload.fetch('timestamp')).to eq(countup.dig(:json, :timestamp)) clear = source_client.patch("/tournaments/#{source.fetch(:id)}/set_timer_end", body: { clear_timer: true }) expect(clear[:status]).to eq(200) cleared_payload = wait_for_timer_state!(cable_client, timer_mode: nil) - expect(cleared_payload.fetch('timer_end')).to be_nil + expect(cleared_payload.fetch('timestamp')).to be_nil ensure cable_client&.close end diff --git a/spec/e2e/http/tournament_timer_websocket_spec.rb b/spec/e2e/http/tournament_timer_websocket_spec.rb index 9fd18c3..f9d423e 100644 --- a/spec/e2e/http/tournament_timer_websocket_spec.rb +++ b/spec/e2e/http/tournament_timer_websocket_spec.rb @@ -29,26 +29,26 @@ RSpec.describe 'Tournament timer websocket HTTP E2E' do cable_client.subscribe_timer!(tournament_id: tournament.fetch(:id)) initial_payload = cable_client.wait_for_timer_payload! - expect(initial_payload.fetch('timer_end')).to be_nil + expect(initial_payload.fetch('timestamp')).to be_nil expect(initial_payload.fetch('timer_mode')).to be_nil - countdown = client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timer_end_seconds: 120 }) + countdown = client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timestamp_seconds: 120 }) expect(countdown[:status]).to eq(200) countdown_payload = wait_for_timer_state!(cable_client, timer_mode: 'countdown') - expect(countdown_payload.fetch('timer_end')).to eq(countdown.dig(:json, :timer_end)) + expect(countdown_payload.fetch('timestamp')).to eq(countdown.dig(:json, :timestamp)) countup = client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { timer_mode: 'countup' }) expect(countup[:status]).to eq(200) countup_payload = wait_for_timer_state!(cable_client, timer_mode: 'countup') - expect(countup_payload.fetch('timer_end')).to eq(countup.dig(:json, :timer_end)) + expect(countup_payload.fetch('timestamp')).to eq(countup.dig(:json, :timestamp)) clear = client.patch("/tournaments/#{tournament.fetch(:id)}/set_timer_end", body: { clear_timer: true }) expect(clear[:status]).to eq(200) cleared_payload = wait_for_timer_state!(cable_client, timer_mode: nil) - expect(cleared_payload.fetch('timer_end')).to be_nil + expect(cleared_payload.fetch('timestamp')).to be_nil ensure cable_client&.close end diff --git a/spec/services/tournament_snapshot_importer_spec.rb b/spec/services/tournament_snapshot_importer_spec.rb index 25d9d0b..4c9d1ef 100644 --- a/spec/services/tournament_snapshot_importer_spec.rb +++ b/spec/services/tournament_snapshot_importer_spec.rb @@ -183,7 +183,7 @@ RSpec.describe TournamentSnapshotImporter do playoff_teams_amount: tournament.playoff_teams_amount, instant_finalists_amount: tournament.instant_finalists_amount, intermediate_round_participants_amount: tournament.intermediate_round_participants_amount, - timer_end: tournament.timer_end&.iso8601, + timestamp: tournament.timer_end&.iso8601, timer_mode: tournament.timer_mode, teams: tournament.teams.order(:name).map { |team| team.name }, team_action_lists: tournament.team_action_lists.order(:key).map do |list|