mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-10-31 03:09:06 -04:00
Keep every packet label when a side is photographed again, and use the newest photo of a side. Before, giving a second photo the same label cleared the older photo's, which let it back into the crop's photos and default picture. Carry the label through the HTML photo picker (its search form and the links that add a photo), so a packet photo added without JavaScript is labelled. Have the OCR script treat any network failure on an image (DNS, timeout) as that image failing, so the rest of the batch still runs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
693 lines
23 KiB
Ruby
693 lines
23 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
describe Crop do
|
|
context 'all fields present' do
|
|
let(:crop) { create(:tomato) }
|
|
|
|
it 'saves a basic crop' do
|
|
crop.save.should be(true)
|
|
end
|
|
|
|
it 'is fetchable from the database' do
|
|
crop.save
|
|
@crop2 = described_class.find_by(name: 'tomato')
|
|
@crop2.en_wikipedia_url.should eq("http://en.wikipedia.org/wiki/Tomato")
|
|
@crop2.slug.should eq("tomato")
|
|
end
|
|
|
|
it 'stringifies as the system name' do
|
|
crop.save
|
|
crop.to_s.should eq('tomato')
|
|
end
|
|
|
|
it 'has a creator' do
|
|
crop.save
|
|
crop.creator.should be_an_instance_of Member
|
|
end
|
|
end
|
|
|
|
context 'invalid data' do
|
|
it 'does not save a crop without a system name' do
|
|
crop = build(:crop, name: nil)
|
|
expect { crop.save }.to raise_error ActiveRecord::StatementInvalid
|
|
end
|
|
end
|
|
|
|
context 'ordering' do
|
|
before do
|
|
@uppercase = create(:uppercasecrop, created_at: 1.minute.ago)
|
|
@lowercase = create(:lowercasecrop, created_at: 2.days.ago)
|
|
end
|
|
|
|
it 'recent scope sorts by creation date' do
|
|
described_class.recent.first.should == @uppercase
|
|
end
|
|
end
|
|
|
|
context 'popularity' do
|
|
let(:tomato) { create(:tomato) }
|
|
let(:maize) { create(:maize) }
|
|
|
|
before do
|
|
create_list(:planting, 10, crop: maize)
|
|
create_list(:planting, 3, crop: tomato)
|
|
end
|
|
|
|
it "sorts by most plantings" do
|
|
expect(described_class.popular.first).to eq maize
|
|
create_list(:planting, 10, crop: tomato)
|
|
expect(described_class.popular.first).to eq tomato
|
|
end
|
|
end
|
|
|
|
it 'finds a default scientific name' do
|
|
@crop = create(:tomato)
|
|
expect(@crop.default_scientific_name).to be_nil
|
|
@sn = create(:solanum_lycopersicum, crop: @crop)
|
|
@crop.reload
|
|
expect(@crop.default_scientific_name.to_s).to eq @sn.name
|
|
end
|
|
|
|
it 'counts plantings' do
|
|
@crop = create(:tomato)
|
|
expect(@crop.plantings.size).to eq 0
|
|
@planting = create(:planting, crop: @crop)
|
|
@crop.reload
|
|
expect(@crop.plantings.size).to eq 1
|
|
end
|
|
|
|
context "wikipedia url" do
|
|
subject { build(:tomato, en_wikipedia_url: wikipedia_url) }
|
|
|
|
context 'not a url' do
|
|
let(:wikipedia_url) { 'this is not valid' }
|
|
|
|
it { expect(subject).not_to be_valid }
|
|
end
|
|
|
|
context 'http url' do
|
|
let(:wikipedia_url) { 'http://en.wikipedia.org/wiki/SomePage' }
|
|
|
|
it { expect(subject).to be_valid }
|
|
end
|
|
|
|
context 'with ssl' do
|
|
let(:wikipedia_url) { 'https://en.wikipedia.org/wiki/SomePage' }
|
|
|
|
it { expect(subject).to be_valid }
|
|
end
|
|
|
|
context 'with utf8 macrons' do
|
|
let(:wikipedia_url) { 'https://en.wikipedia.org/wiki/Māori' }
|
|
|
|
it { expect(subject).to be_valid }
|
|
end
|
|
|
|
context 'urlencoded' do
|
|
let(:wikipedia_url) { 'https://en.wikipedia.org/wiki/M%C4%81ori' }
|
|
|
|
it { expect(subject).to be_valid }
|
|
end
|
|
|
|
context 'with new lines in url' do
|
|
let(:wikipedia_url) { 'http://en.wikipedia.org/wiki/SomePage\n\nBrendaRocks' }
|
|
|
|
it { expect(subject).not_to be_valid }
|
|
end
|
|
|
|
context "with script tags in url" do
|
|
let(:wikipedia_url) { 'http://en.wikipedia.org/wiki/SomePage<script>alert(\'BrendaRocks\')</script>' }
|
|
|
|
it { expect(subject).not_to be_valid }
|
|
end
|
|
end
|
|
|
|
context 'varieties' do
|
|
it 'has a crop hierarchy' do
|
|
@tomato = create(:tomato)
|
|
@roma = create(:roma, parent_id: @tomato.id)
|
|
expect(@roma.parent).to eq @tomato
|
|
expect(@tomato.varieties).to eq [@roma]
|
|
end
|
|
|
|
it 'toplevel scope works' do
|
|
@tomato = create(:tomato)
|
|
@roma = create(:roma, parent_id: @tomato.id)
|
|
expect(described_class.toplevel).to eq [@tomato]
|
|
end
|
|
end
|
|
|
|
context 'photos' do
|
|
shared_examples 'has default photo' do
|
|
it { expect(described_class.has_photos).to include(crop) }
|
|
end
|
|
|
|
let!(:crop) { create(:tomato) }
|
|
|
|
context 'with a planting photo' do
|
|
let!(:photo) { create(:photo, owner: planting.owner) }
|
|
let!(:planting) { create(:planting, crop:) }
|
|
|
|
before { planting.photos << photo }
|
|
|
|
it { expect(crop.default_photo).to eq photo }
|
|
|
|
it 'caches thumbnail_url string in Rails.cache' do
|
|
expected_url = photo.source == 'flickr' ? photo.fullsize_url : photo.thumbnail_url
|
|
expect(crop.thumbnail_url).to eq expected_url
|
|
cached_value = Rails.cache.read("#{crop.cache_key_with_version}/thumbnail_url")
|
|
expect(cached_value).to eq expected_url
|
|
end
|
|
|
|
include_examples 'has default photo'
|
|
end
|
|
|
|
context 'with a harvest photo' do
|
|
let!(:harvest) { create(:harvest, crop:) }
|
|
let!(:photo) { create(:photo, owner: harvest.owner) }
|
|
|
|
before { harvest.photos << photo }
|
|
|
|
it { expect(crop.default_photo).to eq photo }
|
|
|
|
include_examples 'has default photo'
|
|
|
|
context 'and planting photo' do
|
|
let(:planting) { create(:planting, crop:) }
|
|
let!(:planting_photo) { create(:photo, owner: planting.owner) }
|
|
|
|
before { planting.photos << planting_photo }
|
|
|
|
it 'prefers the planting photo' do
|
|
expect(crop.default_photo.id).to eq planting_photo.id
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with a photo of a seed packet' do
|
|
let(:seed) { create(:seed, crop:) }
|
|
let!(:packet_photo) { create(:photo, owner: seed.owner, likes_count: 10) }
|
|
|
|
before { seed.photo_associations.create!(photo: packet_photo, label: 'back') }
|
|
|
|
it('is not one of the crop photos') { expect(crop.photos).not_to include(packet_photo) }
|
|
it('is not the default photo') { expect(crop.default_photo).to be_nil }
|
|
it('does not make the crop interesting') { expect(described_class.has_photos).not_to include(crop) }
|
|
|
|
context 'with the same photo on another seed, with no label there' do
|
|
let(:other_seed) { create(:seed, crop:, owner: seed.owner) }
|
|
|
|
before { other_seed.photos << packet_photo }
|
|
|
|
it('is still not one of the crop photos') { expect(crop.photos).not_to include(packet_photo) }
|
|
it('is still not the default photo') { expect(crop.default_photo).to be_nil }
|
|
end
|
|
|
|
context 'with the packet photographed again, so a newer photo has the label' do
|
|
before { seed.photo_associations.create!(photo: create(:photo, owner: seed.owner), label: 'back') }
|
|
|
|
it('still leaves out the older packet photo') { expect(crop.photos).not_to include(packet_photo) }
|
|
it('leaves out the newer one too') { expect(crop.photos).to be_empty }
|
|
end
|
|
|
|
context 'with a photo of the seeds too' do
|
|
let!(:seed_photo) { create(:photo, owner: seed.owner) }
|
|
|
|
before { seed.photos << seed_photo }
|
|
|
|
it('has the photo of the seeds') { expect(crop.photos).to contain_exactly(seed_photo) }
|
|
end
|
|
end
|
|
|
|
context 'with no plantings or harvests' do
|
|
it 'has no default photo' do
|
|
expect(crop.default_photo).to be_nil
|
|
end
|
|
|
|
it { expect(crop.photos.size).to eq 0 }
|
|
it { expect(crop.photos.by_model(Planting).size).to eq 0 }
|
|
it { expect(crop.photos.by_model(Harvest).size).to eq 0 }
|
|
it { expect(crop.photos.by_model(Seed).size).to eq 0 }
|
|
end
|
|
|
|
describe 'finding all photos' do
|
|
let(:planting) { create(:planting, crop:) }
|
|
let(:harvest) { create(:harvest, crop:) }
|
|
let(:seed) { create(:seed, crop:) }
|
|
|
|
before do
|
|
# Add photos to all
|
|
planting.photos << create(:photo, owner: planting.owner)
|
|
harvest.photos << create(:photo, owner: harvest.owner)
|
|
seed.photos << create(:photo, owner: seed.owner)
|
|
end
|
|
|
|
it { expect(crop.photos.size).to eq 3 }
|
|
it { expect(crop.photos.by_model(Planting).size).to eq 1 }
|
|
it { expect(crop.photos.by_model(Harvest).size).to eq 1 }
|
|
it { expect(crop.photos.by_model(Seed).size).to eq 1 }
|
|
end
|
|
end
|
|
|
|
context 'sunniness' do
|
|
let(:crop) { create(:tomato) }
|
|
|
|
it 'returns a hash of sunniness values' do
|
|
create(:sunny_planting, crop:)
|
|
create(:sunny_planting, crop:)
|
|
create(:semi_shady_planting, crop:)
|
|
create(:shady_planting, crop:)
|
|
crop.sunniness.should be_an_instance_of Hash
|
|
end
|
|
|
|
it 'counts each sunniness value' do
|
|
create(:sunny_planting, crop:)
|
|
create(:sunny_planting, crop:)
|
|
create(:semi_shady_planting, crop:)
|
|
create(:shady_planting, crop:)
|
|
crop.sunniness.should == { 'sun' => 2, 'shade' => 1, 'semi-shade' => 1 }
|
|
end
|
|
|
|
it 'ignores unused sunniness values' do
|
|
create(:sunny_planting, crop:)
|
|
create(:sunny_planting, crop:)
|
|
create(:semi_shady_planting, crop:)
|
|
crop.sunniness.should == { 'sun' => 2, 'semi-shade' => 1 }
|
|
end
|
|
end
|
|
|
|
context 'planted_from' do
|
|
let(:crop) { create(:tomato) }
|
|
|
|
it 'returns a hash of sunniness values' do
|
|
create(:seed_planting, crop:)
|
|
create(:seed_planting, crop:)
|
|
create(:seedling_planting, crop:)
|
|
create(:cutting_planting, crop:)
|
|
crop.planted_from.should be_an_instance_of Hash
|
|
end
|
|
|
|
it 'counts each planted_from value' do
|
|
create(:seed_planting, crop:)
|
|
create(:seed_planting, crop:)
|
|
create(:seedling_planting, crop:)
|
|
create(:cutting_planting, crop:)
|
|
crop.planted_from.should == { 'seed' => 2, 'seedling' => 1, 'cutting' => 1 }
|
|
end
|
|
|
|
it 'ignores unused planted_from values' do
|
|
create(:seed_planting, crop:)
|
|
create(:seed_planting, crop:)
|
|
create(:seedling_planting, crop:)
|
|
crop.planted_from.should == { 'seed' => 2, 'seedling' => 1 }
|
|
end
|
|
end
|
|
|
|
context 'popular plant parts' do
|
|
let(:crop) { create(:tomato) }
|
|
|
|
it 'returns a hash of plant_part values' do
|
|
crop.popular_plant_parts.should be_an_instance_of Hash
|
|
end
|
|
|
|
it 'counts each plant_part value' do
|
|
@fruit = create(:plant_part)
|
|
@seed = create(:plant_part, name: 'seed')
|
|
@root = create(:plant_part, name: 'root')
|
|
@bulb = create(:plant_part, name: 'bulb')
|
|
@harvest1 = create(:harvest,
|
|
crop:,
|
|
plant_part: @fruit)
|
|
@harvest2 = create(:harvest,
|
|
crop:,
|
|
plant_part: @fruit)
|
|
@harvest3 = create(:harvest,
|
|
crop:,
|
|
plant_part: @seed)
|
|
@harvest4 = create(:harvest,
|
|
crop:,
|
|
plant_part: @root)
|
|
crop.popular_plant_parts.should == { [@fruit.id, @fruit.name] => 2,
|
|
[@seed.id, @seed.name] => 1,
|
|
[@root.id, @root.name] => 1 }
|
|
end
|
|
end
|
|
|
|
context 'interesting' do
|
|
subject { described_class.interesting }
|
|
|
|
# first, a couple of candidate crops
|
|
let(:first_crop) { create(:crop) }
|
|
let(:second_crop) { create(:crop) }
|
|
|
|
let(:first_crop_planting) { first_crop.plantings.first }
|
|
let(:second_crop_planting) { second_crop.plantings.first }
|
|
|
|
let(:member) { create(:member, login_name: 'pikachu') }
|
|
|
|
describe 'lists interesting crops' do
|
|
before do
|
|
# they need 3+ plantings each to be interesting
|
|
create_list(:planting, 3, crop: first_crop, owner: member)
|
|
create_list(:planting, 3, crop: second_crop, owner: member)
|
|
# crops need 3+ photos to be interesting
|
|
first_crop_planting.photos = create_list :photo, 3, owner: member
|
|
second_crop_planting.photos = create_list :photo, 3, owner: member
|
|
end
|
|
|
|
it { is_expected.to include first_crop }
|
|
it { is_expected.to include second_crop }
|
|
it { expect(subject.size).to eq 2 }
|
|
end
|
|
|
|
describe 'crops without plantings are not interesting' do
|
|
before do
|
|
# only first_crop has plantings
|
|
create_list(:planting, 3, crop: first_crop, owner: member)
|
|
# ... and photos
|
|
first_crop_planting.photos = create_list(:photo, 3, owner: member)
|
|
end
|
|
|
|
it { is_expected.to include first_crop }
|
|
it { is_expected.not_to include second_crop }
|
|
it { expect(subject.size).to eq 1 }
|
|
end
|
|
|
|
describe 'crops without photos are not interesting' do
|
|
before do
|
|
# both crops have plantings
|
|
create_list(:planting, 3, crop: first_crop, owner: member)
|
|
create_list(:planting, 3, crop: second_crop, owner: member)
|
|
|
|
# but only first_crop has photos
|
|
first_crop_planting.photos = create_list(:photo, 3, owner: member)
|
|
end
|
|
|
|
it { is_expected.to include first_crop }
|
|
it { is_expected.not_to include second_crop }
|
|
it { expect(subject.size).to eq 1 }
|
|
end
|
|
end
|
|
|
|
context "harvests" do
|
|
let!(:crop) { create(:crop) }
|
|
let!(:harvest) { create(:harvest, crop:) }
|
|
|
|
it "has harvests" do
|
|
expect(crop.harvests).to eq [harvest]
|
|
end
|
|
end
|
|
|
|
it "doesn't duplicate plant_parts" do
|
|
@maize = create(:maize)
|
|
@pp1 = create(:plant_part)
|
|
@h1 = create(:harvest, crop: @maize, plant_part: @pp1)
|
|
@h2 = create(:harvest, crop: @maize, plant_part: @pp1)
|
|
expect(@maize.plant_parts).to eq [@pp1]
|
|
end
|
|
|
|
context "csv loading" do
|
|
before do
|
|
# don't use 'let' for this -- we need to actually create it,
|
|
# regardless of whether it's used.
|
|
@cropbot = create(:cropbot)
|
|
end
|
|
|
|
context "scientific names" do
|
|
it "adds a scientific name to a crop that has none" do
|
|
row = ["parent", "http://en.wikipedia.org/wiki/Parent", "", "Foo bar"]
|
|
tomato = CsvImporter.new.import_crop(row)
|
|
expect(tomato.scientific_names.size).to eq 1
|
|
expect(tomato.default_scientific_name.to_s).to eq "Foo bar"
|
|
end
|
|
|
|
it "picks up scientific name from parent crop if available" do
|
|
parent = CsvImporter.new.import_crop(
|
|
["parent", "http://en.wikipedia.org/wiki/Parent", "", "Parentis cropis"]
|
|
)
|
|
|
|
tomato = CsvImporter.new.import_crop(
|
|
["Tomato", "http://en.wikipedia.org/wiki/Parent", "parent"]
|
|
)
|
|
|
|
expect(tomato.parent).to eq parent
|
|
expect(tomato.parent.default_scientific_name.to_s).to eq "Parentis cropis"
|
|
expect(tomato.default_scientific_name.to_s).to eq "Parentis cropis"
|
|
end
|
|
|
|
it "doesn't add a duplicate scientific name" do
|
|
row = ["parent", "http://en.wikipedia.org/wiki/Parent", "", "Foo bar, Foo bar"]
|
|
tomato = CsvImporter.new.import_crop(row)
|
|
expect(tomato.scientific_names.size).to eq 1
|
|
end
|
|
|
|
it "doesn't add a duplicate scientific name from parent" do
|
|
CsvImporter.new.import_crop(
|
|
["parent", "http://en.wikipedia.org/wiki/Parent", "", "Parentis cropis"]
|
|
)
|
|
tomato = CsvImporter.new.import_crop(
|
|
["Tomato", "http://en.wikipedia.org/wiki/Parent", "parent", "Parentis cropis"]
|
|
)
|
|
expect(tomato.scientific_names.size).to eq 1
|
|
end
|
|
|
|
it "loads a crop with multiple scientific names" do
|
|
row = ["parent", "http://en.wikipedia.org/wiki/Parent", "", "Foo,Bar"]
|
|
tomato = CsvImporter.new.import_crop(row)
|
|
expect(tomato.scientific_names[0].name).to eq "Foo"
|
|
expect(tomato.scientific_names[1].name).to eq "Bar"
|
|
end
|
|
|
|
it "loads multiple scientific names with variant spacing" do
|
|
row = ["parent", "http://en.wikipedia.org/wiki/Parent", "", "Baz, Quux"]
|
|
tomato = CsvImporter.new.import_crop(row)
|
|
expect(tomato.scientific_names.size).to eq 2
|
|
expect(tomato.scientific_names[0].name).to eq "Baz"
|
|
expect(tomato.scientific_names[1].name).to eq "Quux"
|
|
end
|
|
end # scientific names
|
|
|
|
context "alternate names" do
|
|
it "loads an alternate name" do
|
|
row = ["tomato", "http://en.wikipedia.org/wiki/Parent", "", "", "Foo"]
|
|
tomato = CsvImporter.new.import_crop(row)
|
|
expect(tomato.alternate_names.size).to eq 1
|
|
expect(tomato.alternate_names.last.name).to eq "Foo"
|
|
end
|
|
|
|
it "adds multiple alternate names" do
|
|
row = ["tomato", "http://en.wikipedia.org/wiki/Parent", "", "", "Foo, Bar"]
|
|
tomato = CsvImporter.new.import_crop(row)
|
|
expect(tomato.alternate_names.size).to eq 2
|
|
expect(tomato.alternate_names[0].name).to eq "Foo"
|
|
expect(tomato.alternate_names[1].name).to eq "Bar"
|
|
end
|
|
|
|
it "adds multiple alt names with variant spacing" do
|
|
row = ["tomato", "http://en.wikipedia.org/wiki/Parent", "", "", "Foo,Bar,Baz, Quux"]
|
|
tomato = CsvImporter.new.import_crop(row)
|
|
expect(tomato.alternate_names.size).to eq 4
|
|
expect(tomato.alternate_names[0].name).to eq "Foo"
|
|
expect(tomato.alternate_names[1].name).to eq "Bar"
|
|
expect(tomato.alternate_names[2].name).to eq "Baz"
|
|
expect(tomato.alternate_names[3].name).to eq "Quux"
|
|
end
|
|
|
|
it "Adds a duplicate alternate name for second crop" do
|
|
row = ["tomato", "http://en.wikipedia.org/wiki/tomato", "", "", "Foo"]
|
|
tomato = CsvImporter.new.import_crop(row)
|
|
row = ["tomoto", "http://en.wikipedia.org/wiki/tomoto", "", "", "Foo"]
|
|
tomoto = CsvImporter.new.import_crop(row)
|
|
expect(tomato.alternate_names.size).to eq 1
|
|
expect(tomoto.alternate_names.size).to eq 1
|
|
end
|
|
end # alternate names
|
|
|
|
it "loads the simplest possible crop" do
|
|
tomato_row = ["tomato", "http://en.wikipedia.org/wiki/Tomato"]
|
|
tomato = CsvImporter.new.import_crop(tomato_row)
|
|
|
|
expect(tomato.name).to eq "tomato"
|
|
expect(tomato.en_wikipedia_url).to eq 'http://en.wikipedia.org/wiki/Tomato'
|
|
expect(tomato.creator).to eq @cropbot
|
|
end
|
|
|
|
it "loads a crop with a scientific name" do
|
|
tomato_row = ["tomato", "http://en.wikipedia.org/wiki/Tomato", "", "Solanum lycopersicum"]
|
|
tomato = CsvImporter.new.import_crop(tomato_row)
|
|
|
|
expect(tomato.name).to eq "tomato"
|
|
expect(tomato.scientific_names.size).to eq 1
|
|
expect(tomato.scientific_names.last.name).to eq "Solanum lycopersicum"
|
|
end
|
|
|
|
it "loads a crop with an alternate name" do
|
|
crop = CsvImporter.new.import_crop(
|
|
["tomato", "http://en.wikipedia.org/wiki/Tomato", nil, nil, "Foo"]
|
|
)
|
|
|
|
expect(crop.name).to eq "tomato"
|
|
expect(crop.alternate_names.size).to eq 1
|
|
expect(crop.alternate_names.last.name).to eq "Foo"
|
|
end
|
|
|
|
it "loads a crop with a parent" do
|
|
parent = create(:crop, name: 'parent')
|
|
crop = CsvImporter.new.import_crop(
|
|
["tomato", "http://en.wikipedia.org/wiki/Tomato", "parent"]
|
|
)
|
|
expect(crop.parent).to eq parent
|
|
end
|
|
|
|
it "loads a crop with a missing parent" do
|
|
tomato_row = "tomato,http://en.wikipedia.org/wiki/Tomato,parent"
|
|
|
|
CSV.parse(tomato_row) do |row|
|
|
CsvImporter.new.import_crop(row)
|
|
end
|
|
|
|
loaded = described_class.last
|
|
expect(loaded.parent).to be_nil
|
|
end
|
|
|
|
it "doesn't add unnecessary duplicate crops" do
|
|
tomato_row = "tomato,http://en.wikipedia.org/wiki/Tomato,,Solanum lycopersicum"
|
|
|
|
CSV.parse(tomato_row) do |row|
|
|
CsvImporter.new.import_crop(row)
|
|
end
|
|
|
|
loaded = described_class.last
|
|
expect(loaded.name).to eq "tomato"
|
|
expect(loaded.en_wikipedia_url).to eq 'http://en.wikipedia.org/wiki/Tomato'
|
|
expect(loaded.creator).to eq @cropbot
|
|
end
|
|
end
|
|
|
|
context "crop-post association" do
|
|
let!(:tomato) { create(:tomato) }
|
|
let!(:maize) { create(:maize) }
|
|
let!(:post) { create(:post, body: "[maize](crop)[tomato](crop)[tomato](crop)") }
|
|
|
|
describe "destroying a crop" do
|
|
before do
|
|
tomato.destroy
|
|
end
|
|
|
|
it "deletes the association between post and the crop(tomato)" do
|
|
expect(Post.find(post.id).crops).to eq [maize]
|
|
end
|
|
|
|
it "does not delete the posts" do
|
|
expect(Post.find(post.id)).not_to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
context "destroying a crop" do
|
|
let!(:crop_a) { create(:crop) }
|
|
let!(:crop_b) { create(:crop) }
|
|
|
|
before do
|
|
CropCompanion.create(crop_a: crop_a, crop_b: crop_b)
|
|
CropCompanion.create(crop_a: crop_b, crop_b: crop_a)
|
|
end
|
|
|
|
it "destroys companion links" do
|
|
expect { crop_a.destroy }.to change(CropCompanion, :count).from(2).to(0)
|
|
end
|
|
end
|
|
|
|
context "crop rejections" do
|
|
let!(:rejected_reason) do
|
|
create(:crop, name: 'tomato',
|
|
approval_status: 'rejected',
|
|
reason_for_rejection: 'not edible')
|
|
end
|
|
let!(:rejected_other) do
|
|
create(:crop, name: 'tomato',
|
|
approval_status: 'rejected',
|
|
reason_for_rejection: 'other',
|
|
rejection_notes: 'blah blah blah')
|
|
end
|
|
|
|
describe "rejecting a crop" do
|
|
it "gives reason if a default option" do
|
|
expect(rejected_reason.rejection_explanation).to eq "not edible"
|
|
end
|
|
|
|
it "shows rejection notes if reason was other" do
|
|
expect(rejected_other.rejection_explanation).to eq "blah blah blah"
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'its size on a garden layout' do
|
|
let(:tomato) { create(:crop, name: 'tomato') }
|
|
let(:variety) { create(:crop, name: 'island bay tomato', parent: tomato) }
|
|
|
|
it 'has none until someone sets it' do
|
|
expect(tomato.layout_diameter).to be_nil
|
|
end
|
|
|
|
it 'follows its parent crop until it has its own' do
|
|
tomato.update!(default_diameter: 2)
|
|
expect(variety.layout_diameter).to eq 2
|
|
|
|
variety.update!(default_diameter: 1.5)
|
|
expect(variety.layout_diameter).to eq 1.5
|
|
end
|
|
|
|
it 'must be more than nothing, and no more than a plant can be drawn' do
|
|
expect(build(:crop, default_diameter: 0)).not_to be_valid
|
|
expect(build(:crop, default_diameter: Plant::MAX_DIAMETER + 1)).not_to be_valid
|
|
expect(build(:crop, default_diameter: 0.5)).to be_valid
|
|
end
|
|
end
|
|
|
|
describe 'its icon' do
|
|
let(:tomato) { create(:crop, name: 'tomato') }
|
|
let(:variety) { create(:crop, name: 'island bay tomato', parent: tomato) }
|
|
|
|
it 'has none until one is chosen, or OpenFarm had one' do
|
|
expect(tomato.svg_icon).to be_nil
|
|
end
|
|
|
|
it "draws the app's icon chosen for it" do
|
|
tomato.update!(icon: 'tomato')
|
|
expect(tomato.svg_icon).to eq Rails.root.join('app/assets/images/crops/tomato.svg').read
|
|
end
|
|
|
|
it 'prefers its chosen icon to the one OpenFarm had' do
|
|
tomato.update!(icon: 'tomato', openfarm_data: { 'attributes' => { 'svg_icon' => '<svg>openfarm</svg>' } })
|
|
expect(tomato.svg_icon).to include('viewBox="0 0 32 32"')
|
|
end
|
|
|
|
it 'falls back to the one OpenFarm had' do
|
|
tomato.update!(openfarm_data: { 'attributes' => { 'svg_icon' => '<svg>openfarm</svg>' } })
|
|
expect(tomato.svg_icon).to eq '<svg>openfarm</svg>'
|
|
end
|
|
|
|
it 'follows its parent crop until it has its own' do
|
|
tomato.update!(icon: 'tomato')
|
|
expect(variety.svg_icon).to eq tomato.svg_icon
|
|
|
|
variety.update!(icon: 'cherries')
|
|
expect(variety.svg_icon).to eq described_class.icon_svg('cherries')
|
|
end
|
|
|
|
it "must be one of the app's icons" do
|
|
expect(build(:crop, icon: 'tomato')).to be_valid
|
|
expect(build(:crop, icon: 'dragon')).not_to be_valid
|
|
expect(build(:crop, icon: '')).to be_valid
|
|
end
|
|
|
|
it 'lists the icons there are to choose from' do
|
|
expect(described_class.icon_names).to include('tomato', 'herb', 'leafy_green')
|
|
expect(described_class.icon_names).to eq described_class.icon_names.sort
|
|
end
|
|
end
|
|
end
|