940 lines
34 KiB
Ruby
940 lines
34 KiB
Ruby
# frozen_string_literal: true
|
|
require "spec_helper"
|
|
|
|
describe WorksController, work_search: true do
|
|
include LoginMacros
|
|
include RedirectExpectationHelper
|
|
|
|
let(:banned_user) { create(:user, banned: true) }
|
|
let(:banned_users_work) do
|
|
banned_user.update!(banned: false)
|
|
work = create(:work, authors: [banned_user.pseuds.first])
|
|
banned_user.update!(banned: true)
|
|
work
|
|
end
|
|
|
|
let(:suspended_user) { create(:user, suspended: true, suspended_until: 1.week.from_now) }
|
|
let(:suspended_users_work) do
|
|
suspended_user.update!(suspended: false, suspended_until: nil)
|
|
work = create(:work, authors: [suspended_user.pseuds.first])
|
|
suspended_user.update!(suspended: true, suspended_until: 1.week.from_now)
|
|
work
|
|
end
|
|
|
|
describe "before_action #clean_work_search_params" do
|
|
let(:params) { {} }
|
|
|
|
def call_with_params(params)
|
|
controller.params = { work_search: params }
|
|
controller.params[:work_search] = controller.clean_work_search_params
|
|
end
|
|
|
|
context "when no work search parameters are given" do
|
|
it "redirects to the login screen when no user is logged in" do
|
|
get :clean_work_search_params, params: params
|
|
it_redirects_to_with_error(new_user_session_path,
|
|
"Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
|
|
end
|
|
end
|
|
|
|
context "when the query contains countable search parameters" do
|
|
it "escapes less and greater than in query" do
|
|
[
|
|
{ params: "< 5 words", expected: "< 5 words", message: "Should escape <" },
|
|
{ params: "> 5 words", expected: "> 5 words", message: "Should escape >" },
|
|
].each do |settings|
|
|
call_with_params(query: settings[:params])
|
|
expect(controller.params[:work_search][:query])
|
|
.to eq(settings[:expected]), settings[:message]
|
|
end
|
|
end
|
|
|
|
it "converts 'word' to 'word_count'" do
|
|
call_with_params(query: "word:6")
|
|
expect(controller.params[:work_search][:word_count]).to eq("6")
|
|
end
|
|
|
|
it "converts 'words' to 'word_count'" do
|
|
call_with_params(query: "words:7")
|
|
expect(controller.params[:work_search][:word_count]).to eq("7")
|
|
end
|
|
|
|
it "converts 'hits' queries to 'hits'" do
|
|
call_with_params(query: "hits:8")
|
|
expect(controller.params[:work_search][:hits]).to eq("8")
|
|
end
|
|
|
|
it "converts other queries to (pluralized term)_count" do
|
|
%w(kudo comment bookmark).each do |term|
|
|
call_with_params(query: "#{term}:9")
|
|
expect(controller.params[:work_search]["#{term.pluralize}_count"])
|
|
.to eq("9"), "Search term '#{term}' should become #{term.pluralize}_count key"
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when sort parameters are provided" do
|
|
it "converts variations on 'sorted by: X' into :sort_column key" do
|
|
[
|
|
"sort by: words",
|
|
"sorted by: words",
|
|
"sorted: words",
|
|
"sort: words",
|
|
"sort by < words",
|
|
"sort by > words",
|
|
"sort by = words"
|
|
].each do |query|
|
|
call_with_params(query: query)
|
|
expect(controller.params[:work_search][:sort_column])
|
|
.to eq("word_count"), "Sort command '#{query}' should be converted to :sort_column"
|
|
end
|
|
end
|
|
|
|
it "converts variations on sort columns to column name" do
|
|
[
|
|
{ query: "sort by: word count", expected: "word_count" },
|
|
{ query: "sort by: words", expected: "word_count" },
|
|
{ query: "sort by: word", expected: "word_count" },
|
|
{ query: "sort by: creator", expected: "authors_to_sort_on" },
|
|
{ query: "sort by: title", expected: "title_to_sort_on" },
|
|
{ query: "sort by: date", expected: "created_at" },
|
|
{ query: "sort by: date posted", expected: "created_at" },
|
|
{ query: "sort by: hits", expected: "hits" },
|
|
{ query: "sort by: kudos", expected: "kudos_count" },
|
|
{ query: "sort by: comments", expected: "comments_count" },
|
|
{ query: "sort by: bookmarks", expected: "bookmarks_count" },
|
|
].each do |settings|
|
|
call_with_params(query: settings[:query])
|
|
actual = controller.params[:work_search][:sort_column]
|
|
expect(actual)
|
|
.to eq(settings[:expected]),
|
|
"Query '#{settings[:query]}' should be converted to :sort_column '#{settings[:expected]}' but is '#{actual}'"
|
|
end
|
|
end
|
|
|
|
it "converts 'ascending' or '>' into :sort_direction key 'asc'" do
|
|
[
|
|
"sort > word_count",
|
|
"sort: word_count ascending",
|
|
"sort: hits ascending",
|
|
].each do |query|
|
|
call_with_params(query: query)
|
|
expect(controller.params[:work_search][:sort_direction]).to eq("asc")
|
|
end
|
|
end
|
|
|
|
it "converts 'descending' or '<' into :sort_direction key 'desc'" do
|
|
[
|
|
"sort < word_count",
|
|
"sort: word_count descending",
|
|
"sort: hits descending",
|
|
].each do |query|
|
|
call_with_params(query: query)
|
|
expect(controller.params[:work_search][:sort_direction]).to eq("desc")
|
|
end
|
|
end
|
|
|
|
# The rest of these are probably bugs
|
|
it "returns no sort column if there is NO punctuation after 'sort by' clause" do
|
|
call_with_params(query: "sort by word count")
|
|
expect(controller.params[:work_search][:sort_column]).to be_nil
|
|
end
|
|
|
|
it "can't search by date updated" do
|
|
[
|
|
{ query: "sort by: date updated", expected: "revised_at" },
|
|
].each do |settings|
|
|
call_with_params(query: settings[:query])
|
|
expect(controller.params[:work_search][:sort_column]).to eq("created_at") # should be revised_at
|
|
end
|
|
end
|
|
|
|
it "can't sort ascending if more than one word follows the colon" do
|
|
[
|
|
"sort by: word count ascending",
|
|
].each do |query|
|
|
call_with_params(query: query)
|
|
expect(controller.params[:work_search][:sort_direction]).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when the query contains categories" do
|
|
it "surrounds categories in quotes" do
|
|
[
|
|
{ query: "M/F sort by: comments", expected: "\"m/f\"" },
|
|
{ query: "f/f Scully/Reyes", expected: "\"f/f\" Scully/Reyes" },
|
|
].each do |settings|
|
|
call_with_params(query: settings[:query])
|
|
expect(controller.params[:work_search][:query]).to eq(settings[:expected])
|
|
end
|
|
end
|
|
|
|
it "does not surround categories in quotes when it shouldn't" do
|
|
query = "sam/frodo sort by: word"
|
|
call_with_params(query: query)
|
|
expect(controller.params[:work_search][:query]).to eq("sam/frodo")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "new" do
|
|
it "doesn't return the form for anyone not logged in" do
|
|
get :new
|
|
it_redirects_to_with_error(new_user_session_path,
|
|
"Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
|
|
end
|
|
|
|
it "renders the form if logged in" do
|
|
fake_login
|
|
get :new
|
|
expect(response).to render_template("new")
|
|
end
|
|
|
|
it "errors and redirects to user page when user is banned" do
|
|
fake_login_known_user(banned_user)
|
|
get :new
|
|
it_redirects_to_simple(user_path(banned_user))
|
|
expect(flash[:error]).to include("Your account has been banned.")
|
|
end
|
|
end
|
|
|
|
describe "create" do
|
|
let(:user) { create(:user) }
|
|
|
|
before { fake_login_known_user(user) }
|
|
|
|
it "doesn't allow a user to create a work in a series that they don't own" do
|
|
@series = create(:series)
|
|
work_attributes = attributes_for(:work).except(:posted)
|
|
work_attributes[:series_attributes] = { id: @series.id }
|
|
expect {
|
|
post :create, params: { work: work_attributes }
|
|
}.not_to change { @series.works.all.count }
|
|
expect(response).to render_template :new
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include("You can't add a work to that series.")
|
|
end
|
|
|
|
it "doesn't allow a user to submit only a pseud that is not theirs" do
|
|
user2 = create(:user)
|
|
work_attributes = attributes_for(:work).except(:posted)
|
|
work_attributes[:author_attributes] = { ids: [user2.pseuds.first.id] }
|
|
expect {
|
|
post :create, params: { work: work_attributes }
|
|
}.to_not change(Work, :count)
|
|
expect(response).to render_template("new")
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include "You're not allowed to use that pseud."
|
|
end
|
|
|
|
it "renders new if the work has invalid pseuds" do
|
|
work_attributes = attributes_for(:work).except(:posted)
|
|
work_attributes[:author_attributes] = { ids: user.pseud_ids,
|
|
byline: "*impossible*" }
|
|
post :create, params: { work: work_attributes }
|
|
expect(response).to render_template("new")
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include "Invalid creator: Could not find a pseud *impossible*."
|
|
end
|
|
|
|
it "renders new if the work has ambiguous pseuds" do
|
|
create(:pseud, name: "ambiguous")
|
|
create(:pseud, name: "ambiguous")
|
|
work_attributes = attributes_for(:work).except(:posted)
|
|
work_attributes[:author_attributes] = { ids: user.pseud_ids,
|
|
byline: "ambiguous" }
|
|
post :create, params: { work: work_attributes }
|
|
expect(response).to render_template("new")
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include "Invalid creator: The pseud ambiguous is ambiguous."
|
|
end
|
|
|
|
it "renders new if the work has noncanonical warnings" do
|
|
work_attributes = attributes_for(:work).except(:posted, :archive_warning_string).merge(archive_warning_string: "Warning")
|
|
post :create, params: { work: work_attributes }
|
|
expect(response).to render_template("new")
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include /Only canonical warning tags are allowed./
|
|
end
|
|
|
|
it "renders new if the work has noncanonical rating" do
|
|
work_attributes = attributes_for(:work).except(:posted, :rating_string).merge(rating_string: "Rating")
|
|
post :create, params: { work: work_attributes }
|
|
expect(response).to render_template("new")
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include /Only canonical rating tags are allowed./
|
|
end
|
|
|
|
it "renders new if the work has noncanonical category" do
|
|
work_attributes = attributes_for(:work).except(:posted).merge(category_strings: ["Category"])
|
|
post :create, params: { work: work_attributes }
|
|
expect(response).to render_template("new")
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include /Only canonical category tags are allowed./
|
|
end
|
|
|
|
context "as a tag wrangler" do
|
|
let(:user) { create(:tag_wrangler) }
|
|
|
|
it "does not set wrangling activity when posting with a new fandom" do
|
|
work_attributes = attributes_for(:work).except(:posted, :fandom_string).merge(fandom_string: "New Fandom")
|
|
post :create, params: { work: work_attributes }
|
|
expect(user.last_wrangling_activity).to be_nil
|
|
end
|
|
|
|
it "does not set wrangling activity when posting with an unsorted tag" do
|
|
tag = create(:unsorted_tag)
|
|
work_attributes = attributes_for(:work).except(:posted, :freeform_string).merge(freeform_string: tag.name)
|
|
post :create, params: { work: work_attributes }
|
|
expect(user.last_wrangling_activity).to be_nil
|
|
end
|
|
end
|
|
|
|
it "errors and redirects to user page when user is banned" do
|
|
fake_login_known_user(banned_user)
|
|
tag = create(:unsorted_tag)
|
|
work_attributes = attributes_for(:work).except(:posted, :freeform_string).merge(freeform_string: tag.name)
|
|
post :create, params: { work: work_attributes }
|
|
it_redirects_to_simple(user_path(banned_user))
|
|
expect(flash[:error]).to include("Your account has been banned.")
|
|
end
|
|
end
|
|
|
|
describe "show" do
|
|
let(:work) { create(:work) }
|
|
|
|
it "doesn't error when a work has no fandoms" do
|
|
work_no_fandoms = build(:work, fandom_string: "")
|
|
work_no_fandoms.save!(validate: false)
|
|
fake_login
|
|
|
|
get :show, params: { id: work_no_fandoms.id }
|
|
|
|
expect(assigns(:page_title)).to include "No fandom specified"
|
|
end
|
|
|
|
it "assigns @page_subtitle with unrevealed work and not @page_title" do
|
|
work.update!(in_unrevealed_collection: true)
|
|
get :show, params: { id: work.id }
|
|
expect(assigns[:page_subtitle]).to eq("Mystery Work")
|
|
expect(assigns[:page_title]).to be_nil
|
|
end
|
|
|
|
context "when work does not exist" do
|
|
it "raises an error" do
|
|
expect do
|
|
get :show, params: { id: "999999999" }
|
|
end.to raise_error ActiveRecord::RecordNotFound
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "share" do
|
|
it "returns a 404 response for unrevealed works" do
|
|
unrevealed_collection = create :unrevealed_collection
|
|
unrevealed_work = create :work, collections: [unrevealed_collection]
|
|
|
|
get :share, params: { id: unrevealed_work.id }, xhr: true
|
|
expect(response.status).to eq(404)
|
|
end
|
|
|
|
it "redirects to referer with an error for non-ajax warnings requests" do
|
|
work = create(:work)
|
|
referer = work_path(work)
|
|
request.headers["HTTP_REFERER"] = referer
|
|
get :share, params: { id: work.id }
|
|
it_redirects_to_with_error(referer, "Sorry, you need to have JavaScript enabled for this.")
|
|
end
|
|
end
|
|
|
|
describe "index" do
|
|
before do
|
|
@fandom = create(:canonical_fandom)
|
|
@work = create(:work, fandom_string: @fandom.name)
|
|
end
|
|
|
|
it "returns the work" do
|
|
get :index
|
|
expect(assigns(:works)).to include(@work)
|
|
end
|
|
|
|
it "sets the fandom when given a fandom id" do
|
|
params = { fandom_id: @fandom.id }
|
|
get :index, params: params
|
|
expect(assigns(:fandom)).to eq(@fandom)
|
|
end
|
|
|
|
describe "when the fandom id is invalid" do
|
|
it "raises a 404 for an invalid id" do
|
|
params = { fandom_id: 0 }
|
|
expect { get :index, params: params }
|
|
.to raise_error ActiveRecord::RecordNotFound
|
|
end
|
|
end
|
|
|
|
describe "when the fandom id is empty" do
|
|
it "returns the work" do
|
|
params = { fandom_id: nil }
|
|
get :index, params: params
|
|
expect(assigns(:works)).to include(@work)
|
|
end
|
|
end
|
|
|
|
describe "without caching" do
|
|
before do
|
|
AdminSetting.first.update_attribute(:enable_test_caching, false)
|
|
end
|
|
|
|
it "returns the result with different works the second time" do
|
|
get :index
|
|
expect(assigns(:works)).to include(@work)
|
|
work2 = create(:work)
|
|
get :index
|
|
expect(assigns(:works)).to include(work2)
|
|
end
|
|
end
|
|
|
|
describe "with caching" do
|
|
before do
|
|
AdminSetting.first.update_attribute(:enable_test_caching, true)
|
|
end
|
|
|
|
context "with NO owner tag" do
|
|
it "returns the same result the second time when a new work is created within the expiration time" do
|
|
get :index
|
|
expect(assigns(:works)).to include(@work)
|
|
work2 = create(:work)
|
|
run_all_indexing_jobs
|
|
get :index
|
|
expect(assigns(:works)).not_to include(work2)
|
|
end
|
|
end
|
|
|
|
context "with a valid owner tag" do
|
|
before do
|
|
@fandom2 = create(:canonical_fandom)
|
|
@work2 = create(:work, fandom_string: @fandom2.name)
|
|
run_all_indexing_jobs
|
|
end
|
|
|
|
it "only gets works under that tag" do
|
|
get :index, params: { tag_id: @fandom.name }
|
|
expect(assigns(:works).items).to include(@work)
|
|
expect(assigns(:works).items).not_to include(@work2)
|
|
end
|
|
|
|
it "shows different results on second page" do
|
|
get :index, params: { tag_id: @fandom.name, page: 2 }
|
|
expect(assigns(:works).items).not_to include(@work)
|
|
end
|
|
|
|
context "with restricted works" do
|
|
before do
|
|
@work2 = create(:work, fandom_string: @fandom.name, restricted: true)
|
|
run_all_indexing_jobs
|
|
end
|
|
|
|
it "shows restricted works to guests" do
|
|
get :index, params: { tag_id: @fandom.name }
|
|
expect(assigns(:works).items).to include(@work)
|
|
expect(assigns(:works).items).not_to include(@work2)
|
|
end
|
|
|
|
end
|
|
|
|
context "when tag is a synonym" do
|
|
let(:fandom_synonym) { create(:fandom, merger: @fandom) }
|
|
|
|
it "redirects to the merger's work index" do
|
|
params = { tag_id: fandom_synonym.name }
|
|
get :index, params: params
|
|
it_redirects_to tag_works_path(@fandom)
|
|
end
|
|
|
|
context "when collection is specified" do
|
|
let(:collection) { create(:collection) }
|
|
|
|
it "redirects to the merger's collection works index" do
|
|
params = { tag_id: fandom_synonym.name, collection_id: collection.name }
|
|
get :index, params: params
|
|
it_redirects_to collection_tag_works_path(collection, @fandom)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with an invalid owner tag" do
|
|
it "raises an error" do
|
|
params = { tag_id: "nonexistent_tag" }
|
|
expect { get :index, params: params }.to raise_error(
|
|
ActiveRecord::RecordNotFound,
|
|
"Couldn't find tag named 'nonexistent_tag'"
|
|
)
|
|
end
|
|
end
|
|
|
|
context "with an invalid owner user" do
|
|
it "raises an error" do
|
|
params = { user_id: "nonexistent_user" }
|
|
expect { get :index, params: params }.to raise_error(
|
|
ActiveRecord::RecordNotFound
|
|
)
|
|
end
|
|
|
|
context "with an invalid pseud" do
|
|
it "raises an error" do
|
|
params = { user_id: "nonexistent_user", pseud_id: "nonexistent_pseud" }
|
|
expect { get :index, params: params }.to raise_error(
|
|
ActiveRecord::RecordNotFound
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with a valid owner user" do
|
|
let(:user) { create(:user) }
|
|
let!(:user_work) { create(:work, authors: [user.default_pseud]) }
|
|
let(:pseud) { create(:pseud, user: user) }
|
|
let!(:pseud_work) { create(:work, authors: [pseud]) }
|
|
|
|
before { run_all_indexing_jobs }
|
|
|
|
it "includes only works for that user" do
|
|
params = { user_id: user.login }
|
|
get :index, params: params
|
|
expect(assigns(:works).items).to include(user_work, pseud_work)
|
|
expect(assigns(:works).items).not_to include(@work)
|
|
end
|
|
|
|
context "with a valid pseud" do
|
|
it "includes only works for that pseud" do
|
|
params = { user_id: user.login, pseud_id: pseud.name }
|
|
get :index, params: params
|
|
expect(assigns(:works).items).to include(pseud_work)
|
|
expect(assigns(:works).items).not_to include(user_work, @work)
|
|
end
|
|
end
|
|
|
|
context "with an invalid pseud" do
|
|
it "includes all of that user's works" do
|
|
params = { user_id: user.login, pseud_id: "nonexistent_pseud" }
|
|
get :index, params: params
|
|
expect(assigns(:works).items).to include(user_work, pseud_work)
|
|
expect(assigns(:works).items).not_to include(@work)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "update" do
|
|
let(:update_user) { create(:user) }
|
|
let(:update_work) {
|
|
work = create(:work, authors: [update_user.default_pseud])
|
|
create(:chapter, work: work)
|
|
work
|
|
}
|
|
|
|
before do
|
|
fake_login_known_user(update_user)
|
|
end
|
|
|
|
it "doesn't allow the user to add a series that they don't own" do
|
|
@series = create(:series)
|
|
attrs = { series_attributes: { id: @series.id } }
|
|
expect {
|
|
put :update, params: { id: update_work.id, work: attrs }
|
|
}.not_to change { @series.works.all.count }
|
|
expect(response).to render_template :edit
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include("You can't add a work to that series.")
|
|
end
|
|
|
|
it "redirects to the edit page if the work could not be saved" do
|
|
allow_any_instance_of(Work).to receive(:save).and_return(false)
|
|
update_work.fandom_string = "Testing"
|
|
attrs = { title: "New Work Title" }
|
|
put :update, params: { id: update_work.id, work: attrs }
|
|
expect(response).to render_template :edit
|
|
allow_any_instance_of(Work).to receive(:save).and_call_original
|
|
end
|
|
|
|
it "updates the editor's pseuds for all chapters" do
|
|
new_pseud = create(:pseud, user: update_user)
|
|
put :update, params: { id: update_work.id, work: { author_attributes: { ids: [new_pseud.id] } } }
|
|
expect(update_work.pseuds.reload).to contain_exactly(new_pseud)
|
|
update_work.chapters.reload.each do |c|
|
|
expect(c.pseuds.reload).to contain_exactly(new_pseud)
|
|
end
|
|
end
|
|
|
|
it "allows the user to invite co-creators" do
|
|
co_creator = create(:user)
|
|
co_creator.preference.update!(allow_cocreator: true)
|
|
put :update, params: { id: update_work.id, work: { author_attributes: { byline: co_creator.login } } }
|
|
expect(update_work.pseuds.reload).not_to include(co_creator.default_pseud)
|
|
expect(update_work.user_has_creator_invite?(co_creator)).to be_truthy
|
|
end
|
|
|
|
it "prevents inviting users who have disallowed co-creators" do
|
|
no_co_creator = create(:user)
|
|
no_co_creator.preference.update!(allow_cocreator: false)
|
|
put :update, params: { id: update_work.id, work: { author_attributes: { byline: no_co_creator.login } } }
|
|
expect(response).to render_template :edit
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include "Invalid creator: #{no_co_creator.login} does not allow others to invite them to be a co-creator."
|
|
expect(update_work.pseuds.reload).not_to include(no_co_creator.default_pseud)
|
|
expect(update_work.user_has_creator_invite?(no_co_creator)).to be_falsey
|
|
end
|
|
|
|
context "when the work has broken dates" do
|
|
let(:update_work) { create(:work, authors: [update_user.default_pseud]) }
|
|
let(:update_chapter) { update_work.first_chapter }
|
|
|
|
let(:attributes) do
|
|
{
|
|
backdate: "1",
|
|
chapter_attributes: {
|
|
published_at: "2021-09-01"
|
|
}
|
|
}
|
|
end
|
|
|
|
before do
|
|
# Work where chapter published_at did not override revised_at, times
|
|
# taken from AO3-5392
|
|
update_work.update_column(:revised_at, Time.new(2018, 4, 22, 23, 51, 42, "+04:00"))
|
|
update_chapter.update_column(:published_at, Date.new(2015, 7, 23))
|
|
end
|
|
|
|
it "can be backdated" do
|
|
put :update, params: { id: update_work.id, work: attributes }
|
|
|
|
expect(update_chapter.reload.published_at).to eq(Date.new(2021, 9, 1))
|
|
expect(update_work.reload.revised_at).to eq(Time.utc(2021, 9, 1, 12)) # noon UTC
|
|
end
|
|
end
|
|
|
|
# If the time zone in config/application.rb is changed to something other
|
|
# than the default (UTC), these tests will need adjusting:
|
|
context "when redating to the present" do
|
|
let!(:update_work) do
|
|
# November 30, 2 PM UTC -- no time zone oddities here
|
|
travel_to(Time.utc(2021, 11, 30, 14)) do
|
|
create(:work, authors: [update_user.default_pseud])
|
|
end
|
|
end
|
|
|
|
let(:attributes) do
|
|
{
|
|
backdate: "1",
|
|
chapter_attributes: {
|
|
published_at: "2021-12-05"
|
|
}
|
|
}
|
|
end
|
|
|
|
before do
|
|
travel_to(redate_time)
|
|
|
|
# Simulate the system time being UTC:
|
|
allow(Time).to receive(:now).and_return(redate_time)
|
|
allow(DateTime).to receive(:now).and_return(redate_time)
|
|
allow(Date).to receive(:today).and_return(redate_time.to_date)
|
|
|
|
put :update, params: { id: update_work.id, work: attributes }
|
|
end
|
|
|
|
context "before midnight UTC and after midnight Samara" do
|
|
# December 5, 3 AM Europe/Samara (UTC+04:00) -- still December 4 in UTC
|
|
let(:redate_time) { Time.new(2021, 12, 5, 3, 0, 0, "+04:00") }
|
|
|
|
it "prevents setting the publication date to the future" do
|
|
expect(response).to render_template :edit
|
|
expect(assigns[:work].errors.full_messages).to \
|
|
include("Publication date can't be in the future.")
|
|
end
|
|
end
|
|
|
|
context "before noon UTC" do
|
|
# December 5, 6 AM Europe/Samara -- before noon, but after midnight in both time zones
|
|
let(:redate_time) { Time.new(2021, 12, 5, 6, 0, 0, "+04:00") }
|
|
|
|
it "doesn't set revised_at to the future" do
|
|
update_work.reload
|
|
expect(update_work.revised_at).to be <= Time.current
|
|
end
|
|
end
|
|
end
|
|
|
|
it "errors and redirects to user page when user is banned" do
|
|
fake_login_known_user(banned_user)
|
|
attrs = { title: "New Work Title" }
|
|
put :update, params: { id: banned_users_work.id, work: attrs }
|
|
it_redirects_to_simple(user_path(banned_user))
|
|
expect(flash[:error]).to include("Your account has been banned.")
|
|
end
|
|
end
|
|
|
|
describe "collected" do
|
|
let(:collection) { create(:collection) }
|
|
let(:collected_user) { create(:user) }
|
|
|
|
it "returns not found error if user does not exist" do
|
|
expect do
|
|
get :collected, params: { user_id: "dummyuser" }
|
|
end.to raise_error(ActiveRecord::RecordNotFound)
|
|
end
|
|
|
|
it "returns not found error if no user is set" do
|
|
expect do
|
|
get :collected
|
|
end.to raise_error(ActiveRecord::RecordNotFound)
|
|
end
|
|
|
|
context "with anonymous works" do
|
|
let(:anonymous_collection) { create(:anonymous_collection) }
|
|
|
|
let!(:work) do
|
|
create(:work,
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: collection.name)
|
|
end
|
|
|
|
let!(:anonymous_work) do
|
|
create(:work,
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: anonymous_collection.name)
|
|
end
|
|
|
|
before { run_all_indexing_jobs }
|
|
|
|
it "does not return anonymous works in collections for guests" do
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works)).to include(work)
|
|
expect(assigns(:works)).not_to include(anonymous_work)
|
|
end
|
|
|
|
it "does not return anonymous works in collections for logged-in users" do
|
|
fake_login
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works)).to include(work)
|
|
expect(assigns(:works)).not_to include(anonymous_work)
|
|
end
|
|
|
|
it "returns anonymous works in collections for the author" do
|
|
fake_login_known_user(collected_user)
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works)).to include(work, anonymous_work)
|
|
end
|
|
end
|
|
|
|
context "with restricted works" do
|
|
let(:collected_fandom) { create(:canonical_fandom) }
|
|
let(:collected_fandom_2) { create(:canonical_fandom) }
|
|
|
|
let!(:unrestricted_work) do
|
|
create(:work,
|
|
authors: [collected_user.default_pseud],
|
|
fandom_string: collected_fandom.name)
|
|
end
|
|
|
|
let!(:unrestricted_work_in_collection) do
|
|
create(:work,
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: collection.name,
|
|
fandom_string: collected_fandom.name)
|
|
end
|
|
|
|
let!(:unrestricted_work_2_in_collection) do
|
|
create(:work,
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: collection.name,
|
|
fandom_string: collected_fandom_2.name)
|
|
end
|
|
|
|
let!(:restricted_work_in_collection) do
|
|
create(:work,
|
|
restricted: true,
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: collection.name,
|
|
fandom_string: collected_fandom.name)
|
|
end
|
|
|
|
before { run_all_indexing_jobs }
|
|
|
|
context "as a guest" do
|
|
it "renders the collected form" do
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(response).to render_template("collected")
|
|
end
|
|
|
|
it "returns ONLY unrestricted works in collections" do
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works)).to include(unrestricted_work_in_collection, unrestricted_work_2_in_collection)
|
|
expect(assigns(:works)).not_to include(unrestricted_work, restricted_work_in_collection)
|
|
end
|
|
|
|
it "returns filtered works when search parameters are provided" do
|
|
get :collected, params: { user_id: collected_user.login, work_search: { query: "fandom_ids:#{collected_fandom_2.id}" }}
|
|
expect(assigns(:works)).to include(unrestricted_work_2_in_collection)
|
|
expect(assigns(:works)).not_to include(unrestricted_work_in_collection)
|
|
end
|
|
end
|
|
|
|
context "with a logged-in user" do
|
|
before { fake_login }
|
|
|
|
it "returns ONLY works in collections" do
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works)).to include(unrestricted_work_in_collection, restricted_work_in_collection)
|
|
expect(assigns(:works)).not_to include(unrestricted_work)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with unrevealed works" do
|
|
let(:unrevealed_collection) { create(:unrevealed_collection) }
|
|
|
|
let!(:work) do
|
|
create(:work,
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: collection.name)
|
|
end
|
|
|
|
let!(:unrevealed_work) do
|
|
create(:work,
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: unrevealed_collection.name)
|
|
end
|
|
|
|
before { run_all_indexing_jobs }
|
|
|
|
it "doesn't return unrevealed works in collections for guests" do
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works)).to include(work)
|
|
expect(assigns(:works)).not_to include(unrevealed_work)
|
|
end
|
|
|
|
it "doesn't return unrevealed works in collections for logged-in users" do
|
|
fake_login
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works)).to include(work)
|
|
expect(assigns(:works)).not_to include(unrevealed_work)
|
|
end
|
|
|
|
it "returns unrevealed works in collections for the author" do
|
|
fake_login_known_user(collected_user)
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works)).to include(work, unrevealed_work)
|
|
end
|
|
end
|
|
|
|
context "with sorting options" do
|
|
let!(:new_work) do
|
|
create(:work,
|
|
title: "New Title",
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: collection.name,
|
|
created_at: 3.days.ago,
|
|
revised_at: 3.days.ago)
|
|
end
|
|
|
|
let!(:old_work) do
|
|
create(:work,
|
|
title: "Old Title",
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: collection.name,
|
|
created_at: 30.days.ago,
|
|
revised_at: 30.days.ago)
|
|
end
|
|
|
|
let!(:revised_work) do
|
|
create(:work,
|
|
title: "Revised Title",
|
|
authors: [collected_user.default_pseud],
|
|
collection_names: collection.name,
|
|
created_at: 20.days.ago,
|
|
revised_at: 2.days.ago)
|
|
end
|
|
|
|
before { run_all_indexing_jobs }
|
|
|
|
it "sorts by date" do
|
|
get :collected, params: { user_id: collected_user.login }
|
|
expect(assigns(:works).map(&:title)).to eq([revised_work, new_work, old_work].map(&:title))
|
|
get :collected, params: { user_id: collected_user.login, work_search: { sort_direction: "asc" } }
|
|
expect(assigns(:works).map(&:title)).to eq([old_work, new_work, revised_work].map(&:title))
|
|
end
|
|
|
|
it "sorts by title" do
|
|
get :collected, params: { user_id: collected_user.login, work_search: { sort_column: "title_to_sort_on" } }
|
|
expect(assigns(:works).map(&:title)).to eq([new_work, old_work, revised_work].map(&:title))
|
|
get :collected, params: { user_id: collected_user.login, work_search: { sort_column: "title_to_sort_on", sort_direction: "desc" } }
|
|
expect(assigns(:works).map(&:title)).to eq([revised_work, old_work, new_work].map(&:title))
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "destroy" do
|
|
let(:work) { create(:work) }
|
|
let(:work_title) { work.title }
|
|
|
|
context "when a work has consecutive deleted comments in a thread" do
|
|
before do
|
|
thread_depth = 4
|
|
chapter = work.first_chapter
|
|
|
|
commentable = chapter
|
|
comments = []
|
|
thread_depth.times do
|
|
commentable = create(:comment, commentable: commentable, parent: chapter)
|
|
comments << commentable
|
|
end
|
|
|
|
# Delete all but the last comment in the thread.
|
|
# We should have (thread_depth - 1) consecutive deleted comment placeholders.
|
|
comments.reverse.drop(1).each(&:destroy_or_mark_deleted)
|
|
|
|
fake_login_known_user(work.users.first)
|
|
end
|
|
|
|
it "deletes the work and redirects to the user's works with a notice" do
|
|
delete :destroy, params: { id: work.id }
|
|
|
|
it_redirects_to_with_notice(user_works_path(controller.current_user), "Your work #{work_title} was deleted.")
|
|
expect { work.reload }.to raise_exception(ActiveRecord::RecordNotFound)
|
|
expect(Comment.count).to eq(0)
|
|
end
|
|
end
|
|
|
|
context "when a logged in user is suspended" do
|
|
before do
|
|
fake_login_known_user(suspended_user)
|
|
end
|
|
|
|
it "errors and redirects to user page" do
|
|
fake_login_known_user(suspended_user)
|
|
delete :destroy, params: { id: suspended_users_work.id }
|
|
|
|
it_redirects_to_simple(user_path(suspended_user))
|
|
expect(flash[:error]).to include("Your account has been suspended")
|
|
end
|
|
end
|
|
|
|
context "when a logged in user is banned" do
|
|
before do
|
|
fake_login_known_user(banned_user)
|
|
end
|
|
|
|
it "deletes the work and redirects to the user's works with a notice" do
|
|
delete :destroy, params: { id: banned_users_work.id }
|
|
|
|
it_redirects_to_with_notice(user_works_path(controller.current_user), "Your work #{banned_users_work.title} was deleted.")
|
|
expect { banned_users_work.reload }
|
|
.to raise_exception(ActiveRecord::RecordNotFound)
|
|
end
|
|
end
|
|
end
|
|
end
|