Files
growstuff/spec/controllers/photos_controller_spec.rb
T
google-labs-jules[bot]andCloCkWeRX 9ebd98de2d Harden Rack::Attack and pagination against aggressive web crawlers
- Configure Allow2Ban in Rack::Attack to ban IPs requesting >500 pages per day for 1 week (7 days).
- Add honeypot route /dont-crawl-me disallowed in robots.txt and configure Fail2Ban in Rack::Attack to ban IPs visiting it for 7 days upon 1 hit.
- Update PhotosController#index to raise ActiveRecord::RecordNotFound when page parameter is out of bounds, returning 404 Not Found to crawlers instead of 200 OK.
- Add request and controller specs for Rack::Attack rules and pagination 404 responses.

Co-authored-by: CloCkWeRX <365751+CloCkWeRX@users.noreply.github.com>
2026-09-16 18:50:30 +12:00

236 lines
8.4 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
describe PhotosController do
login_member
describe 'GET index' do
describe 'all photos' do
let!(:photo) { create(:photo) }
before do
get :index
end
it "finds photos" do
expect(assigns(:photos).count).to eq 1
expect(assigns(:photos).first.id).to eq photo.id
end
it 'returns 404 not found when page is out of bounds' do
get :index, params: { page: 105 }
expect(response).to have_http_status(:not_found)
end
end
describe '#index crop photos' do
let!(:photo) { create(:photo, owner: member, title: 'no assocations photo') }
let!(:crop_photo) { create(:photo, owner: member, title: 'photos of planting') }
let!(:crop) { create(:crop) }
let!(:planting) { create(:planting, crop:, owner: member) }
before do
planting.photos << crop_photo
get :index, params: { crop_slug: crop.to_param }
end
describe "find photos by crop" do
it "assigns crop" do
expect(assigns(:crop)).to eq crop
end
it { expect(assigns(:photos).size).to eq 1 }
it { expect(assigns(:photos).first.crops).to include crop }
it { expect(assigns(:photos).first.id).to eq crop_photo.id }
end
end
end
describe "GET new" do
let(:tomato) { create(:tomato) }
let(:planting) { create(:planting, crop: tomato, owner: member) }
let(:garden) { create(:garden, owner: member) }
let(:harvest) { create(:harvest, owner: member) }
let(:member) { create(:member) }
let!(:auth) { create(:flickr_authentication, member:) }
before do
sign_in member
member.stub(:flickr_photos) { [[], 0] }
member.stub(:flickr_sets) { { "foo" => "bar" } }
member.stub(:flickr_auth_valid?) { true }
controller.stub(:current_member) { member }
end
describe "planting photos" do
before { get :new, params: { type: "planting", id: planting.id } }
it { expect(assigns(:flickr_auth)).to be_an_instance_of(Authentication) }
it { expect(assigns(:item)).to eq planting }
it { expect(flash[:alert]).not_to be_present }
it { expect(flash[:alert]).not_to be_present }
end
describe "harvest photos" do
before { get :new, params: { type: "harvest", id: harvest.id } }
it { expect(assigns(:item)).to eq harvest }
it { expect(flash[:alert]).not_to be_present }
end
describe "garden photos" do
before { get :new, params: { type: "garden", id: garden.id } }
it { expect(assigns(:item)).to eq garden }
it { expect(flash[:alert]).not_to be_present }
end
describe "filtering by tag" do
let(:tag) { "tomato" }
it "passes the tag to flickr_photos" do
expect(member).to receive(:flickr_photos).with(anything, nil, tag).and_return([[], 0])
get :new, params: { type: "planting", id: planting.id, tag: tag }
expect(assigns(:current_tag)).to eq tag
end
end
end
describe "POST create" do
before do
Photo.any_instance.stub(:flickr_metadata).and_return(title: "A Heartbreaking work of staggering genius",
license_name: "CC-BY",
license_url: "http://example.com/aybpl",
thumbnail_url: "http://example.com/thumb.jpg",
fullsize_url: "http://example.com/full.jpg",
link_url: "http://example.com")
end
let(:member) { create(:member) }
let(:garden) { create(:garden, owner: member) }
let(:planting) { create(:planting, garden:, owner: member) }
let(:harvest) { create(:harvest, owner: member) }
let(:photo) { create(:photo, owner: member) }
describe "with valid params" do
before { controller.stub(:current_member) { member } }
describe "attaches the photo to a planting" do
before do
post :create, params: {
photo: { source_id: photo.source_id, source: 'flickr' },
type: "planting", id: planting.id
}
end
it { expect(flash[:alert]).not_to be_present }
it { expect(Photo.last.plantings.first).to eq planting }
end
describe "doesn't attach a photo to a planting twice" do
before do
post :create, params: {
photo: { source_id: photo.source_id, source: 'flickr' }, type: "planting", id: planting.id
}
post :create, params: {
photo: { source_id: photo.source_id, source: 'flickr' }, type: "planting", id: planting.id
}
end
it { expect(flash[:alert]).not_to be_present }
it { expect(Photo.last.plantings.size).to eq 1 }
end
it "attaches the photo to a harvest" do
post :create, params: { photo: { source_id: photo.source_id, source: 'flickr' }, type: "harvest", id: harvest.id }
expect(flash[:alert]).not_to be_present
Photo.last.harvests.first.should eq harvest
end
describe "doesn't attach a photo to a harvest twice" do
before do
post :create, params: {
photo: { source_id: photo.source_id, source: 'flickr' }, type: "harvest", id: harvest.id
}
post :create, params: {
photo: { source_id: photo.source_id, source: 'flickr' }, type: "harvest", id: harvest.id
}
end
it { expect(flash[:alert]).not_to be_present }
it { expect(Photo.last.harvests.size).to eq 1 }
end
it "doesn't attach photo to a comment" do
comment = create(:comment)
expect do
post :create, params: {
photo: { source_id: photo.source_id, source: 'flickr' }, type: "comment", id: comment.id
}
end.to raise_error 'Photos not supported'
end
end
describe "for the second time" do
let(:planting) { create(:planting, owner: member) }
let(:valid_params) { { photo: { source_id: 1 }, id: planting.id, type: 'planting' } }
it "does not add a photo twice" do
expect { post :create, params: valid_params }.to change(Photo, :count).by(1)
expect { post :create, params: valid_params }.not_to change(Photo, :count)
end
end
describe "with matching owners" do
before { controller.stub(:current_member) { member } }
describe "creates the planting/photo link" do
let(:planting) { create(:planting, garden:, owner: member) }
let(:photo) { create(:photo, owner: member) }
before { post :create, params: { photo: { source_id: photo.source_id, source: 'flickr' }, type: "planting", id: planting.id } }
it { expect(flash[:alert]).not_to be_present }
it { expect(Photo.last.plantings.first).to eq planting }
end
describe "creates the harvest/photo link" do
before do
post :create, params: { photo: { source_id: photo.source_id, source: 'flickr' }, type: "harvest", id: harvest.id }
end
it { expect(flash[:alert]).not_to be_present }
it { expect(Photo.last.harvests.first).to eq harvest }
end
end
describe "with mismatched owners" do
let(:photo) { create(:photo) }
it "does not create the planting/photo link" do
# members will be auto-created, and different
another_planting = create(:planting)
expect do
post :create, params: {
photo: { source_id: photo.source_id, source: 'flickr' },
type: "planting", id: another_planting.id
}
end.to raise_error(ActiveRecord::RecordInvalid)
expect(Photo.last.plantings.first).not_to eq another_planting
end
it "does not create the harvest/photo link" do
# members will be auto-created, and different
another_harvest = create(:harvest)
expect do
post :create, params: {
photo: { source_id: photo.source_id, source: 'flickr' }, type: "harvest", id: another_harvest.id
}
end.to raise_error(ActiveRecord::RecordInvalid)
expect(Photo.last.harvests.first).not_to eq another_harvest
end
end
end
end