|
- require 'rails_helper'
- require 'active_support/testing/time_helpers'
-
-
- RSpec.describe 'Theatres API', type: :request do
- include ActiveSupport::Testing::TimeHelpers
-
- around do |example|
- travel_to(Time.zone.parse('2026-03-18 21:00:00')) do
- example.run
- end
- end
-
- let(:member) { create(:user, :member, name: 'member user') }
- let(:other_user) { create(:user, :member, name: 'other user') }
-
- let!(:youtube_post) do
- Post.create!(
- title: 'youtube post',
- url: 'https://www.youtube.com/watch?v=spec123'
- )
- end
-
- let!(:other_post) do
- Post.create!(
- title: 'other post',
- url: 'https://example.com/posts/1'
- )
- end
-
- let!(:theatre) do
- Theatre.create!(
- name: 'spec theatre',
- opens_at: Time.zone.parse('2026-03-18 20:00:00'),
- kind: 0,
- created_by_user: member
- )
- end
-
- describe 'GET /theatres/:id' do
- subject(:do_request) do
- get "/theatres/#{theatre_id}"
- end
-
- context 'when theatre exists' do
- let(:theatre_id) { theatre.id }
-
- it 'returns theatre json' do
- do_request
-
- expect(response).to have_http_status(:ok)
- expect(json).to include(
- 'id' => theatre.id,
- 'name' => 'spec theatre'
- )
- expect(json).to have_key('opens_at')
- expect(json).to have_key('closes_at')
- expect(json).to have_key('created_at')
- expect(json).to have_key('updated_at')
-
- expect(json['created_by_user']).to include(
- 'id' => member.id,
- 'name' => 'member user'
- )
- end
- end
-
- context 'when theatre does not exist' do
- let(:theatre_id) { 999_999_999 }
-
- it 'returns 404' do
- do_request
- expect(response).to have_http_status(:not_found)
- end
- end
- end
-
- describe 'PUT /theatres/:id/watching' do
- subject(:do_request) do
- put "/theatres/#{theatre_id}/watching"
- end
-
- let(:theatre_id) { theatre.id }
-
- context 'when not logged in' do
- it 'returns 401' do
- sign_out
- do_request
- expect(response).to have_http_status(:unauthorized)
- end
- end
-
- context 'when theatre does not exist' do
- let(:theatre_id) { 999_999_999 }
-
- it 'returns 404' do
- sign_in_as(member)
- do_request
- expect(response).to have_http_status(:not_found)
- end
- end
-
- context 'when theatre has no host yet' do
- before do
- sign_in_as(member)
- end
-
- it 'creates watching row, assigns current user as host, and returns current theatre info' do
- expect { do_request }
- .to change { TheatreWatchingUser.count }.by(1)
-
- expect(response).to have_http_status(:ok)
-
- theatre.reload
- watch = TheatreWatchingUser.find_by!(theatre: theatre, user: member)
-
- expect(theatre.host_user_id).to eq(member.id)
- expect(watch.expires_at).to be_within(1.second).of(30.seconds.from_now)
-
- expect(json).to include(
- 'host_flg' => true,
- 'post_id' => nil,
- 'post_started_at' => nil
- )
-
- expect(json.fetch('watching_users')).to contain_exactly(
- {
- 'id' => member.id,
- 'name' => 'member user'
- }
- )
- end
- end
-
- context 'when current user is already watching' do
- let!(:watching_row) do
- TheatreWatchingUser.create!(
- theatre: theatre,
- user: member,
- expires_at: 5.seconds.from_now
- )
- end
-
- before do
- sign_in_as(member)
- end
-
- it 'refreshes expires_at without creating another row' do
- expect { do_request }
- .not_to change { TheatreWatchingUser.count }
-
- expect(response).to have_http_status(:ok)
-
- expect(watching_row.reload.expires_at)
- .to be_within(1.second).of(30.seconds.from_now)
- end
- end
-
- context 'when another active host exists' do
- before do
- TheatreWatchingUser.create!(
- theatre: theatre,
- user: other_user,
- expires_at: 10.minutes.from_now
- )
- theatre.update!(host_user: other_user)
- sign_in_as(member)
- end
-
- it 'does not steal host and returns host_flg false' do
- expect { do_request }
- .to change { TheatreWatchingUser.count }.by(1)
-
- expect(response).to have_http_status(:ok)
- expect(theatre.reload.host_user_id).to eq(other_user.id)
-
- expect(json).to include(
- 'host_flg' => false,
- 'post_id' => nil,
- 'post_started_at' => nil
- )
-
- expect(json.fetch('watching_users')).to contain_exactly(
- {
- 'id' => member.id,
- 'name' => 'member user'
- },
- {
- 'id' => other_user.id,
- 'name' => 'other user'
- }
- )
- end
- end
-
- context 'when host is set but no longer actively watching' do
- let(:started_at) { 2.minutes.ago }
-
- before do
- TheatreWatchingUser.create!(
- theatre: theatre,
- user: other_user,
- expires_at: 1.second.ago
- )
- theatre.update!(
- host_user: other_user,
- current_post: youtube_post,
- current_post_started_at: started_at
- )
- sign_in_as(member)
- end
-
- it 'reassigns host to current user and returns current post info' do
- expect { do_request }
- .to change { TheatreWatchingUser.count }.by(1)
-
- expect(response).to have_http_status(:ok)
-
- theatre.reload
- expect(theatre.host_user_id).to eq(member.id)
-
- expect(json['host_flg']).to eq(true)
- expect(json['post_id']).to eq(youtube_post.id)
- expect(Time.zone.parse(json['post_started_at']))
- .to be_within(1.second).of(started_at)
- end
- end
- end
-
- describe 'PATCH /theatres/:id/next_post' do
- subject(:do_request) do
- patch "/theatres/#{theatre_id}/next_post"
- end
-
- let(:theatre_id) { theatre.id }
-
- context 'when not logged in' do
- it 'returns 401' do
- sign_out
- do_request
- expect(response).to have_http_status(:unauthorized)
- end
- end
-
- context 'when theatre does not exist' do
- let(:theatre_id) { 999_999_999 }
-
- it 'returns 404' do
- sign_in_as(member)
- do_request
- expect(response).to have_http_status(:not_found)
- end
- end
-
- context 'when logged in but not host' do
- before do
- theatre.update!(host_user: other_user)
- sign_in_as(member)
- end
-
- it 'returns 403' do
- do_request
- expect(response).to have_http_status(:forbidden)
- end
- end
-
- context 'when current user is host' do
- before do
- theatre.update!(host_user: member)
- sign_in_as(member)
- end
-
- it 'sets current_post to an eligible post and updates current_post_started_at' do
- expect { do_request }
- .to change { theatre.reload.current_post_id }
- .from(nil).to(youtube_post.id)
-
- expect(response).to have_http_status(:no_content)
- expect(theatre.reload.current_post_started_at)
- .to be_within(1.second).of(Time.current)
- end
- end
-
- context 'when current user is host and no eligible post exists' do
- before do
- youtube_post.destroy!
- theatre.update!(
- host_user: member,
- current_post: other_post,
- current_post_started_at: 1.hour.ago
- )
- sign_in_as(member)
- end
-
- it 'still returns 204 and clears current_post' do
- do_request
-
- expect(response).to have_http_status(:no_content)
-
- theatre.reload
- expect(theatre.current_post_id).to be_nil
- expect(theatre.current_post_started_at)
- .to be_within(1.second).of(Time.current)
- end
- end
- end
- end
|