mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-10-31 03:09:06 -04:00
* Remove unused bluecloth gem Last released in 2011. It was only required, never called; kramdown renders the markdown. * Add specs for Tilt markdown rendering and posts markdown partial * Remove rails_12factor gem Last released in 2014. Rails 5+ does this via RAILS_SERVE_STATIC_FILES and RAILS_LOG_TO_STDOUT, which production.rb already reads. * Remove memcachier gem from Gemfile and Gemfile.lock * Remove unused webrat gem Last released in 2011. Nothing requires it; the specs use Rails' assert_select and Capybara matchers. Only generator comments mention it. * Remove crowdin-cli gem Last released in 2018. Crowdin now ships its own standalone CLI, which reads crowdin.yml and doesn't need the gem. Also drops the dependencies only it used (rest-client, gli, crowdin-api and friends). * Removes the crowdin-cli gem (last released 2018), the dependencies only it needed, and crowdin.yml. Crowdin was never set up: the config still has a placeholder project ID and nothing else in the repo mentions it. Assisted by Claude * Remove newsletter functionality and associated views and tests * Remove newsletter functionality and related code across the application * keep the column * Remove stale test step * add back newsletter * Add migration to update newsletter column comment in members table --------- Co-authored-by: Daniel O'Connor <daniel.oconnor@gmail.com>
402 lines
11 KiB
Ruby
402 lines
11 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
describe Member do
|
|
context 'valid member' do
|
|
let!(:member) { create(:member, login_name: 'hinemoa') }
|
|
|
|
describe 'should be fetchable from the database' do
|
|
subject { Member.find(member.id) }
|
|
|
|
it { is_expected.to be_an_instance_of Member }
|
|
it { expect(subject.encrypted_password).not_to be_nil }
|
|
end
|
|
|
|
describe 'should have a friendly slug' do
|
|
it { expect(member.slug).to eq('hinemoa') }
|
|
end
|
|
|
|
it 'has a bio' do
|
|
member.bio = 'I love seeds'
|
|
expect(member.bio).to eq 'I love seeds'
|
|
end
|
|
|
|
it 'has a default garden' do
|
|
expect(member.gardens.count).to eq 1
|
|
end
|
|
|
|
it "doesn't show email by default" do
|
|
expect(member.show_email).to be false
|
|
end
|
|
|
|
it 'stringifies as the login_name' do
|
|
expect(member.to_s).to eq 'hinemoa'
|
|
end
|
|
|
|
it 'is able to fetch posts' do
|
|
post = create(:post, author: member)
|
|
expect(member.posts).to eq [post]
|
|
end
|
|
|
|
it 'is able to fetch gardens' do
|
|
expect(member.gardens.first.name).to eq "Garden"
|
|
end
|
|
|
|
it 'has many plantings' do
|
|
create(:planting, owner: member)
|
|
member.plantings.size.should eq 1
|
|
end
|
|
|
|
it "has many comments" do
|
|
create(:comment, author: member)
|
|
create(:comment, author: member)
|
|
member.comments.size.should == 2
|
|
end
|
|
|
|
it "has many forums" do
|
|
create(:forum, owner: member)
|
|
create(:forum, owner: member)
|
|
member.forums.size.should == 2
|
|
end
|
|
|
|
it "has many likes" do
|
|
@post1 = create(:post, author: member)
|
|
@post2 = create(:post, author: member)
|
|
@like1 = create(:like, member:, likeable: @post1)
|
|
@like2 = create(:like, member:, likeable: @post2)
|
|
|
|
expect(member.likes.length).to eq 2
|
|
end
|
|
|
|
it 'has location and lat/long fields' do
|
|
member.update(location: 'Greenwich, UK')
|
|
member.location.should eq 'Greenwich, UK'
|
|
member.latitude.round(2).should eq 51.48
|
|
member.longitude.round(2).should eq 0.00
|
|
end
|
|
|
|
it 'empties the lat/long if location removed' do
|
|
member.update(location: 'Greenwich, UK')
|
|
member.update(location: '')
|
|
member.location.should eq ''
|
|
member.latitude.should be_nil
|
|
member.longitude.should be_nil
|
|
end
|
|
|
|
it 'fails gracefully for unfound locations' do
|
|
member.update(location: 'Tatooine')
|
|
member.location.should eq 'Tatooine'
|
|
member.latitude.should be_nil
|
|
member.longitude.should be_nil
|
|
end
|
|
end
|
|
|
|
context 'no TOS agreement' do
|
|
let(:member) { build(:no_tos_member) }
|
|
|
|
it "refuses to save a member who hasn't agreed to the TOS" do
|
|
member.save.should_not be(true)
|
|
end
|
|
end
|
|
|
|
context 'same :login_name' do
|
|
it "does not allow two members with the same login_name" do
|
|
create(:member, login_name: "bob")
|
|
member = build(:member, login_name: "bob")
|
|
member.should_not be_valid
|
|
member.errors[:login_name].should include("has already been taken")
|
|
end
|
|
|
|
it "tests uniqueness case-insensitively" do
|
|
create(:member, login_name: "bob")
|
|
member = build(:member, login_name: "BoB")
|
|
member.should_not be_valid
|
|
member.errors[:login_name].should include("has already been taken")
|
|
end
|
|
end
|
|
|
|
context 'case sensitivity' do
|
|
it 'preserves case of login name' do
|
|
create(:member, login_name: "BOB")
|
|
Member.find('bob').login_name.should eq 'BOB'
|
|
end
|
|
end
|
|
|
|
context 'invalid login names' do
|
|
it "doesn't allow short names" do
|
|
member = build(:invalid_member_shortname)
|
|
member.should_not be_valid
|
|
member.errors[:login_name].should include("should be between 2 and 25 characters long")
|
|
end
|
|
|
|
it "doesn't allow really long names" do
|
|
member = build(:invalid_member_longname)
|
|
member.should_not be_valid
|
|
member.errors[:login_name].should include("should be between 2 and 25 characters long")
|
|
end
|
|
|
|
it "doesn't allow spaces in names" do
|
|
member = build(:invalid_member_spaces)
|
|
member.should_not be_valid
|
|
member.errors[:login_name].should include("may only include letters, numbers, or underscores")
|
|
end
|
|
|
|
it "doesn't allow other chars in names" do
|
|
member = build(:invalid_member_badchars)
|
|
member.should_not be_valid
|
|
member.errors[:login_name].should include("may only include letters, numbers, or underscores")
|
|
end
|
|
|
|
it "doesn't allow reserved names" do
|
|
member = build(:invalid_member_badname)
|
|
member.should_not be_valid
|
|
member.errors[:login_name].should include("name is reserved")
|
|
end
|
|
end
|
|
|
|
context 'valid login names' do
|
|
it "allows plain alphanumeric chars in names" do
|
|
member = build(:valid_member_alphanumeric)
|
|
member.should be_valid
|
|
end
|
|
|
|
it "allows uppercase chars in names" do
|
|
member = build(:valid_member_uppercase)
|
|
member.should be_valid
|
|
end
|
|
|
|
it "allows underscores in names" do
|
|
member = build(:valid_member_underscore)
|
|
member.should be_valid
|
|
end
|
|
end
|
|
|
|
context 'roles' do
|
|
let(:member) { create(:member) }
|
|
let(:role) { create(:role) }
|
|
|
|
before do
|
|
member.roles << role
|
|
end
|
|
|
|
it 'has a role' do
|
|
member.roles.first.should eq role
|
|
member.role?(:moderator).should be true
|
|
end
|
|
|
|
it 'sets up roles in factories' do
|
|
admin = create(:admin_member)
|
|
admin.role?(:admin).should be true
|
|
end
|
|
|
|
it 'converts role names properly' do
|
|
# need to make sure spaces get turned to underscores
|
|
role = create(:role, name: "a b c")
|
|
member.roles << role
|
|
member.role?(:a_b_c).should be true
|
|
end
|
|
end
|
|
|
|
context 'confirmed scope' do
|
|
before do
|
|
create(:member)
|
|
create(:member)
|
|
end
|
|
|
|
it 'sees confirmed members' do
|
|
Member.confirmed.size.should == 2
|
|
end
|
|
|
|
it 'ignores unconfirmed members' do
|
|
create(:unconfirmed_member)
|
|
Member.confirmed.size.should == 2
|
|
end
|
|
end
|
|
|
|
context 'located scope' do
|
|
# located members must have location, lat, long
|
|
it 'finds members who have locations' do
|
|
london_member = create(:london_member)
|
|
Member.located.should include london_member
|
|
end
|
|
|
|
it 'ignores members with blank locations' do
|
|
nowhere_member = create(:member)
|
|
Member.located.should_not include nowhere_member
|
|
end
|
|
|
|
it 'ignores members with blank lat/long' do
|
|
london_member = create(:london_member)
|
|
london_member.latitude = nil
|
|
london_member.longitude = nil
|
|
london_member.save(validate: false)
|
|
Member.located.should_not include london_member
|
|
end
|
|
end
|
|
|
|
context 'near location' do
|
|
it 'finds nearby members and sorts them' do
|
|
edinburgh_member = create(:edinburgh_member)
|
|
london_member = create(:london_member)
|
|
Member.nearest_to('Greenwich, UK').should eq [london_member, edinburgh_member]
|
|
end
|
|
|
|
it 'returns Member.none when place is blank or un-geocoded' do
|
|
expect(Member.nearest_to('')).to eq Member.none
|
|
expect(Member.nearest_to(nil)).to eq Member.none
|
|
end
|
|
|
|
it 'respects the limit parameter' do
|
|
create(:edinburgh_member)
|
|
create(:london_member)
|
|
expect(Member.nearest_to('Greenwich, UK', 1).to_a.size).to eq 1
|
|
end
|
|
end
|
|
|
|
describe 'interesting scope' do
|
|
# interesting members are defined as:
|
|
# 1) confirmed
|
|
# 2) have a location
|
|
# 3) have at least one planting
|
|
# 4) ordered by the most recent sign in
|
|
|
|
context 'with a few members and plantings' do
|
|
before do
|
|
@members = [
|
|
:london_member, :london_member, :london_member,
|
|
:unconfirmed_member, # !1
|
|
:london_member, # 1, 2, !3
|
|
:member # 1, !2, 3
|
|
].collect { |m| create(m) }
|
|
|
|
[0, 1, 2, 3, 5].each do |i|
|
|
create(:planting, owner: @members[i])
|
|
end
|
|
|
|
@members[0].updated_at = 3.days.ago
|
|
@members[1].updated_at = 2.days.ago
|
|
@members[2].updated_at = 1.day.ago
|
|
|
|
# TODO: Shouldn't this save?
|
|
|
|
@result = Member.interesting
|
|
|
|
# Some members have multiple plantings, but should only appear once
|
|
create_list(:planting, 3, owner: @members[2])
|
|
end
|
|
|
|
it 'finds interesting members without duplicates in the correct order' do
|
|
@result.should eq [@members[2], @members[1], @members[0]]
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'harvests' do
|
|
it 'has harvests' do
|
|
member = create(:member)
|
|
harvest = create(:harvest, owner: member)
|
|
member.harvests.should eq [harvest]
|
|
end
|
|
end
|
|
|
|
context 'member who followed another member' do
|
|
let(:follower) { create(:member) }
|
|
let(:followed_member) { create(:member) }
|
|
let(:other_member) { create(:member) }
|
|
|
|
before do
|
|
@follow = follower.follows.create(follower_id: follower.id, followed_id: followed_member.id)
|
|
end
|
|
|
|
context 'already_following' do
|
|
it 'detects that member is already following a member' do
|
|
expect(follower.already_following?(followed_member)).to be true
|
|
end
|
|
|
|
it 'detects that member is not already following a member' do
|
|
expect(follower.already_following?(other_member)).to be false
|
|
end
|
|
end
|
|
|
|
context 'get_follow' do
|
|
it 'gets the correct follow for a followed member' do
|
|
expect(follower.get_follow(followed_member).id).to eq @follow.id
|
|
end
|
|
|
|
it 'returns nil for a member that is not followed' do
|
|
expect(follower.get_follow(other_member)).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'member deleted' do
|
|
let(:member) { create(:member) }
|
|
|
|
context 'queries a scope' do
|
|
before { member.destroy }
|
|
|
|
it { expect(Member.all).not_to include(member) }
|
|
it { expect(Member.confirmed).not_to include(member) }
|
|
it { expect(Member.located).not_to include(member) }
|
|
it { expect(Member.recently_signed_in).not_to include(member) }
|
|
it { expect(Member.recently_joined).not_to include(member) }
|
|
it { expect(Member.interesting).not_to include(member) }
|
|
it { expect(Member.has_plantings).not_to include(member) }
|
|
end
|
|
|
|
context "deleted admin member" do
|
|
let(:member) { create(:admin_member) }
|
|
|
|
before { member.discard }
|
|
|
|
context 'crop creator' do
|
|
let!(:crop) { create(:crop, creator: member) }
|
|
|
|
it "leaves crops behind, reassigned to cropbot" do
|
|
expect(Crop.all).to include(crop)
|
|
end
|
|
end
|
|
|
|
context 'forum owners' do
|
|
let!(:forum) { create(:forum, owner: member) }
|
|
|
|
it "leaves forums behind, reassigned to ex_admin" do
|
|
expect(forum.owner).to eq(member)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#nursery' do
|
|
let(:member) { create(:member) }
|
|
|
|
it 'makes a nursery garden' do
|
|
expect(member.nursery!).to have_attributes(nursery: true, owner: member, name: 'Nursery')
|
|
end
|
|
|
|
it 'returns the same nursery each time' do
|
|
first = member.nursery!
|
|
expect(member.nursery!).to eq first
|
|
end
|
|
|
|
it 'makes only one nursery' do
|
|
2.times { member.nursery! }
|
|
expect(member.gardens.nurseries.count).to eq 1
|
|
end
|
|
|
|
it 'does not count the nursery as a second garden' do
|
|
member.nursery!
|
|
expect(member.has_activity?).to be false
|
|
end
|
|
|
|
context 'when a garden is already called Nursery' do
|
|
before { create(:garden, owner: member, name: 'Nursery') }
|
|
|
|
it 'picks another name' do
|
|
expect(member.nursery!.name).to eq 'Nursery 2'
|
|
end
|
|
end
|
|
end
|
|
end
|