691 lines
24 KiB
Ruby
691 lines
24 KiB
Ruby
require "spec_helper"
|
|
|
|
describe Work do
|
|
# see lib/collectible_spec for collection-related tests
|
|
|
|
it "creates a minimal work" do
|
|
expect(create(:work)).to be_valid
|
|
end
|
|
|
|
context "when posted" do
|
|
it "posts the first chapter" do
|
|
work = create(:work)
|
|
expect(work.first_chapter.posted).to eq true
|
|
end
|
|
end
|
|
|
|
context "create_stat_counter" do
|
|
it "creates a stat counter for that work id" do
|
|
expect do
|
|
@work = build(:work)
|
|
@work.save!
|
|
end.to change { StatCounter.all.count }
|
|
.by(1)
|
|
expect(StatCounter.where(work_id: @work.id)).to exist
|
|
end
|
|
end
|
|
|
|
context "invalid title" do
|
|
it { expect(build(:work, title: nil)).to be_invalid }
|
|
|
|
it "cannot be shorter than ArchiveConfig.TITLE_MIN" do
|
|
expect(build(:work, title: Faker::Lorem.characters(number: ArchiveConfig.TITLE_MIN - 1))).to be_invalid
|
|
end
|
|
|
|
it "cannot be longer than ArchiveConfig.TITLE_MAX" do
|
|
expect(build(:work, title: Faker::Lorem.characters(number: ArchiveConfig.TITLE_MAX + 1))).to be_invalid
|
|
end
|
|
end
|
|
|
|
context "clean_and_validate_title" do
|
|
before do
|
|
ArchiveConfig.TITLE_MIN = 10
|
|
end
|
|
it "strips out leading spaces from the title" do
|
|
@work = create(:work, title: " Has Leading Spaces")
|
|
@work.reload
|
|
expect(@work.title).to eq("Has Leading Spaces")
|
|
end
|
|
|
|
let(:too_short) { ArchiveConfig.TITLE_MIN - 1 }
|
|
it "errors if the title without leading spaces is shorter than #{ArchiveConfig.TITLE_MIN}" do
|
|
expect do
|
|
@work = create(:work, title: " #{too_short}")
|
|
@work.reload
|
|
end.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Title must be at least #{ArchiveConfig.TITLE_MIN} characters long without leading spaces.")
|
|
end
|
|
|
|
# Reset the min characters in the title, so that the factory is valid
|
|
after do
|
|
ArchiveConfig.TITLE_MIN = 1
|
|
end
|
|
end
|
|
|
|
context "invalid summary" do
|
|
it "cannot be longer than ArchiveConfig.SUMMARY_MAX" do
|
|
expect(build(:work, title: Faker::Lorem.characters(number: ArchiveConfig.SUMMARY_MAX + 1))).to be_invalid
|
|
end
|
|
end
|
|
|
|
context "invalid notes" do
|
|
it "cannot be longer than ArchiveConfig.NOTES_MAX" do
|
|
expect(build(:work, title: Faker::Lorem.characters(number: ArchiveConfig.NOTES_MAX + 1))).to be_invalid
|
|
end
|
|
end
|
|
|
|
context "invalid endnotes" do
|
|
it "cannot be longer than ArchiveConfig.NOTES_MAX" do
|
|
expect(build(:work, title: Faker::Lorem.characters(number: ArchiveConfig.NOTES_MAX + 1))).to be_invalid
|
|
end
|
|
end
|
|
|
|
context "invalid language" do
|
|
let(:deleted_language_id) do
|
|
briefly_lived_language = create(:language)
|
|
deleted_language_id = briefly_lived_language.id
|
|
briefly_lived_language.destroy
|
|
deleted_language_id
|
|
end
|
|
|
|
it "is valid with a supported language" do
|
|
work = build(:work, language_id: Language.default.id)
|
|
expect(work).to be_valid
|
|
end
|
|
|
|
it "is not valid with a language we don't support" do
|
|
work = build(:work, language_id: deleted_language_id)
|
|
|
|
expect(work).not_to be_valid
|
|
expect(work.errors.messages[:base]).to include("Language cannot be blank.")
|
|
end
|
|
|
|
it "is not valid without a language" do
|
|
work = build(:work, language_id: "")
|
|
|
|
expect(work).not_to be_valid
|
|
expect(work.errors.messages[:base]).to include("Language cannot be blank.")
|
|
end
|
|
end
|
|
|
|
context "invalid rating" do
|
|
it "cannot have more than one rating" do
|
|
expect(build(:work, rating_string: "Not Rated, General Audiences")).to be_invalid
|
|
end
|
|
end
|
|
|
|
context "validate authors" do
|
|
let(:invalid_work) { build(:no_authors) }
|
|
|
|
it "does not save if author is blank" do
|
|
expect(invalid_work.save).to be_falsey
|
|
expect(invalid_work.errors[:base]).to include "Work must have at least one creator."
|
|
end
|
|
end
|
|
|
|
describe "reindexing" do
|
|
let!(:work) { create(:work) }
|
|
|
|
context "when draft status is changed" do
|
|
it "enqueues tags for reindex" do
|
|
expect do
|
|
work.update!(posted: false)
|
|
end.to add_to_reindex_queue(work.fandoms.last, :main)
|
|
end
|
|
end
|
|
|
|
context "when hidden by an admin" do
|
|
it "enqueues tags for reindex" do
|
|
expect do
|
|
work.update!(hidden_by_admin: true)
|
|
end.to add_to_reindex_queue(work.fandoms.last, :main)
|
|
end
|
|
end
|
|
|
|
context "when put in an unrevealed collection" do
|
|
it "enqueues tags for reindex" do
|
|
expect do
|
|
work.update!(in_unrevealed_collection: true)
|
|
end.to add_to_reindex_queue(work.fandoms.last, :main)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#crossover" do
|
|
it "is not crossover with one fandom" do
|
|
fandom = create(:canonical_fandom, name: "nge")
|
|
work = create(:work, fandoms: [fandom])
|
|
expect(work.crossover).to be_falsy
|
|
end
|
|
|
|
it "is not crossover with one fandom and one of its synonyms" do
|
|
rel = create(:canonical_fandom, name: "evanescence")
|
|
syn = create(:fandom, name: "can't wake up (wake me up inside)", merger: rel)
|
|
work = create(:work, fandoms: [rel, syn])
|
|
expect(work.crossover).to be_falsy
|
|
end
|
|
|
|
it "is not crossover with multiple synonyms of one fandom" do
|
|
rel = create(:canonical_fandom, name: "nge")
|
|
syn1 = create(:fandom, name: "eva", merger: rel)
|
|
syn2 = create(:fandom, name: "end of eva", merger: rel)
|
|
work = create(:work, fandoms: [syn1, syn2])
|
|
expect(work.crossover).to be_falsy
|
|
end
|
|
|
|
it "is not crossover with fandoms sharing a direct meta tag" do
|
|
rel1 = create(:canonical_fandom, name: "rebuild")
|
|
rel2 = create(:canonical_fandom, name: "campus apocalypse")
|
|
meta_tag = create(:canonical_fandom, name: "nge")
|
|
meta_tag.update_attribute(:sub_tag_string, "#{rel1.name},#{rel2.name}")
|
|
rel1.reload
|
|
rel2.reload
|
|
|
|
work = create(:work, fandoms: [rel1, rel2])
|
|
expect(work.crossover).to be_falsy
|
|
end
|
|
|
|
it "is not a crossover between fandoms sharing an indirect meta tag" do
|
|
grand = create(:canonical_fandom)
|
|
parent1 = create(:canonical_fandom)
|
|
parent2 = create(:canonical_fandom)
|
|
child1 = create(:canonical_fandom)
|
|
child2 = create(:canonical_fandom)
|
|
|
|
grand.update_attribute(:sub_tag_string, "#{parent1.name},#{parent2.name}")
|
|
child1.update_attribute(:meta_tag_string, parent1.name)
|
|
child2.update_attribute(:meta_tag_string, parent2.name)
|
|
|
|
work = create(:work, fandom_string: "#{child1.name},#{child2.name}")
|
|
expect(work.crossover).to be_falsey
|
|
end
|
|
|
|
it "is crossover with fandoms in different meta tag trees" do
|
|
rel1 = create(:canonical_fandom, name: "rebuild again eventually")
|
|
rel2 = create(:canonical_fandom, name: "evanescence")
|
|
meta_tag = create(:canonical_fandom, name: "rebuild")
|
|
meta_tag.update_attribute(:sub_tag_string, rel1.name)
|
|
super_meta_tag = create(:canonical_fandom, name: "nge")
|
|
super_meta_tag.update_attribute(:sub_tag_string, meta_tag.name)
|
|
|
|
rel1.reload
|
|
rel2.reload
|
|
meta_tag.reload
|
|
super_meta_tag.reload
|
|
|
|
work = create(:work, fandoms: [rel1, rel2])
|
|
expect(work.crossover).to be_truthy
|
|
|
|
work = create(:work, fandoms: [meta_tag, super_meta_tag])
|
|
expect(work.crossover).to be_falsy
|
|
end
|
|
|
|
it "is crossover with unrelated fandoms" do
|
|
ships = [create(:canonical_fandom, name: "nge"), create(:canonical_fandom, name: "evanescence")]
|
|
work = create(:work, fandoms: ships)
|
|
expect(work.crossover).to be_truthy
|
|
end
|
|
|
|
it "is a crossover when missing meta-taggings" do
|
|
f1 = create(:canonical_fandom)
|
|
f2 = create(:canonical_fandom)
|
|
f3 = create(:canonical_fandom)
|
|
unrelated = create(:canonical_fandom)
|
|
|
|
f2.update_attribute(:meta_tag_string, f3.name)
|
|
f2.update_attribute(:sub_tag_string, f1.name)
|
|
f1.meta_tags.delete(f3)
|
|
|
|
work = create(:work, fandom_string: "#{f1.name}, #{unrelated.name}")
|
|
expect(work.crossover).to be_truthy
|
|
end
|
|
|
|
context "when one tagged fandom has two unrelated meta tags" do
|
|
let(:meta1) { create(:canonical_fandom) }
|
|
let(:meta2) { create(:canonical_fandom) }
|
|
let(:fandom) { create(:canonical_fandom) }
|
|
|
|
before do
|
|
fandom.update_attribute(:meta_tag_string, "#{meta1.name},#{meta2.name}")
|
|
end
|
|
|
|
it "is not a crossover with the fandom's synonym" do
|
|
syn = create(:fandom, merger: fandom)
|
|
work = create(:work, fandom_string: "#{fandom.name},#{syn.name}")
|
|
expect(work.crossover).to be_falsey
|
|
end
|
|
|
|
it "is not a crossover with the fandom's meta tag" do
|
|
work = create(:work, fandom_string: "#{fandom.name},#{meta1.name}")
|
|
expect(work.crossover).to be_falsey
|
|
end
|
|
|
|
it "is not a crossover with another subtag of the fandom's meta tag" do
|
|
sub = create(:canonical_fandom)
|
|
sub.update_attribute(:meta_tag_string, meta1.name)
|
|
work = create(:work, fandom_string: "#{fandom.name},#{sub.name}")
|
|
expect(work.crossover).to be_falsey
|
|
end
|
|
|
|
it "is not a crossover with another fandom sharing the same two meta tags" do
|
|
other = create(:canonical_fandom)
|
|
other.update_attribute(:meta_tag_string, "#{meta1.name},#{meta2.name}")
|
|
work = create(:work, fandom_string: "#{fandom.name},#{other.name}")
|
|
expect(work.crossover).to be_falsey
|
|
end
|
|
|
|
it "is not a crossover with another fandom with two unrelated meta tags, only one of which is shared by both fandoms" do
|
|
# The tag fandom and the tag other share one meta tag (meta2), but
|
|
# fandom has a meta tag meta1 completely unrelated to other, and other
|
|
# has a meta tag meta3 completely unrelated to fandom. However, the
|
|
# shared meta tag means that they are related, and thus a work tagged
|
|
# with both is not a crossover.
|
|
meta3 = create(:canonical_fandom)
|
|
other = create(:canonical_fandom)
|
|
other.update_attribute(:meta_tag_string, "#{meta2.name},#{meta3.name}")
|
|
work = create(:work, fandom_string: "#{fandom.name},#{other.name}")
|
|
expect(work.crossover).to be_falsey
|
|
end
|
|
|
|
it "is a crossover with another fandom with two unrelated meta tags, when none of the meta tags are shared" do
|
|
meta3 = create(:canonical_fandom)
|
|
meta4 = create(:canonical_fandom)
|
|
other = create(:canonical_fandom)
|
|
other.update_attribute(:meta_tag_string, "#{meta3.name},#{meta4.name}")
|
|
work = create(:work, fandom_string: "#{fandom.name},#{other.name}")
|
|
expect(work.crossover).to be_truthy
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#otp" do
|
|
it "is not otp with no relationship" do
|
|
work = create(:work)
|
|
expect(work.relationships).to be_empty
|
|
expect(work.otp).to be_falsy
|
|
end
|
|
|
|
it "is otp with only one relationship" do
|
|
rel = create(:relationship, name: "asushin")
|
|
work = create(:work, relationships: [rel])
|
|
expect(work.otp).to be_truthy
|
|
end
|
|
|
|
it "is otp with one canonical relationship and one of its synonyms" do
|
|
rel = create(:canonical_relationship, name: "kawoshin")
|
|
syn = create(:relationship, name: "shinkawo", merger: rel)
|
|
work = create(:work, relationships: [rel, syn])
|
|
expect(work.otp).to be_truthy
|
|
end
|
|
|
|
it "is otp with multiple synonyms of the same canonical relationship" do
|
|
rel = create(:canonical_relationship, name: "kawoshin")
|
|
syn1 = create(:relationship, name: "shinkawo", merger: rel)
|
|
syn2 = create(:relationship, name: "kaworu/shinji", merger: rel)
|
|
work = create(:work, relationships: [syn1, syn2])
|
|
expect(work.otp).to be_truthy
|
|
end
|
|
|
|
it "is not otp with unrelated relationships, one of which is canonical" do
|
|
ships = [create(:relationship, name: "shinrei"), create(:canonical_relationship, name: "asurei")]
|
|
work = create(:work, relationships: ships)
|
|
expect(work.otp).to be_falsy
|
|
end
|
|
|
|
it "is not otp with unrelated relationships" do
|
|
ships = [create(:relationship, name: "asushin"), create(:relationship, name: "asurei")]
|
|
work = create(:work, relationships: ships)
|
|
expect(work.otp).to be_falsy
|
|
end
|
|
|
|
it "is not otp with relationships sharing a meta tag" do
|
|
rel1 = create(:canonical_relationship, name: "shinrei")
|
|
rel2 = create(:canonical_relationship, name: "asurei")
|
|
meta_tag = create(:canonical_relationship)
|
|
meta_tag.update_attribute(:sub_tag_string, "#{rel1.name},#{rel2.name}")
|
|
rel1.reload
|
|
rel2.reload
|
|
|
|
work = create(:work, relationships: [rel1, rel2])
|
|
expect(work.otp).to be_falsy
|
|
end
|
|
end
|
|
|
|
describe "#authors_to_sort_on" do
|
|
let(:work) { build(:work) }
|
|
|
|
context "when the pseuds start with special characters" do
|
|
it "removes those characters" do
|
|
allow(work).to receive(:pseuds).and_return [Pseud.new(name: "-jolyne")]
|
|
expect(work.authors_to_sort_on).to eq "jolyne"
|
|
|
|
allow(work).to receive(:pseuds).and_return [Pseud.new(name: "_hermes")]
|
|
expect(work.authors_to_sort_on).to eq "hermes"
|
|
end
|
|
end
|
|
|
|
context "when the pseuds start with numbers" do
|
|
it "does not remove numbers" do
|
|
allow(work).to receive(:pseuds).and_return [Pseud.new(name: "007james")]
|
|
expect(work.authors_to_sort_on).to eq "007james"
|
|
end
|
|
end
|
|
|
|
context "when the work is anonymous" do
|
|
it "returns Anonymous" do
|
|
work.in_anon_collection = true
|
|
allow(work).to receive(:pseuds).and_return [Pseud.new(name: "stealthy")]
|
|
expect(work.authors_to_sort_on).to eq "Anonymous"
|
|
end
|
|
end
|
|
|
|
context "when the work has multiple pseuds" do
|
|
it "sorts them like the byline then joins them with commas" do
|
|
allow(work).to receive(:pseuds).and_return [Pseud.new(name: "diavolo"), Pseud.new(name: "doppio")]
|
|
expect(work.authors_to_sort_on).to eq "diavolo, doppio"
|
|
|
|
allow(work).to receive(:pseuds).and_return [Pseud.new(name: "Tiziano"), Pseud.new(name: "squalo")]
|
|
expect(work.authors_to_sort_on).to eq "squalo, tiziano"
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "work_skin_allowed" do
|
|
context "public skin"
|
|
|
|
context "private skin" do
|
|
before :each do
|
|
@skin_author = create(:user)
|
|
@second_author = create(:user)
|
|
@private_skin = create(:work_skin, :private, author_id: @skin_author.id)
|
|
end
|
|
|
|
let(:work_author) { @skin_author }
|
|
let(:work) { build(:custom_work_skin, authors: [work_author.pseuds.first], work_skin_id: @private_skin.id) }
|
|
it "can be used by the work skin author" do
|
|
expect(work.save).to be_truthy
|
|
end
|
|
|
|
let(:work) { build(:custom_work_skin, authors: [@second_author.pseuds.first], work_skin_id: @private_skin.id) }
|
|
it "cannot be used by another user" do
|
|
work.work_skin_allowed
|
|
expect(work.errors[:base]).to include("You do not have permission to use that custom work stylesheet.")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "new gifts virtual attribute" do
|
|
let(:recipient1) { create(:user).pseuds.first.name }
|
|
let(:recipient2) { create(:user).pseuds.first.name }
|
|
let(:recipient3) { create(:user).pseuds.first.name }
|
|
|
|
let(:work) { build(:work) }
|
|
|
|
before do
|
|
work.recipients = "#{recipient1},#{recipient2}"
|
|
end
|
|
|
|
it "contains gifts for the same recipients when they are first added" do
|
|
expect(work.new_gifts.collect(&:recipient)).to eq([recipient1, recipient2])
|
|
end
|
|
|
|
it "only contains a gift for the new recipient if replacing the previous recipients" do
|
|
work.recipients = recipient3
|
|
expect(work.new_gifts.collect(&:recipient)).to eq([recipient3])
|
|
end
|
|
|
|
it "simple assignment works" do
|
|
work.recipients = recipient2
|
|
expect(work.new_gifts.collect(&:recipient)).to eq([recipient2])
|
|
end
|
|
|
|
it "only contains one gift if the same recipient is entered twice" do
|
|
work.recipients = "#{recipient2},#{recipient2}"
|
|
expect(work.new_gifts.collect(&:recipient)).to eq([recipient2])
|
|
end
|
|
end
|
|
|
|
describe "#find_by_url" do
|
|
it "should find imported works with various URL formats" do
|
|
%w[http://foo.com/bar.html
|
|
http://foo.com/bar
|
|
http://lj-site.com/bar/foo?color=blue
|
|
https://www.lj-site.com/bar/foo?color=blue
|
|
http://www.foo.com/bar https://www.foo.com/bar].each do |url|
|
|
work = create(:work, imported_from_url: url)
|
|
expect(Work.find_by_url(url)).to eq(work)
|
|
work.destroy
|
|
end
|
|
end
|
|
|
|
it "should not mix up imported works with similar URLs or significant query parameters" do
|
|
{
|
|
"http://foo.com/12345" => "http://foo.com/123",
|
|
"http://efiction-site.com/viewstory.php?sid=123" => "http://efiction-site.com/viewstory.php?sid=456",
|
|
"http://www.foo.com/i-am-something" => "http://foo.com/i-am-something/else"
|
|
}.each do |import_url, find_url|
|
|
work = create(:work, imported_from_url: import_url)
|
|
expect(Work.find_by_url(find_url)).to_not eq(work)
|
|
work.destroy
|
|
end
|
|
end
|
|
|
|
it "should find works imported with irrelevant query parameters" do
|
|
work = create(:work, imported_from_url: "http://lj-site.com/thing1?style=mine")
|
|
expect(Work.find_by_url("http://lj-site.com/thing1?style=other")).to eq(work)
|
|
work.destroy
|
|
end
|
|
|
|
it "finds works imported with HTTP protocol and irrelevant query parameters" do
|
|
work = create(:work, imported_from_url: "http://lj-site.com/thing1?style=mine")
|
|
expect(Work.find_by_url("https://lj-site.com/thing1?style=other")).to eq(work)
|
|
work.destroy
|
|
end
|
|
|
|
it "finds works imported with HTTPS protocol and irrelevant query parameters" do
|
|
work = create(:work, imported_from_url: "https://lj-site.com/thing1?style=mine")
|
|
expect(Work.find_by_url("http://lj-site.com/thing1?style=other")).to eq(work)
|
|
work.destroy
|
|
end
|
|
|
|
it "gets the work from cache when searching for an imported work by URL" do
|
|
url = "http://lj-site.com/thing2"
|
|
work = create(:work, imported_from_url: url)
|
|
expect(Rails.cache.read(Work.find_by_url_cache_key(url))).to be_nil
|
|
expect(Work.find_by_url(url)).to eq(work)
|
|
expect(Rails.cache.read(Work.find_by_url_cache_key(url))).to eq(work)
|
|
work.destroy
|
|
end
|
|
end
|
|
|
|
describe "#update_complete_status" do
|
|
it "marks a work complete when it's been completed" do
|
|
work = create(:work, expected_number_of_chapters: 1)
|
|
expect(work.complete).to be_truthy
|
|
end
|
|
|
|
it "marks a work incomplete when it's no longer completed" do
|
|
work = create(:work, expected_number_of_chapters: 1)
|
|
work.update!(expected_number_of_chapters: nil)
|
|
expect(work.reload.complete).to be_falsey
|
|
end
|
|
end
|
|
|
|
describe "#wip_length" do
|
|
it "updating chapter count via wip_length sets a sensible expected_number_of_chapters value" do
|
|
work = create(:work)
|
|
create(:chapter, work: work)
|
|
work.reload
|
|
|
|
work.wip_length = 1
|
|
expect(work.expected_number_of_chapters).to be_nil
|
|
expect(work.wip_length).to eq("?")
|
|
|
|
work.wip_length = 2
|
|
expect(work.expected_number_of_chapters).to eq(2)
|
|
expect(work.wip_length).to eq(work.expected_number_of_chapters)
|
|
|
|
work.wip_length = 3
|
|
expect(work.expected_number_of_chapters).to eq(3)
|
|
expect(work.wip_length).to eq(work.expected_number_of_chapters)
|
|
end
|
|
end
|
|
|
|
describe "#hide_spam" do
|
|
before do
|
|
@admin_setting = AdminSetting.first || AdminSetting.create
|
|
@work = create(:work)
|
|
end
|
|
context "when the admin setting is enabled" do
|
|
before do
|
|
@admin_setting.update_attribute(:hide_spam, true)
|
|
end
|
|
it "automatically hides spam works and sends an email" do
|
|
expect { @work.update!(spam: true) }
|
|
.to change { ActionMailer::Base.deliveries.count }
|
|
.by(1)
|
|
expect(@work.reload.hidden_by_admin).to be_truthy
|
|
expect(ActionMailer::Base.deliveries.last.subject).to eq("[AO3] Your work was hidden as spam")
|
|
end
|
|
end
|
|
context "when the admin setting is disabled" do
|
|
before do
|
|
@admin_setting.update_attribute(:hide_spam, false)
|
|
end
|
|
it "does not automatically hide spam works and does not send an email" do
|
|
expect { @work.update!(spam: true) }
|
|
.not_to change { ActionMailer::Base.deliveries.count }
|
|
expect(@work.reload.hidden_by_admin).to be_falsey
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "co-creator permissions" do
|
|
let(:creator) { create(:user) }
|
|
let(:co_creator) { create(:user) }
|
|
let(:no_co_creator) { create(:user) }
|
|
|
|
before do
|
|
# In order to enable co-creator checks (instead of just having everything
|
|
# be automatically approved), we need to make sure that User.current_user
|
|
# is not nil.
|
|
User.current_user = creator
|
|
co_creator.preference.update!(allow_cocreator: true)
|
|
no_co_creator.preference.update!(allow_cocreator: false)
|
|
end
|
|
|
|
it "allows normal users to invite others as chapter co-creators" do
|
|
work = create(:work, authors: creator.pseuds)
|
|
work.author_attributes = { byline: co_creator.login }
|
|
expect(work).to be_valid
|
|
expect(work.save).to be_truthy
|
|
expect(work.user_has_creator_invite?(co_creator)).to be_truthy
|
|
end
|
|
|
|
it "doesn't allow users to invite others who disallow co-creators" do
|
|
work = create(:work, authors: creator.pseuds)
|
|
work.author_attributes = { byline: no_co_creator.login }
|
|
expect(work).to be_invalid
|
|
expect(work.save).to be_falsey
|
|
expect(work.user_has_creator_invite?(no_co_creator)).to be_falsey
|
|
end
|
|
end
|
|
|
|
describe "#remove_author" do
|
|
let(:to_remove) { create(:user) }
|
|
let(:other) { create(:user) }
|
|
|
|
context "when all the pseuds on the work are owned by one user" do
|
|
let(:pseud1) { create(:pseud, user: to_remove) }
|
|
let(:pseud2) { create(:pseud, user: to_remove) }
|
|
let(:pseud3) { create(:pseud, user: to_remove) }
|
|
|
|
let!(:work) do
|
|
create(:work, authors: [pseud1, pseud2, pseud3])
|
|
end
|
|
|
|
let!(:solo1) { create(:chapter, work: work, authors: [pseud1]) }
|
|
let!(:solo2) { create(:chapter, work: work, authors: [pseud2]) }
|
|
let!(:solo3) { create(:chapter, work: work, authors: [pseud3]) }
|
|
|
|
before { work.reload }
|
|
|
|
it "raises an error" do
|
|
expect { work.remove_author(to_remove) }
|
|
.to raise_exception(
|
|
"Sorry, we can't remove all creators of a work."
|
|
)
|
|
end
|
|
end
|
|
|
|
context "when the work has a chapter whose sole creator is being removed" do
|
|
let!(:work) do
|
|
create(:work, authors: [to_remove.default_pseud, other.default_pseud])
|
|
end
|
|
|
|
let!(:solo_chapter) do
|
|
create(:chapter, work: work, authors: [to_remove.default_pseud])
|
|
end
|
|
|
|
# Make sure we see the newest chapter:
|
|
before { work.reload }
|
|
|
|
it "sets the chapter's creators equal to the work's" do
|
|
work.remove_author(to_remove)
|
|
expect(work.pseuds.reload).to contain_exactly(other.default_pseud)
|
|
expect(solo_chapter.pseuds.reload).to contain_exactly(other.default_pseud)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#destroy" do
|
|
let(:work) { create(:work) }
|
|
|
|
it "does not save an original creator record" do
|
|
expect { work.destroy }
|
|
.not_to change { WorkOriginalCreator.count }
|
|
end
|
|
|
|
context "when an original creator exists" do
|
|
let!(:original_creator) { create(:work_original_creator, work: work) }
|
|
|
|
it "deletes the original creator" do
|
|
work.destroy
|
|
expect { original_creator.reload }
|
|
.to raise_error(ActiveRecord::RecordNotFound)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#allow_collection_invitation?" do
|
|
let(:creator1) { create(:user) }
|
|
let(:creator2) { create(:user) }
|
|
let(:work) { create(:work, authors: [creator1.default_pseud, creator2.default_pseud]) }
|
|
|
|
context "when all creators allow collection invitations" do
|
|
before do
|
|
creator1.preference.update!(allow_collection_invitation: true)
|
|
creator2.preference.update!(allow_collection_invitation: true)
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(work.allow_collection_invitation?).to be true
|
|
end
|
|
end
|
|
|
|
context "when all creators disallow collection invitations" do
|
|
it "returns false" do
|
|
expect(work.allow_collection_invitation?).to be false
|
|
end
|
|
end
|
|
|
|
context "when creators have a mix of collection invitation preferences" do
|
|
before do
|
|
creator1.preference.update!(allow_collection_invitation: true)
|
|
end
|
|
|
|
it "returns true" do
|
|
expect(work.allow_collection_invitation?).to be true
|
|
end
|
|
end
|
|
end
|
|
end
|