# frozen_string_literal: true require 'rails_helper' RSpec.describe 'Family', type: :request do let(:user) { create(:user) } let(:other_user) { create(:user) } let(:family) { create(:family, creator: user) } let!(:membership) { create(:family_membership, user: user, family: family, role: :owner) } before do stub_request(:any, 'https://api.github.com/repos/Freika/dawarich/tags') .to_return(status: 200, body: '[{"name": "1.0.0"}]', headers: {}) sign_in user end describe 'GET /family' do it 'shows the family page' do get "/family" expect(response).to have_http_status(:ok) end context 'when user is not in the family' do let(:outsider) { create(:user) } before { sign_in outsider } it 'redirects to new family path' do get "/family" expect(response).to redirect_to(new_family_path) end end end describe 'GET /family/new' do context 'when user is not in a family' do let(:user_without_family) { create(:user) } before { sign_in user_without_family } it 'renders the new family form' do get '/family/new' expect(response).to have_http_status(:ok) end end context 'when user is already in a family' do it 'redirects to family show page' do get '/family/new' expect(response).to redirect_to(family_path) end end end describe 'POST /family' do let(:user_without_family) { create(:user) } before { sign_in user_without_family } context 'with valid attributes' do let(:valid_attributes) { { family: { name: 'Test Family' } } } it 'creates a new family' do expect do post '/family', params: valid_attributes end.to change(Family, :count).by(1) end it 'creates a family membership for the user' do expect do post '/family', params: valid_attributes end.to change(Family::Membership, :count).by(1) end it 'redirects to the new family with success message' do post '/family', params: valid_attributes expect(response).to have_http_status(:found) expect(response.location).to eq family_url follow_redirect! expect(response.body).to include('Family created successfully!') end end context 'with invalid attributes' do let(:invalid_attributes) { { family: { name: '' } } } it 'does not create a family' do expect do post '/family', params: invalid_attributes end.not_to change(Family, :count) end it 'renders the new template with errors' do post '/family', params: invalid_attributes expect(response).to have_http_status(:unprocessable_content) end end end describe 'GET /family/edit' do it 'shows the edit form' do get "/family/edit" expect(response).to have_http_status(:ok) end context 'when user is not the owner' do before { membership.update!(role: :member) } it 'redirects due to authorization failure' do get "/family/edit" expect(response).to have_http_status(:see_other) expect(flash[:alert]).to include('not authorized') end end end describe 'PATCH /family' do let(:new_attributes) { { family: { name: 'Updated Family Name' } } } context 'with valid attributes' do it 'updates the family' do patch "/family", params: new_attributes family.reload expect(family.name).to eq('Updated Family Name') expect(response).to redirect_to(family_path) end end context 'with invalid attributes' do let(:invalid_attributes) { { family: { name: '' } } } it 'does not update the family' do original_name = family.name patch "/family", params: invalid_attributes family.reload expect(family.name).to eq(original_name) expect(response).to have_http_status(:unprocessable_content) end end context 'when user is not the owner' do before { membership.update!(role: :member) } it 'redirects due to authorization failure' do patch "/family", params: new_attributes expect(response).to have_http_status(:see_other) expect(flash[:alert]).to include('not authorized') end end end describe 'DELETE /family' do context 'when family has only one member' do it 'deletes the family' do expect { delete '/family' }.to change(Family, :count).by(-1) expect(response).to redirect_to(new_family_path) end end context 'when family has multiple members' do before do create(:family_membership, user: other_user, family: family, role: :member) end it 'does not delete the family' do expect { delete "/family" }.not_to change(Family, :count) expect(response).to redirect_to(family_path) follow_redirect! expect(response.body).to include('Cannot delete family with members') end end context 'when user is not the owner' do before { membership.update!(role: :member) } it 'redirects due to authorization failure' do delete "/family" expect(response).to have_http_status(:see_other) expect(flash[:alert]).to include('not authorized') end end end describe 'authorization for outsiders' do let(:outsider) { create(:user) } before { sign_in outsider } it 'denies access to show when user is not in family' do get "/family" expect(response).to redirect_to(new_family_path) end it 'redirects to family page when user is not in family for edit' do get "/family/edit" expect(response).to redirect_to(new_family_path) end it 'redirects to family page when user is not in family for update' do patch "/family", params: { family: { name: 'Hacked' } } expect(response).to redirect_to(new_family_path) end it 'redirects to family page when user is not in family for destroy' do delete "/family" expect(response).to redirect_to(new_family_path) end end describe 'authentication required' do before { sign_out user } it 'redirects to login for index' do get '/family' expect(response).to redirect_to(new_user_session_path) end it 'redirects to login for show' do get "/family" expect(response).to redirect_to(new_user_session_path) end it 'redirects to login for new' do get '/family/new' expect(response).to redirect_to(new_user_session_path) end it 'redirects to login for create' do post '/family', params: { family: { name: 'Test' } } expect(response).to redirect_to(new_user_session_path) end it 'redirects to login for edit' do get "/family/edit" expect(response).to redirect_to(new_user_session_path) end it 'redirects to login for update' do patch "/family", params: { family: { name: 'Test' } } expect(response).to redirect_to(new_user_session_path) end it 'redirects to login for destroy' do delete "/family" expect(response).to redirect_to(new_user_session_path) end end end