443 lines
16 KiB
Ruby
443 lines
16 KiB
Ruby
# frozen_string_literal: true
|
||
|
||
module DiscoursePostEvent
|
||
describe EventsController do
|
||
before do
|
||
Jobs.run_immediately!
|
||
SiteSetting.calendar_enabled = true
|
||
SiteSetting.discourse_post_event_enabled = true
|
||
SiteSetting.displayed_invitees_limit = 3
|
||
end
|
||
|
||
describe "#index" do
|
||
fab!(:event_1) { Fabricate(:event, original_starts_at: 1.day.from_now) }
|
||
|
||
it "should not result in N+1 queries problem when multiple events are returned" do
|
||
original_queries = track_sql_queries { get "/discourse-post-event/events.json" }
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(response.parsed_body["events"].length).to eq(1)
|
||
|
||
event_2 = Fabricate(:event, original_starts_at: 2.days.from_now)
|
||
event_3 = Fabricate(:event, original_starts_at: 3.days.from_now)
|
||
|
||
new_queries = track_sql_queries { get "/discourse-post-event/events.json" }
|
||
|
||
expect(response.status).to eq(200)
|
||
expect(response.parsed_body["events"].length).to eq(3)
|
||
|
||
# TODO: There is still N+1 query problem here so uncomment this line when it is fixed
|
||
# expect(new_queries.count).to eq(original_queries.count)
|
||
end
|
||
end
|
||
|
||
context "with an existing post" do
|
||
let(:user) { Fabricate(:user, admin: true) }
|
||
let(:topic) { Fabricate(:topic, user: user) }
|
||
let(:post1) { Fabricate(:post, user: user, topic: topic) }
|
||
let(:invitee1) { Fabricate(:user) }
|
||
let(:invitee2) { Fabricate(:user) }
|
||
|
||
context "with an existing event" do
|
||
let(:event_1) { Fabricate(:event, post: post1) }
|
||
|
||
before { sign_in(user) }
|
||
|
||
context "when updating" do
|
||
context "when doing csv bulk invite" do
|
||
let(:valid_file) do
|
||
file = Tempfile.new("valid.csv")
|
||
file.write("bob,going\n")
|
||
file.write("sam,interested\n")
|
||
file.write("the_foo_bar_group,not_going\n")
|
||
file.rewind
|
||
file
|
||
end
|
||
|
||
let(:empty_file) do
|
||
file = Tempfile.new("invalid.pdf")
|
||
file.rewind
|
||
file
|
||
end
|
||
|
||
context "when current user can manage the event" do
|
||
context "when no file is given" do
|
||
it "returns an error" do
|
||
post "/discourse-post-event/events/#{event_1.id}/csv-bulk-invite.json"
|
||
expect(response.parsed_body["error_type"]).to eq("invalid_parameters")
|
||
end
|
||
end
|
||
|
||
context "when an empty file is given" do
|
||
it "returns an error" do
|
||
post "/discourse-post-event/events/#{event_1.id}/csv-bulk-invite.json",
|
||
params: {
|
||
file: fixture_file_upload(empty_file),
|
||
}
|
||
expect(response.status).to eq(422)
|
||
end
|
||
end
|
||
|
||
context "when a valid file is given" do
|
||
before { Jobs.run_later! }
|
||
|
||
it "enqueues the job and returns 200" do
|
||
expect_enqueued_with(
|
||
job: :discourse_post_event_bulk_invite,
|
||
args: {
|
||
"event_id" => event_1.id,
|
||
"invitees" => [
|
||
{ "identifier" => "bob", "attendance" => "going" },
|
||
{ "identifier" => "sam", "attendance" => "interested" },
|
||
{ "identifier" => "the_foo_bar_group", "attendance" => "not_going" },
|
||
],
|
||
"current_user_id" => user.id,
|
||
},
|
||
) do
|
||
post "/discourse-post-event/events/#{event_1.id}/csv-bulk-invite.json",
|
||
params: {
|
||
file: fixture_file_upload(valid_file),
|
||
}
|
||
end
|
||
|
||
expect(response.status).to eq(200)
|
||
end
|
||
end
|
||
end
|
||
|
||
context "when current user can’t manage the event" do
|
||
let(:lurker) { Fabricate(:user) }
|
||
|
||
before { sign_in(lurker) }
|
||
|
||
it "returns an error" do
|
||
post "/discourse-post-event/events/#{event_1.id}/csv-bulk-invite.json"
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
end
|
||
|
||
context "when doing bulk invite" do
|
||
context "when current user can manage the event" do
|
||
context "when no invitees are given" do
|
||
it "returns an error" do
|
||
post "/discourse-post-event/events/#{event_1.id}/bulk-invite.json"
|
||
expect(response.parsed_body["error_type"]).to eq("invalid_parameters")
|
||
end
|
||
end
|
||
|
||
context "when empty invitees are given" do
|
||
it "returns an error" do
|
||
post "/discourse-post-event/events/#{event_1.id}/bulk-invite.json",
|
||
params: {
|
||
invitees: [],
|
||
}
|
||
expect(response.status).to eq(400)
|
||
end
|
||
end
|
||
|
||
context "when valid invitees are given" do
|
||
before { Jobs.run_later! }
|
||
|
||
it "enqueues the job and returns 200" do
|
||
expect_enqueued_with(
|
||
job: :discourse_post_event_bulk_invite,
|
||
args: {
|
||
"event_id" => event_1.id,
|
||
"invitees" => [
|
||
{ "identifier" => "bob", "attendance" => "going" },
|
||
{ "identifier" => "sam", "attendance" => "interested" },
|
||
{ "identifier" => "the_foo_bar_group", "attendance" => "not_going" },
|
||
],
|
||
"current_user_id" => user.id,
|
||
},
|
||
) do
|
||
post "/discourse-post-event/events/#{event_1.id}/bulk-invite.json",
|
||
params: {
|
||
invitees: [
|
||
{ "identifier" => "bob", "attendance" => "going" },
|
||
{ "identifier" => "sam", "attendance" => "interested" },
|
||
{ "identifier" => "the_foo_bar_group", "attendance" => "not_going" },
|
||
],
|
||
}
|
||
end
|
||
|
||
expect(response.status).to eq(200)
|
||
end
|
||
end
|
||
end
|
||
|
||
context "when current user can’t manage the event" do
|
||
let(:lurker) { Fabricate(:user) }
|
||
|
||
before { sign_in(lurker) }
|
||
|
||
it "returns an error" do
|
||
post "/discourse-post-event/events/#{event_1.id}/bulk-invite.json"
|
||
expect(response.status).to eq(403)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
context "when acting user has created the event" do
|
||
it "destroys a event" do
|
||
expect(event_1.persisted?).to be(true)
|
||
|
||
messages =
|
||
MessageBus.track_publish { delete "/discourse-post-event/events/#{event_1.id}.json" }
|
||
expect(messages.count).to eq(1)
|
||
message = messages.first
|
||
expect(message.channel).to eq("/discourse-post-event/#{event_1.post.topic_id}")
|
||
expect(message.data[:id]).to eq(event_1.id)
|
||
expect(response.status).to eq(200)
|
||
expect(Event).to_not exist(id: event_1.id)
|
||
end
|
||
end
|
||
|
||
context "when acting user has not created the event" do
|
||
let(:lurker) { Fabricate(:user) }
|
||
|
||
before { sign_in(lurker) }
|
||
|
||
it "doesn’t destroy the event" do
|
||
expect(event_1.persisted?).to be(true)
|
||
delete "/discourse-post-event/events/#{event_1.id}.json"
|
||
expect(response.status).to eq(403)
|
||
expect(Event).to exist(id: event_1.id)
|
||
end
|
||
end
|
||
|
||
context "when watching user is not logged" do
|
||
before { sign_out }
|
||
|
||
context "when topic is public" do
|
||
it "can see the event" do
|
||
get "/discourse-post-event/events/#{event_1.id}.json"
|
||
|
||
expect(response.status).to eq(200)
|
||
end
|
||
end
|
||
|
||
context "when topic is not public" do
|
||
before { event_1.post.topic.convert_to_private_message(Discourse.system_user) }
|
||
|
||
it "can’t see the event" do
|
||
get "/discourse-post-event/events/#{event_1.id}.json"
|
||
|
||
expect(response.status).to eq(404)
|
||
end
|
||
end
|
||
end
|
||
|
||
context "when filtering by category" do
|
||
fab!(:category)
|
||
|
||
fab!(:subcategory) do
|
||
Fabricate(:category, parent_category: category, name: "category subcategory")
|
||
end
|
||
|
||
fab!(:event_1) do
|
||
Fabricate(
|
||
:event,
|
||
original_starts_at: 2.days.from_now,
|
||
post: Fabricate(:post, post_number: 1, topic: Fabricate(:topic, category: category)),
|
||
)
|
||
end
|
||
|
||
fab!(:event_2) do
|
||
Fabricate(
|
||
:event,
|
||
original_starts_at: 1.day.from_now,
|
||
post:
|
||
Fabricate(:post, post_number: 1, topic: Fabricate(:topic, category: subcategory)),
|
||
)
|
||
end
|
||
|
||
fab!(:event_3) do
|
||
Fabricate(
|
||
:event,
|
||
post: Fabricate(:post, post_number: 1, topic: Fabricate(:topic, category: category)),
|
||
original_starts_at: 10.days.ago,
|
||
original_ends_at: 9.days.ago,
|
||
)
|
||
end
|
||
|
||
it "can filter the event by category" do
|
||
get "/discourse-post-event/events.json?category_id=#{category.id}"
|
||
|
||
expect(response.status).to eq(200)
|
||
events = response.parsed_body["events"]
|
||
expect(events.length).to eq(1)
|
||
expect(events[0]["id"]).to eq(event_1.id)
|
||
end
|
||
|
||
it "includes subcategory events when param provided" do
|
||
get "/discourse-post-event/events.json?category_id=#{category.id}&include_subcategories=true"
|
||
|
||
expect(response.status).to eq(200)
|
||
events = response.parsed_body["events"]
|
||
expect(events.length).to eq(2)
|
||
expect(events).to match_array(
|
||
[hash_including("id" => event_1.id), hash_including("id" => event_2.id)],
|
||
)
|
||
end
|
||
|
||
it "includes events' details when param provided" do
|
||
get "/discourse-post-event/events.json?category_id=#{category.id}&include_subcategories=true&include_details=true"
|
||
|
||
expect(response.status).to eq(200)
|
||
events = response.parsed_body["events"]
|
||
expect(events.length).to eq(2)
|
||
expect(events[0].keys).to include(
|
||
"creator",
|
||
"sample_invitees",
|
||
"watching_invitee",
|
||
"stats",
|
||
"status",
|
||
"can_update_attendance",
|
||
"should_display_invitees",
|
||
"is_public",
|
||
"is_private",
|
||
"is_standalone",
|
||
)
|
||
end
|
||
|
||
it "includes expired events when param provided" do
|
||
get "/discourse-post-event/events.json?category_id=#{category.id}&include_subcategories=true&include_expired=true"
|
||
|
||
expect(response.status).to eq(200)
|
||
events = response.parsed_body["events"]
|
||
expect(events.length).to eq(3)
|
||
expect(events).to match_array(
|
||
[
|
||
hash_including("id" => event_1.id),
|
||
hash_including("id" => event_2.id),
|
||
hash_including("id" => event_3.id),
|
||
],
|
||
)
|
||
end
|
||
|
||
it "limits the number of events returned when limit param provided" do
|
||
get "/discourse-post-event/events.json?category_id=#{category.id}&include_subcategories=true&limit=1"
|
||
|
||
expect(response.status).to eq(200)
|
||
events = response.parsed_body["events"]
|
||
expect(events.length).to eq(1)
|
||
expect(events[0]["id"]).to eq(event_2.id)
|
||
end
|
||
|
||
it "filters events before the provided datetime if before param provided" do
|
||
get "/discourse-post-event/events.json?category_id=#{category.id}&include_subcategories=true&include_expired=true&before=#{event_2.starts_at}"
|
||
|
||
expect(response.status).to eq(200)
|
||
events = response.parsed_body["events"]
|
||
expect(events.length).to eq(1)
|
||
expect(events[0]["id"]).to eq(event_3.id)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
context "with a private event" do
|
||
let(:moderator) { Fabricate(:user, moderator: true) }
|
||
let(:topic) { Fabricate(:topic, user: moderator) }
|
||
let(:first_post) { Fabricate(:post, user: moderator, topic: topic) }
|
||
let(:private_event) { Fabricate(:event, post: first_post, status: Event.statuses[:private]) }
|
||
|
||
before { sign_in(moderator) }
|
||
|
||
context "when bulk inviting via CSV file" do
|
||
def csv_file(content)
|
||
file = Tempfile.new("invites.csv")
|
||
file.write(content)
|
||
file.rewind
|
||
file
|
||
end
|
||
|
||
it "doesn't invite a private group" do
|
||
private_group = Fabricate(:group, visibility_level: Group.visibility_levels[:owners])
|
||
|
||
file = csv_file("#{private_group.name},going\n")
|
||
params = { file: fixture_file_upload(file) }
|
||
post "/discourse-post-event/events/#{private_event.id}/csv-bulk-invite.json",
|
||
params: params
|
||
|
||
expect(response.status).to eq(200)
|
||
private_event.reload
|
||
expect(private_event.raw_invitees).to be_nil
|
||
end
|
||
|
||
it "returns 200 when inviting a non-existent group" do
|
||
file = csv_file("non-existent group name,going\n")
|
||
params = { file: fixture_file_upload(file) }
|
||
post "/discourse-post-event/events/#{private_event.id}/csv-bulk-invite.json",
|
||
params: params
|
||
|
||
expect(response.status).to eq(200)
|
||
end
|
||
|
||
it "doesn't invite a public group with private members" do
|
||
public_group_with_private_members =
|
||
Fabricate(
|
||
:group,
|
||
visibility_level: Group.visibility_levels[:public],
|
||
members_visibility_level: Group.visibility_levels[:owners],
|
||
)
|
||
|
||
file = csv_file("#{public_group_with_private_members.name},going\n")
|
||
params = { file: fixture_file_upload(file) }
|
||
post "/discourse-post-event/events/#{private_event.id}/csv-bulk-invite.json",
|
||
params: params
|
||
|
||
expect(response.status).to eq(200)
|
||
private_event.reload
|
||
expect(private_event.raw_invitees).to be_nil
|
||
end
|
||
end
|
||
|
||
context "when doing bulk inviting via UI" do
|
||
it "doesn't invite a private group" do
|
||
private_group = Fabricate(:group, visibility_level: Group.visibility_levels[:owners])
|
||
|
||
params = { invitees: [{ "identifier" => private_group.name, "attendance" => "going" }] }
|
||
post "/discourse-post-event/events/#{private_event.id}/bulk-invite.json", params: params
|
||
|
||
expect(response.status).to eq(200)
|
||
private_event.reload
|
||
expect(private_event.raw_invitees).to be_nil
|
||
end
|
||
|
||
it "returns 200 when inviting a non-existent group" do
|
||
params = {
|
||
invitees: [{ "identifier" => "non-existent group name", "attendance" => "going" }],
|
||
}
|
||
post "/discourse-post-event/events/#{private_event.id}/bulk-invite.json", params: params
|
||
|
||
expect(response.status).to eq(200)
|
||
end
|
||
|
||
it "doesn't invite a public group with private members" do
|
||
public_group_with_private_members =
|
||
Fabricate(
|
||
:group,
|
||
visibility_level: Group.visibility_levels[:public],
|
||
members_visibility_level: Group.visibility_levels[:owners],
|
||
)
|
||
|
||
params = {
|
||
invitees: [
|
||
{ "identifier" => public_group_with_private_members.name, "attendance" => "going" },
|
||
],
|
||
}
|
||
post "/discourse-post-event/events/#{private_event.id}/bulk-invite.json", params: params
|
||
|
||
expect(response.status).to eq(200)
|
||
private_event.reload
|
||
expect(private_event.raw_invitees).to be_nil
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|