|
- require 'rails_helper'
-
- RSpec.describe 'Users', type: :request do
- let(:remote_ip) { '203.0.113.10' }
-
- before do
- allow_any_instance_of(ActionDispatch::Request)
- .to receive(:remote_ip)
- .and_return(remote_ip)
- end
-
- def auth_headers(user)
- { 'X-Transfer-Code' => user.inheritance_code }
- end
-
- describe 'POST /users' do
- it 'creates guest user, IpAddress and UserIp, and returns code' do
- expect {
- post '/users'
- }.to change(User, :count).by(1)
- .and change(IpAddress, :count).by(1)
- .and change(UserIp, :count).by(1)
-
- expect(response).to have_http_status(:created)
- expect(json['code']).to be_present
- expect(json['user']['role']).to eq('guest')
-
- user = User.last
- ip_address = IpAddress.find_by(ip_address: IPAddr.new(remote_ip).hton)
-
- expect(user.role).to eq('guest')
- expect(ip_address).to be_present
- expect(UserIp.exists?(user:, ip_address:)).to eq(true)
- end
-
- it 'returns 403 and does not create user when current IP address is banned' do
- IpAddress.create!(
- ip_address: IPAddr.new(remote_ip).hton,
- banned_at: Time.current
- )
-
- expect {
- post '/users'
- }.not_to change(User, :count)
-
- expect(response).to have_http_status(:forbidden)
- expect(UserIp.count).to eq(0)
- end
- end
-
- describe 'POST /users/code/renew' do
- it 'returns 401 when not logged in' do
- post '/users/code/renew'
-
- expect(response).to have_http_status(:unauthorized)
- end
-
- it 'returns 403 when current user is banned' do
- user = create(:user, :banned)
-
- post '/users/code/renew', headers: auth_headers(user)
-
- expect(response).to have_http_status(:forbidden)
- end
-
- it 'returns 403 when current IP address is banned' do
- user = create(:user)
-
- IpAddress.create!(
- ip_address: IPAddr.new(remote_ip).hton,
- banned_at: Time.current
- )
-
- post '/users/code/renew', headers: auth_headers(user)
-
- expect(response).to have_http_status(:forbidden)
- end
- end
-
- describe 'PUT /users/:id' do
- let(:user) { create(:user, name: 'old-name', role: 'guest') }
-
- it 'returns 401 when current_user id mismatch' do
- other_user = create(:user)
-
- put "/users/#{user.id}",
- params: { name: 'new-name' },
- headers: auth_headers(other_user)
-
- expect(response).to have_http_status(:unauthorized)
- end
-
- it 'returns 400 when name is blank' do
- put "/users/#{user.id}",
- params: { name: ' ' },
- headers: auth_headers(user)
-
- expect(response).to have_http_status(:bad_request)
- end
-
- it 'updates name and returns user slice' do
- put "/users/#{user.id}",
- params: { name: 'new-name' },
- headers: auth_headers(user)
-
- expect(response).to have_http_status(:ok)
- expect(json['id']).to eq(user.id)
- expect(json['name']).to eq('new-name')
-
- user.reload
- expect(user.name).to eq('new-name')
- end
-
- it 'returns 403 when current user is banned' do
- user.update!(banned_at: Time.current)
-
- put "/users/#{user.id}",
- params: { name: 'new-name' },
- headers: auth_headers(user)
-
- expect(response).to have_http_status(:forbidden)
-
- user.reload
- expect(user.name).to eq('old-name')
- end
-
- it 'returns 403 when current IP address is banned' do
- IpAddress.create!(
- ip_address: IPAddr.new(remote_ip).hton,
- banned_at: Time.current
- )
-
- put "/users/#{user.id}",
- params: { name: 'new-name' },
- headers: auth_headers(user)
-
- expect(response).to have_http_status(:forbidden)
-
- user.reload
- expect(user.name).to eq('old-name')
- end
- end
-
- describe 'POST /users/verify' do
- it 'returns valid:false when code not found' do
- post '/users/verify', params: { code: 'nope' }
-
- expect(response).to have_http_status(:ok)
- expect(json['valid']).to eq(false)
- end
-
- it 'returns 403 when current IP address is banned' do
- user = create(:user, inheritance_code: SecureRandom.uuid, role: 'guest')
-
- IpAddress.create!(
- ip_address: IPAddr.new(remote_ip).hton,
- banned_at: Time.current
- )
-
- expect {
- post '/users/verify', params: { code: user.inheritance_code }
- }.not_to change(UserIp, :count)
-
- expect(response).to have_http_status(:forbidden)
- end
-
- it 'returns 403 when verified user is banned' do
- user = create(
- :user,
- :banned,
- inheritance_code: SecureRandom.uuid,
- role: 'guest'
- )
-
- expect {
- post '/users/verify', params: { code: user.inheritance_code }
- }.not_to change(UserIp, :count)
-
- expect(response).to have_http_status(:forbidden)
- end
-
- it 'creates IpAddress and UserIp, and returns valid:true with user slice' do
- user = create(:user, inheritance_code: SecureRandom.uuid, role: 'guest')
-
- expect {
- post '/users/verify', params: { code: user.inheritance_code }
- }.to change(UserIp, :count).by(1)
- .and change(IpAddress, :count).by(1)
-
- expect(response).to have_http_status(:ok)
- expect(json['valid']).to eq(true)
- expect(json['user']['id']).to eq(user.id)
- expect(json['user']['inheritance_code']).to eq(user.inheritance_code)
- expect(json['user']['role']).to eq('guest')
-
- ip_address = IpAddress.find_by(ip_address: IPAddr.new(remote_ip).hton)
- expect(ip_address).to be_present
- expect(UserIp.exists?(user:, ip_address:)).to eq(true)
- end
-
- it 'is idempotent for same user and same IP address' do
- user = create(:user, inheritance_code: SecureRandom.uuid, role: 'guest')
-
- post '/users/verify', params: { code: user.inheritance_code }
- expect(response).to have_http_status(:ok)
-
- expect {
- post '/users/verify', params: { code: user.inheritance_code }
- }.not_to change(UserIp, :count)
-
- expect(response).to have_http_status(:ok)
- expect(json['valid']).to eq(true)
- end
-
- it 'creates another UserIp for same user and different IP address' do
- user = create(:user, inheritance_code: SecureRandom.uuid, role: 'guest')
-
- post '/users/verify', params: { code: user.inheritance_code }
- expect(response).to have_http_status(:ok)
-
- allow_any_instance_of(ActionDispatch::Request)
- .to receive(:remote_ip)
- .and_return('203.0.113.11')
-
- expect {
- post '/users/verify', params: { code: user.inheritance_code }
- }.to change(UserIp, :count).by(1)
-
- expect(response).to have_http_status(:ok)
- expect(json['valid']).to eq(true)
- end
- end
-
- describe 'GET /users/me' do
- it 'returns 404 when code not found' do
- get '/users/me', params: { code: 'nope' }
-
- expect(response).to have_http_status(:not_found)
- end
-
- it 'returns user slice when found' do
- user = create(:user, inheritance_code: SecureRandom.uuid, name: 'me', role: 'guest')
-
- get '/users/me', params: { code: user.inheritance_code }
-
- expect(response).to have_http_status(:ok)
- expect(json['id']).to eq(user.id)
- expect(json['name']).to eq('me')
- expect(json['inheritance_code']).to eq(user.inheritance_code)
- expect(json['role']).to eq('guest')
- end
-
- it 'returns 403 when current IP address is banned' do
- user = create(:user, inheritance_code: SecureRandom.uuid)
-
- IpAddress.create!(
- ip_address: IPAddr.new(remote_ip).hton,
- banned_at: Time.current
- )
-
- get '/users/me', params: { code: user.inheritance_code }
-
- expect(response).to have_http_status(:forbidden)
- end
- end
- end
|