mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 03:36:18 +08:00
FEATURE: Merge discourse-automation (#26432)
Automation (previously known as discourse-automation) is now a core plugin.
This commit is contained in:
@ -0,0 +1,122 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "AddUserTogroupThroughCustomField" do
|
||||
fab!(:user_1) { Fabricate(:user) }
|
||||
fab!(:user_2) { Fabricate(:user) }
|
||||
fab!(:target_group) { Fabricate(:group, full_name: "Groupity Group") }
|
||||
fab!(:user_field) do
|
||||
Fabricate(:user_field, name: "groupity_group", field_type: "text", description: "a nice field")
|
||||
end
|
||||
|
||||
fab!(:automation) { Fabricate(:automation, script: "add_user_to_group_through_custom_field") }
|
||||
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"custom_field_name",
|
||||
"custom_field",
|
||||
{ value: user_field.id },
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
context "with no matching user custom fields" do
|
||||
it "works" do
|
||||
expect(user_1.in_any_groups?([target_group.id])).to eq(false)
|
||||
expect(user_2.in_any_groups?([target_group.id])).to eq(false)
|
||||
|
||||
automation.trigger!("kind" => DiscourseAutomation::Triggers::RECURRING)
|
||||
|
||||
expect(user_1.reload.in_any_groups?([target_group.id])).to eq(false)
|
||||
expect(user_2.reload.in_any_groups?([target_group.id])).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "with one matching user" do
|
||||
before do
|
||||
UserCustomField.create!(
|
||||
user_id: user_1.id,
|
||||
name: "user_field_#{user_field.id}",
|
||||
value: target_group.full_name,
|
||||
)
|
||||
end
|
||||
|
||||
it "works" do
|
||||
expect(user_1.in_any_groups?([target_group.id])).to eq(false)
|
||||
expect(user_2.in_any_groups?([target_group.id])).to eq(false)
|
||||
|
||||
automation.trigger!("kind" => DiscourseAutomation::Triggers::RECURRING)
|
||||
|
||||
expect(user_1.reload.in_any_groups?([target_group.id])).to eq(true)
|
||||
expect(user_2.reload.in_any_groups?([target_group.id])).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "when group is already present" do
|
||||
before { target_group.add(user_1) }
|
||||
|
||||
it "works" do
|
||||
expect(user_1.in_any_groups?([target_group.id])).to eq(true)
|
||||
expect(user_2.in_any_groups?([target_group.id])).to eq(false)
|
||||
|
||||
automation.trigger!("kind" => DiscourseAutomation::Triggers::RECURRING)
|
||||
|
||||
expect(user_1.reload.in_any_groups?([target_group.id])).to eq(true)
|
||||
expect(user_2.reload.in_any_groups?([target_group.id])).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "with user_first_logged_in trigger" do
|
||||
fab!(:automation) { Fabricate(:automation, script: "add_user_to_group_through_custom_field") }
|
||||
|
||||
context "with existing custom fields" do
|
||||
before do
|
||||
UserCustomField.create!(
|
||||
user_id: user_1.id,
|
||||
name: "user_field_#{user_field.id}",
|
||||
value: target_group.full_name,
|
||||
)
|
||||
end
|
||||
|
||||
it "adds the user to the group" do
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user_1,
|
||||
)
|
||||
|
||||
expect(user_1.reload.in_any_groups?([target_group.id])).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "with non existing/matching custom fields" do
|
||||
it "does nothing" do
|
||||
expect {
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user_1,
|
||||
)
|
||||
}.not_to change { user_1.reload.belonging_to_group_ids.length }
|
||||
end
|
||||
end
|
||||
|
||||
context "with non existing target group" do
|
||||
before do
|
||||
UserCustomField.create!(
|
||||
user_id: user_1.id,
|
||||
name: "user_field_#{user_field.id}",
|
||||
value: "xx",
|
||||
)
|
||||
end
|
||||
|
||||
it "does nothing" do
|
||||
expect {
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user_1,
|
||||
)
|
||||
}.not_to change { user_1.reload.belonging_to_group_ids.length }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "AppendLastCheckedBy" do
|
||||
fab!(:post) { Fabricate(:post, raw: "this is a post with no edit") }
|
||||
fab!(:moderator)
|
||||
|
||||
fab!(:automation) do
|
||||
Fabricate(:automation, script: DiscourseAutomation::Scripts::APPEND_LAST_CHECKED_BY)
|
||||
end
|
||||
|
||||
def trigger_automation(post)
|
||||
cooked = automation.trigger!("post" => post, "cooked" => post.cooked)
|
||||
checked_at = post.updated_at + 1.hour
|
||||
date_time = checked_at.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
[cooked, checked_at, date_time]
|
||||
end
|
||||
|
||||
def text(key)
|
||||
I18n.t("discourse_automation.scriptables.append_last_checked_by.#{key}")
|
||||
end
|
||||
|
||||
describe "#trigger!" do
|
||||
it "works for newly created post" do
|
||||
cooked, checked_at, date_time = trigger_automation(post)
|
||||
|
||||
expect(cooked.include?("<blockquote class=\"discourse-automation\">")).to be_truthy
|
||||
expect(
|
||||
cooked.include?(
|
||||
"<details><summary>#{text("summary")}</summary>#{text("details")}<input type=\"button\" value=\"#{text("button_text")}\" class=\"btn btn-checked\"></details>",
|
||||
),
|
||||
).to be_truthy
|
||||
end
|
||||
|
||||
it "works for checked post" do
|
||||
topic = post.topic
|
||||
topic.custom_fields[DiscourseAutomation::TOPIC_LAST_CHECKED_BY] = moderator.username
|
||||
topic.custom_fields[DiscourseAutomation::TOPIC_LAST_CHECKED_AT] = post.updated_at + 1.hour
|
||||
topic.save_custom_fields
|
||||
|
||||
cooked, checked_at = trigger_automation(post)
|
||||
|
||||
expect(
|
||||
cooked.include?(
|
||||
PrettyText.cook(
|
||||
I18n.t(
|
||||
"discourse_automation.scriptables.append_last_checked_by.text",
|
||||
username: moderator.username,
|
||||
date_time:
|
||||
"[date=#{checked_at.to_date} time=#{checked_at.strftime("%H:%M:%S")} timezone=UTC]",
|
||||
),
|
||||
),
|
||||
),
|
||||
).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,73 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "AppendLastEditedBy" do
|
||||
fab!(:post) { Fabricate(:post, raw: "this is a post with no edit") }
|
||||
fab!(:moderator)
|
||||
|
||||
fab!(:automation) do
|
||||
Fabricate(:automation, script: DiscourseAutomation::Scripts::APPEND_LAST_EDITED_BY)
|
||||
end
|
||||
|
||||
def trigger_automation(post)
|
||||
cooked = automation.trigger!("post" => post, "cooked" => post.cooked)
|
||||
updated_at = post.updated_at
|
||||
date_time = updated_at.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
[cooked, updated_at]
|
||||
end
|
||||
|
||||
describe "#trigger!" do
|
||||
it "works for newly created post" do
|
||||
freeze_time
|
||||
|
||||
cooked, updated_at = trigger_automation(post)
|
||||
expect(
|
||||
cooked.include?(
|
||||
PrettyText.cook(
|
||||
I18n.t(
|
||||
"discourse_automation.scriptables.append_last_edited_by.text",
|
||||
username: post.user.username,
|
||||
date_time:
|
||||
"[date=#{updated_at.to_date} time=#{updated_at.strftime("%H:%M:%S")} timezone=UTC]",
|
||||
),
|
||||
),
|
||||
),
|
||||
).to be_truthy
|
||||
end
|
||||
|
||||
it "works for existing post with last edited by detail" do
|
||||
freeze_time
|
||||
|
||||
cooked, updated_at = trigger_automation(post)
|
||||
expect(
|
||||
cooked.include?(
|
||||
PrettyText.cook(
|
||||
I18n.t(
|
||||
"discourse_automation.scriptables.append_last_edited_by.text",
|
||||
username: post.user.username,
|
||||
date_time:
|
||||
"[date=#{updated_at.to_date} time=#{updated_at.strftime("%H:%M:%S")} timezone=UTC]",
|
||||
),
|
||||
),
|
||||
),
|
||||
).to be_truthy
|
||||
|
||||
PostRevisor.new(post).revise!(moderator, raw: "this is a post with edit")
|
||||
|
||||
cooked, updated_at = trigger_automation(post.reload)
|
||||
expect(
|
||||
cooked.include?(
|
||||
PrettyText.cook(
|
||||
I18n.t(
|
||||
"discourse_automation.scriptables.append_last_edited_by.text",
|
||||
username: moderator.username,
|
||||
date_time:
|
||||
"[date=#{updated_at.to_date} time=#{updated_at.strftime("%H:%M:%S")} timezone=UTC]",
|
||||
),
|
||||
),
|
||||
),
|
||||
).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
208
plugins/automation/spec/scripts/auto_responder_spec.rb
Normal file
208
plugins/automation/spec/scripts/auto_responder_spec.rb
Normal file
@ -0,0 +1,208 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "AutoResponder" do
|
||||
fab!(:topic)
|
||||
|
||||
fab!(:automation) { Fabricate(:automation, script: DiscourseAutomation::Scripts::AUTO_RESPONDER) }
|
||||
|
||||
context "without word filter" do
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"word_answer_list",
|
||||
"key-value",
|
||||
{ value: [{ key: "", value: "this is the reply" }].to_json },
|
||||
)
|
||||
end
|
||||
|
||||
it "creates an answer" do
|
||||
post = create_post(topic: topic, raw: "this is a post")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.posts.last.raw).to eq("this is the reply")
|
||||
end
|
||||
end
|
||||
|
||||
context "with present word_answer list" do
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"word_answer_list",
|
||||
"key-value",
|
||||
{
|
||||
value: [
|
||||
{ key: "fooz?|bar", value: "this is {{key}}" },
|
||||
{ key: "bar", value: "this is {{key}}" },
|
||||
].to_json,
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
context "when post is first post" do
|
||||
context "when topic title contains keywords" do
|
||||
it "creates an answer" do
|
||||
topic = Fabricate(:topic, title: "What a foo day to walk")
|
||||
post = create_post(topic: topic, raw: "this is a post with no keyword")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.posts.last.raw).to eq("this is foo")
|
||||
end
|
||||
end
|
||||
|
||||
context "when post and topic title contain keyword" do
|
||||
it "creates only one answer" do
|
||||
topic = Fabricate(:topic, title: "What a foo day to walk")
|
||||
post = create_post(topic: topic, raw: "this is a post with foo keyword")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.posts.last.raw).to eq("this is foo")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the word answer list has a wildcard (empty string) for key" do
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"word_answer_list",
|
||||
"key-value",
|
||||
{ value: [{ key: "", value: "this is a response" }].to_json },
|
||||
)
|
||||
end
|
||||
|
||||
it "creates an answer" do
|
||||
topic = Fabricate(:topic, title: "What a foo day to walk")
|
||||
post = create_post(topic: topic, raw: "this is a post with no keyword")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.posts.last.raw).to eq("this is a response")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when post contains a keyword" do
|
||||
it "creates an answer" do
|
||||
post = create_post(topic: topic, raw: "this is foo a post with foo")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.posts.last.raw).to eq("this is foo")
|
||||
end
|
||||
|
||||
context "when post has direct replies from answering user" do
|
||||
fab!(:answering_user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"answering_user",
|
||||
"user",
|
||||
{ value: answering_user.username },
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
it "doesn’t create another answer" do
|
||||
post_1 = create_post(topic: topic, raw: "this is a post with foo")
|
||||
create_post(user: answering_user, reply_to_post_number: post_1.post_number, topic: topic)
|
||||
|
||||
expect { automation.trigger!("post" => post_1) }.not_to change { Post.count }
|
||||
end
|
||||
end
|
||||
|
||||
context "when user is replying to own post" do
|
||||
fab!(:answering_user) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"answering_user",
|
||||
"user",
|
||||
{ value: answering_user.username },
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
it "doesn’t create an answer" do
|
||||
post_1 = create_post(topic: topic)
|
||||
post_2 =
|
||||
create_post(
|
||||
user: answering_user,
|
||||
topic: topic,
|
||||
reply_to_post_number: post_1.post_number,
|
||||
raw: "this is a post with foo",
|
||||
)
|
||||
|
||||
expect { automation.trigger!("post" => post_2) }.not_to change { Post.count }
|
||||
end
|
||||
end
|
||||
|
||||
context "when once is used" do
|
||||
before { automation.upsert_field!("once", "boolean", { value: true }, target: "script") }
|
||||
|
||||
it "allows only one response by automation" do
|
||||
post = create_post(topic: topic, raw: "this is a post with foo and bar")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(post.topic.reload.posts_count).to eq(2)
|
||||
|
||||
post = create_post(topic: topic, raw: "this is another post with foo and bar")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(post.topic.reload.posts_count).to eq(3)
|
||||
|
||||
another_automation =
|
||||
Fabricate(:automation, script: DiscourseAutomation::Scripts::AUTO_RESPONDER)
|
||||
another_automation.upsert_field!("once", "boolean", { value: true }, target: "script")
|
||||
another_automation.upsert_field!(
|
||||
"word_answer_list",
|
||||
"key-value",
|
||||
{ value: [{ key: "", value: "this is the reply" }].to_json },
|
||||
)
|
||||
post = create_post(topic: topic, raw: "this is the last post with foo and bar")
|
||||
another_automation.trigger!("post" => post)
|
||||
|
||||
expect(post.topic.reload.posts_count).to eq(5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when post contains two keywords" do
|
||||
it "creates an answer with both answers" do
|
||||
post = create_post(topic: topic, raw: "this is a post with FOO and bar")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.posts.last.raw).to eq("this is FOO\n\nthis is bar")
|
||||
end
|
||||
end
|
||||
|
||||
context "when post doesn’t contain a keyword" do
|
||||
it "doesn’t create an answer" do
|
||||
post = create_post(topic: topic, raw: "this is a post with no keyword")
|
||||
|
||||
expect { automation.trigger!("post" => post) }.not_to change { Post.count }
|
||||
end
|
||||
end
|
||||
|
||||
context "when post contains two keywords" do
|
||||
it "creates an answer with both answers" do
|
||||
post = create_post(topic: topic, raw: "this is a post with foo and bar")
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.posts.last.raw).to eq("this is foo\n\nthis is bar")
|
||||
end
|
||||
end
|
||||
|
||||
context "when post doesn’t contain a keyword" do
|
||||
it "doesn’t create an answer" do
|
||||
post = create_post(topic: topic, raw: "this is a post bfoo with no keyword fooa")
|
||||
|
||||
expect { automation.trigger!("post" => post) }.not_to change { Post.count }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when word_answer list is empty" do
|
||||
it "exits early with no error" do
|
||||
expect {
|
||||
post = create_post(topic: topic, raw: "this is a post with foo and bar")
|
||||
automation.trigger!("post" => post)
|
||||
}.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
41
plugins/automation/spec/scripts/auto_tag_topic_spec.rb
Normal file
41
plugins/automation/spec/scripts/auto_tag_topic_spec.rb
Normal file
@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "AutoTagTopic" do
|
||||
fab!(:topic)
|
||||
fab!(:tag1) { Fabricate(:tag, name: "tag1") }
|
||||
fab!(:tag2) { Fabricate(:tag, name: "tag2") }
|
||||
fab!(:tag3) { Fabricate(:tag, name: "tag3") }
|
||||
fab!(:admin) { Fabricate(:admin, refresh_auto_groups: true) }
|
||||
|
||||
fab!(:automation) { Fabricate(:automation, script: DiscourseAutomation::Scripts::AUTO_TAG_TOPIC) }
|
||||
|
||||
context "when tags list is empty" do
|
||||
it "exits early with no error" do
|
||||
expect {
|
||||
post = create_post(topic: topic)
|
||||
automation.trigger!("post" => post)
|
||||
}.to_not raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are tags" do
|
||||
before { automation.upsert_field!("tags", "tags", { value: %w[tag1 tag2] }) }
|
||||
|
||||
it "works" do
|
||||
post = create_post(topic: topic)
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.tags.pluck(:name)).to match_array(%w[tag1 tag2])
|
||||
end
|
||||
|
||||
it "does not remove existing tags" do
|
||||
post = create_post(topic: topic, tags: %w[totally])
|
||||
DiscourseTagging.tag_topic_by_names(topic, Guardian.new(admin), ["tag3"])
|
||||
automation.trigger!("post" => post)
|
||||
|
||||
expect(topic.reload.tags.pluck(:name).sort).to match_array(%w[tag1 tag2 tag3])
|
||||
end
|
||||
end
|
||||
end
|
42
plugins/automation/spec/scripts/banner_topic_spec.rb
Normal file
42
plugins/automation/spec/scripts/banner_topic_spec.rb
Normal file
@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "BannerTopic" do
|
||||
before { automation.upsert_field!("topic_id", "text", { value: topic.id }) }
|
||||
|
||||
fab!(:automation) { Fabricate(:automation, script: DiscourseAutomation::Scripts::BANNER_TOPIC) }
|
||||
fab!(:topic)
|
||||
|
||||
context "when banner until is set" do
|
||||
before do
|
||||
freeze_time
|
||||
automation.upsert_field!("banner_until", "date_time", { value: 10.days.from_now })
|
||||
automation.upsert_field!("topic_id", "text", { value: topic.id })
|
||||
end
|
||||
|
||||
it "banners the topic" do
|
||||
expect(topic.bannered_until).to be_nil
|
||||
expect(topic.archetype).to eq(Archetype.default)
|
||||
|
||||
automation.trigger!
|
||||
topic.reload
|
||||
|
||||
expect(topic.bannered_until).to be_within_one_minute_of(10.days.from_now)
|
||||
expect(topic.archetype).to eq(Archetype.banner)
|
||||
end
|
||||
end
|
||||
|
||||
context "when banner until is not set" do
|
||||
it "banners the topic" do
|
||||
expect(topic.bannered_until).to be_nil
|
||||
expect(topic.archetype).to eq(Archetype.default)
|
||||
|
||||
automation.trigger!
|
||||
topic.reload
|
||||
|
||||
expect(topic.bannered_until).to be_nil
|
||||
expect(topic.archetype).to eq(Archetype.banner)
|
||||
end
|
||||
end
|
||||
end
|
85
plugins/automation/spec/scripts/close_topic_spec.rb
Normal file
85
plugins/automation/spec/scripts/close_topic_spec.rb
Normal file
@ -0,0 +1,85 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "CloseTopic" do
|
||||
fab!(:user)
|
||||
fab!(:category) { Fabricate(:category, user: user) }
|
||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||
fab!(:automation) { Fabricate(:automation, script: DiscourseAutomation::Scripts::CLOSE_TOPIC) }
|
||||
|
||||
before { automation.upsert_field!("topic", "text", { value: topic.id }, target: "script") }
|
||||
|
||||
context "with default params" do
|
||||
it "works" do
|
||||
expect(topic.closed).to be_falsey
|
||||
|
||||
automation.trigger!
|
||||
topic.reload
|
||||
|
||||
expect(topic.closed).to be_truthy
|
||||
|
||||
closing_post = topic.posts.where(action_code: "closed.enabled").last
|
||||
expect(closing_post.raw).to eq("")
|
||||
expect(closing_post.user_id).to eq(-1)
|
||||
end
|
||||
end
|
||||
|
||||
context "with message" do
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"message",
|
||||
"text",
|
||||
{ value: "dingity dongity dong, this topic is closed!" },
|
||||
)
|
||||
end
|
||||
|
||||
it "works" do
|
||||
expect(topic.closed).to be_falsey
|
||||
|
||||
automation.trigger!
|
||||
topic.reload
|
||||
|
||||
expect(topic.closed).to be_truthy
|
||||
|
||||
closing_post = topic.posts.where(action_code: "closed.enabled").last
|
||||
expect(closing_post.raw).to eq("dingity dongity dong, this topic is closed!")
|
||||
end
|
||||
end
|
||||
|
||||
# NOTE: this is only possible because we skip validations for now.
|
||||
# As soon as discourse-automation supports proper error handling and validations take place again,
|
||||
# this test should be removed.
|
||||
context "with very short message" do
|
||||
before { automation.upsert_field!("message", "text", { value: "bye" }) }
|
||||
|
||||
it "closes the topic" do
|
||||
expect(topic.closed).to be_falsey
|
||||
|
||||
automation.trigger!
|
||||
topic.reload
|
||||
|
||||
expect(topic.closed).to be_truthy
|
||||
|
||||
closing_post = topic.posts.where(action_code: "closed.enabled").last
|
||||
expect(closing_post.raw).to eq("bye")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a specific user" do
|
||||
fab!(:specific_user) { Fabricate(:user, admin: true) }
|
||||
|
||||
before { automation.upsert_field!("user", "user", { value: specific_user.username }) }
|
||||
|
||||
it "closes the topic" do
|
||||
expect(topic.closed).to be_falsey
|
||||
|
||||
automation.trigger!
|
||||
topic.reload
|
||||
|
||||
expect(topic.closed).to be_truthy
|
||||
closing_post = topic.posts.where(action_code: "closed.enabled").last
|
||||
expect(closing_post.user_id).to eq(specific_user.id)
|
||||
end
|
||||
end
|
||||
end
|
84
plugins/automation/spec/scripts/flag_post_on_words_spec.rb
Normal file
84
plugins/automation/spec/scripts/flag_post_on_words_spec.rb
Normal file
@ -0,0 +1,84 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "FlagPostsOnWords" do
|
||||
fab!(:user)
|
||||
fab!(:category) { Fabricate(:category, user: user) }
|
||||
fab!(:topic) { Fabricate(:topic, category_id: category.id) }
|
||||
fab!(:automation) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::FLAG_POST_ON_WORDS,
|
||||
trigger: DiscourseAutomation::Triggers::POST_CREATED_EDITED,
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
SiteSetting.discourse_automation_enabled = true
|
||||
automation.fields.create!(
|
||||
component: "text_list",
|
||||
name: "words",
|
||||
metadata: {
|
||||
value: ["foo,bar"],
|
||||
},
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
context "when editing/creating a post" do
|
||||
context "when editing a post" do
|
||||
fab!(:post)
|
||||
|
||||
context "when post has flagged words" do
|
||||
it "flags the post" do
|
||||
post.revise(post.user, raw: "this is another cool topic foo bar")
|
||||
expect(post.reviewable_flag).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context "when post has no/not all flagged words" do
|
||||
it "doesn’t flag the post" do
|
||||
post.revise(post.user, raw: "this is another cool topic")
|
||||
expect(post.reviewable_flag).to_not be_present
|
||||
|
||||
post.revise(post.user, raw: "this is another cool bar topic")
|
||||
expect(post.reviewable_flag).to_not be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when creating a post" do
|
||||
context "when post has flagged words" do
|
||||
it "flags the post" do
|
||||
post_creator =
|
||||
PostCreator.new(
|
||||
user,
|
||||
topic_id: topic.id,
|
||||
raw: "this is quite bar cool a very cool foo post",
|
||||
)
|
||||
post = post_creator.create
|
||||
expect(post.reviewable_flag).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context "when post has no/not all flagged words" do
|
||||
it "doesn’t flag the post" do
|
||||
post_creator =
|
||||
PostCreator.new(user, topic_id: topic.id, raw: "this is quite cool a very cool post")
|
||||
post = post_creator.create
|
||||
expect(post.reviewable_flag).to_not be_present
|
||||
|
||||
post_creator =
|
||||
PostCreator.new(
|
||||
user,
|
||||
topic_id: topic.id,
|
||||
raw: "this is quite cool a foo very cool post",
|
||||
)
|
||||
post = post_creator.create
|
||||
expect(post.reviewable_flag).to_not be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
75
plugins/automation/spec/scripts/gift_exchange_spec.rb
Normal file
75
plugins/automation/spec/scripts/gift_exchange_spec.rb
Normal file
@ -0,0 +1,75 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "GiftExchange" do
|
||||
fab!(:automation) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::GIFT_EXCHANGE,
|
||||
trigger: DiscourseAutomation::Triggers::POINT_IN_TIME,
|
||||
)
|
||||
end
|
||||
fab!(:gift_group) { Fabricate(:group) }
|
||||
fab!(:user_1) { Fabricate(:user) }
|
||||
fab!(:user_2) { Fabricate(:user) }
|
||||
fab!(:user_3) { Fabricate(:user) }
|
||||
|
||||
before do
|
||||
SiteSetting.discourse_automation_enabled = true
|
||||
|
||||
gift_group.add(user_1)
|
||||
gift_group.add(user_2)
|
||||
gift_group.add(user_3)
|
||||
|
||||
automation.upsert_field!(
|
||||
"gift_exchangers_group",
|
||||
"group",
|
||||
{ value: gift_group.id },
|
||||
target: "script",
|
||||
)
|
||||
automation.upsert_field!(
|
||||
"giftee_assignment_messages",
|
||||
"pms",
|
||||
{
|
||||
value: [
|
||||
{
|
||||
title: "Gift {{year}}",
|
||||
raw: "@{{gifter_username}} you should send a gift to {{giftee_username}}",
|
||||
},
|
||||
],
|
||||
},
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
context "when run from point_in_time trigger" do
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"execute_at",
|
||||
"date_time",
|
||||
{ value: 3.hours.from_now },
|
||||
target: "trigger",
|
||||
)
|
||||
end
|
||||
|
||||
it "creates expected PM" do
|
||||
freeze_time 6.hours.from_now do
|
||||
expect {
|
||||
Jobs::DiscourseAutomationTracker.new.execute
|
||||
|
||||
raws = Post.order(created_at: :desc).limit(3).pluck(:raw)
|
||||
expect(raws.any? { |r| r.start_with?("@#{user_1.username}") }).to be_truthy
|
||||
expect(raws.any? { |r| r.start_with?("@#{user_2.username}") }).to be_truthy
|
||||
expect(raws.any? { |r| r.start_with?("@#{user_3.username}") }).to be_truthy
|
||||
expect(raws.any? { |r| r.end_with?("#{user_1.username}") }).to be_truthy
|
||||
expect(raws.any? { |r| r.end_with?("#{user_2.username}") }).to be_truthy
|
||||
expect(raws.any? { |r| r.end_with?("#{user_3.username}") }).to be_truthy
|
||||
|
||||
title = Post.order(created_at: :desc).limit(3).map { |post| post.topic.title }.uniq.first
|
||||
expect(title).to eq("Gift #{Time.zone.now.year}")
|
||||
}.to change { Post.count }.by(3) # each pair receives a PM
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "GroupCategoryNotificationDefault" do
|
||||
fab!(:category)
|
||||
fab!(:group)
|
||||
|
||||
before { SiteSetting.discourse_automation_enabled = true }
|
||||
|
||||
context "when using category_created_edited trigger" do
|
||||
fab!(:automation) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::GROUP_CATEGORY_NOTIFICATION_DEFAULT,
|
||||
trigger: DiscourseAutomation::Triggers::CATEGORY_CREATED_EDITED,
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"restricted_category",
|
||||
"category",
|
||||
{ value: category.id },
|
||||
target: "trigger",
|
||||
)
|
||||
automation.upsert_field!("group", "group", { value: group.id }, target: "script")
|
||||
automation.upsert_field!(
|
||||
"notification_level",
|
||||
"category_notification_level",
|
||||
{ value: 4 },
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
context "when category is allowed" do
|
||||
it "creates a GroupCategoryNotificationDefault record" do
|
||||
subcategory = nil
|
||||
expect { subcategory = Fabricate(:category, parent_category_id: category.id) }.to change {
|
||||
GroupCategoryNotificationDefault.count
|
||||
}.by(1)
|
||||
|
||||
record = GroupCategoryNotificationDefault.last
|
||||
expect(record.category_id).to eq(subcategory.id)
|
||||
expect(record.group_id).to eq(group.id)
|
||||
expect(record.notification_level).to eq(4)
|
||||
end
|
||||
|
||||
it "updates category notification level for existing members" do
|
||||
automation.upsert_field!(
|
||||
"update_existing_members",
|
||||
"boolean",
|
||||
{ value: true },
|
||||
target: "script",
|
||||
)
|
||||
user = Fabricate(:user)
|
||||
group.add(user)
|
||||
subcategory = nil
|
||||
|
||||
expect { subcategory = Fabricate(:category, parent_category_id: category.id) }.to change {
|
||||
CategoryUser.count
|
||||
}.by(1)
|
||||
|
||||
record = CategoryUser.last
|
||||
expect(record.category_id).to eq(subcategory.id)
|
||||
expect(record.user_id).to eq(user.id)
|
||||
expect(record.notification_level).to eq(4)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
66
plugins/automation/spec/scripts/pin_topic_spec.rb
Normal file
66
plugins/automation/spec/scripts/pin_topic_spec.rb
Normal file
@ -0,0 +1,66 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "PinTopic" do
|
||||
fab!(:user)
|
||||
fab!(:category) { Fabricate(:category, user: user) }
|
||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||
fab!(:automation) { Fabricate(:automation, script: DiscourseAutomation::Scripts::PIN_TOPIC) }
|
||||
|
||||
before do
|
||||
automation.upsert_field!("pinnable_topic", "text", { value: topic.id }, target: "script")
|
||||
end
|
||||
|
||||
context "when not pinned globally" do
|
||||
it "works" do
|
||||
expect(topic.pinned_at).to be_nil
|
||||
|
||||
automation.trigger!
|
||||
topic.reload
|
||||
|
||||
expect(topic.pinned_at).to be_within_one_minute_of(Time.zone.now)
|
||||
expect(topic.pinned_globally).to be_falsey
|
||||
expect(topic.pinned_until).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when pinned globally" do
|
||||
before { automation.upsert_field!("pinned_globally", "boolean", { value: true }) }
|
||||
|
||||
it "works" do
|
||||
expect(topic.pinned_at).to be_nil
|
||||
|
||||
automation.trigger!
|
||||
topic.reload
|
||||
|
||||
expect(topic.pinned_at).to be_within_one_minute_of(Time.zone.now)
|
||||
expect(topic.pinned_globally).to be_truthy
|
||||
expect(topic.pinned_until).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "pinned until" do
|
||||
before do
|
||||
freeze_time
|
||||
automation.upsert_field!("pinned_until", "date_time", { value: 10.days.from_now })
|
||||
end
|
||||
|
||||
it "works" do
|
||||
expect(topic.pinned_at).to be_nil
|
||||
|
||||
automation.trigger!
|
||||
|
||||
# expect_enqueued_with is sometimes failing with float precision
|
||||
job = Jobs::UnpinTopic.jobs.last
|
||||
expect(job["args"][0]["topic_id"]).to eq(topic.id)
|
||||
expect(Time.at(job["at"])).to be_within_one_minute_of(10.days.from_now)
|
||||
|
||||
topic.reload
|
||||
|
||||
expect(topic.pinned_at).to be_within_one_minute_of(Time.zone.now)
|
||||
expect(topic.pinned_globally).to be_falsey
|
||||
expect(topic.pinned_until).to be_within_one_minute_of(10.days.from_now)
|
||||
end
|
||||
end
|
||||
end
|
154
plugins/automation/spec/scripts/post_spec.rb
Normal file
154
plugins/automation/spec/scripts/post_spec.rb
Normal file
@ -0,0 +1,154 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "Post" do
|
||||
fab!(:topic_1) { Fabricate(:topic) }
|
||||
let!(:raw) { "this is me testing a post" }
|
||||
|
||||
before { SiteSetting.discourse_automation_enabled = true }
|
||||
|
||||
context "when using point_in_time trigger" do
|
||||
fab!(:automation) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::POST,
|
||||
trigger: DiscourseAutomation::Triggers::POINT_IN_TIME,
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"execute_at",
|
||||
"date_time",
|
||||
{ value: 3.hours.from_now },
|
||||
target: "trigger",
|
||||
)
|
||||
automation.upsert_field!("topic", "text", { value: topic_1.id }, target: "script")
|
||||
automation.upsert_field!("post", "post", { value: raw }, target: "script")
|
||||
end
|
||||
|
||||
it "creates expected post" do
|
||||
freeze_time 6.hours.from_now do
|
||||
expect {
|
||||
Jobs::DiscourseAutomationTracker.new.execute
|
||||
|
||||
expect(topic_1.posts.last.raw).to eq(raw)
|
||||
}.to change { topic_1.posts.count }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when topic is deleted" do
|
||||
before { topic_1.trash! }
|
||||
|
||||
it "does nothing and does not error" do
|
||||
freeze_time 6.hours.from_now do
|
||||
expect { Jobs::DiscourseAutomationTracker.new.execute }.not_to change { Post.count }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when using recurring trigger" do
|
||||
fab!(:automation) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::POST,
|
||||
trigger: DiscourseAutomation::Triggers::RECURRING,
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
automation.upsert_field!("topic", "text", { value: topic_1.id }, target: "script")
|
||||
automation.upsert_field!("post", "post", { value: raw }, target: "script")
|
||||
end
|
||||
|
||||
it "creates expected post" do
|
||||
expect {
|
||||
automation.trigger!
|
||||
|
||||
expect(topic_1.posts.last.raw).to eq(raw)
|
||||
}.to change { topic_1.posts.count }.by(1)
|
||||
end
|
||||
|
||||
it "does not create post on a closed topic" do
|
||||
topic_1.update_status(:closed, true, topic_1.user)
|
||||
|
||||
expect { automation.trigger! }.not_to change { topic_1.posts.count }
|
||||
end
|
||||
|
||||
it "does not create post on an archived topic" do
|
||||
topic_1.update_status(:archived, true, topic_1.user)
|
||||
|
||||
expect { automation.trigger! }.not_to change { topic_1.posts.count }
|
||||
end
|
||||
end
|
||||
|
||||
context "when using user_updated trigger" do
|
||||
fab!(:user_field_1) { Fabricate(:user_field, name: "custom field 1") }
|
||||
fab!(:user_field_2) { Fabricate(:user_field, name: "custom field 2") }
|
||||
|
||||
fab!(:user) do
|
||||
user = Fabricate(:user, trust_level: TrustLevel[0])
|
||||
user.set_user_field(user_field_1.id, "Answer custom 1")
|
||||
user.set_user_field(user_field_2.id, "Answer custom 2")
|
||||
user.user_profile.location = "Japan"
|
||||
user.user_profile.save
|
||||
user.save
|
||||
user
|
||||
end
|
||||
|
||||
fab!(:automation) do
|
||||
automation =
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::POST,
|
||||
trigger: DiscourseAutomation::Triggers::USER_UPDATED,
|
||||
)
|
||||
automation.upsert_field!(
|
||||
"custom_fields",
|
||||
"custom_fields",
|
||||
{ value: ["custom field 1", "custom field 2"] },
|
||||
target: "trigger",
|
||||
)
|
||||
automation.upsert_field!(
|
||||
"user_profile",
|
||||
"user_profile",
|
||||
{ value: ["location"] },
|
||||
target: "trigger",
|
||||
)
|
||||
automation.upsert_field!("first_post_only", "boolean", { value: true }, target: "trigger")
|
||||
automation
|
||||
end
|
||||
let!(:user_raw_post) do
|
||||
"This is a raw test post for user custom field 1: {{custom_field_1}}, custom field 2: {{custom_field_2}} and location: {{location}}"
|
||||
end
|
||||
let!(:placeholder_applied_user_raw_post) do
|
||||
"This is a raw test post for user custom field 1: #{user.custom_fields["user_field_#{user_field_1.id}"]}, custom field 2: #{user.custom_fields["user_field_#{user_field_2.id}"]} and location: #{user.user_profile.location}"
|
||||
end
|
||||
|
||||
before do
|
||||
automation.upsert_field!("topic", "text", { value: topic_1.id }, target: "script")
|
||||
automation.upsert_field!("post", "post", { value: user_raw_post }, target: "script")
|
||||
end
|
||||
|
||||
it "Creates a post correctly" do
|
||||
expect {
|
||||
UserUpdater.new(user, user).update(location: "Japan")
|
||||
expect(topic_1.posts.last.raw).to eq(placeholder_applied_user_raw_post)
|
||||
}.to change { topic_1.posts.count }.by(1)
|
||||
end
|
||||
|
||||
context "when creator is one of accepted context" do
|
||||
before do
|
||||
automation.upsert_field!("creator", "user", { value: "updated_user" }, target: "script")
|
||||
end
|
||||
|
||||
it "sets the creator to the post creator" do
|
||||
expect { UserUpdater.new(user, user).update(location: "Japan") }.to change {
|
||||
Post.where(user_id: user.id).count
|
||||
}.by(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
137
plugins/automation/spec/scripts/send_pms_spec.rb
Normal file
137
plugins/automation/spec/scripts/send_pms_spec.rb
Normal file
@ -0,0 +1,137 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "SendPms" do
|
||||
fab!(:automation) do
|
||||
Fabricate(:automation, script: DiscourseAutomation::Scripts::SEND_PMS, trigger: "stalled_wiki")
|
||||
end
|
||||
|
||||
before do
|
||||
SiteSetting.discourse_automation_enabled = true
|
||||
|
||||
automation.upsert_field!("sender", "user", { value: Discourse.system_user.username })
|
||||
automation.upsert_field!(
|
||||
"sendable_pms",
|
||||
"pms",
|
||||
{
|
||||
value: [
|
||||
{
|
||||
title: "A message from {{sender_username}}",
|
||||
raw: "This is a message sent to @{{receiver_username}}",
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
context "when run from stalled_wiki trigger" do
|
||||
fab!(:post_creator_1) { Fabricate(:user, admin: true) }
|
||||
fab!(:post_1) { Fabricate(:post, user: post_creator_1) }
|
||||
|
||||
before do
|
||||
automation.upsert_field!("stalled_after", "choices", { value: "PT1H" }, target: "trigger")
|
||||
automation.upsert_field!("retriggered_after", "choices", { value: "PT1H" }, target: "trigger")
|
||||
|
||||
post_1.revise(
|
||||
post_creator_1,
|
||||
{ wiki: true },
|
||||
{ force_new_version: true, revised_at: 2.hours.ago },
|
||||
)
|
||||
end
|
||||
|
||||
it "creates expected PM" do
|
||||
expect {
|
||||
Jobs::StalledWikiTracker.new.execute(nil)
|
||||
|
||||
post = Post.last
|
||||
expect(post.topic.title).to eq("A message from #{Discourse.system_user.username}")
|
||||
expect(post.raw).to eq("This is a message sent to @#{post_creator_1.username}")
|
||||
expect(post.topic.topic_allowed_users.exists?(user_id: post_creator_1.id)).to eq(true)
|
||||
expect(post.topic.topic_allowed_users.exists?(user_id: Discourse.system_user.id)).to eq(
|
||||
true,
|
||||
)
|
||||
}.to change { Post.count }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when run from user_added_to_group trigger" do
|
||||
fab!(:user_1) { Fabricate(:user) }
|
||||
fab!(:tracked_group_1) { Fabricate(:group) }
|
||||
|
||||
before do
|
||||
automation.update!(trigger: "user_added_to_group")
|
||||
automation.upsert_field!(
|
||||
"joined_group",
|
||||
"group",
|
||||
{ value: tracked_group_1.id },
|
||||
target: "trigger",
|
||||
)
|
||||
end
|
||||
|
||||
it "creates expected PM" do
|
||||
expect {
|
||||
tracked_group_1.add(user_1)
|
||||
|
||||
post = Post.last
|
||||
expect(post.topic.title).to eq("A message from #{Discourse.system_user.username}")
|
||||
expect(post.raw).to eq("This is a message sent to @#{user_1.username}")
|
||||
expect(post.topic.topic_allowed_users.exists?(user_id: user_1.id)).to eq(true)
|
||||
expect(post.topic.topic_allowed_users.exists?(user_id: Discourse.system_user.id)).to eq(
|
||||
true,
|
||||
)
|
||||
}.to change { Post.count }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when delayed" do
|
||||
fab!(:user_1) { Fabricate(:user) }
|
||||
|
||||
before { automation.update!(trigger: DiscourseAutomation::Triggers::RECURRING) }
|
||||
|
||||
it "correctly sets encrypt preference to false even when option is not specified" do
|
||||
automation.upsert_field!(
|
||||
"sendable_pms",
|
||||
"pms",
|
||||
{
|
||||
value: [
|
||||
{
|
||||
title: "A message from {{sender_username}}",
|
||||
raw: "This is a message sent to @{{receiver_username}}",
|
||||
delay: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
target: "script",
|
||||
)
|
||||
automation.upsert_field!("receiver", "user", { value: Discourse.system_user.username })
|
||||
|
||||
automation.trigger!
|
||||
|
||||
expect(DiscourseAutomation::PendingPm.last.prefers_encrypt).to eq(false)
|
||||
end
|
||||
|
||||
it "correctly stores encrypt preference to false" do
|
||||
automation.upsert_field!(
|
||||
"sendable_pms",
|
||||
"pms",
|
||||
{
|
||||
value: [
|
||||
{
|
||||
title: "A message from {{sender_username}}",
|
||||
raw: "This is a message sent to @{{receiver_username}}",
|
||||
delay: 1,
|
||||
prefers_encrypt: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
target: "script",
|
||||
)
|
||||
automation.upsert_field!("receiver", "user", { value: Discourse.system_user.username })
|
||||
|
||||
automation.trigger!
|
||||
|
||||
expect(DiscourseAutomation::PendingPm.last.prefers_encrypt).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "SuspendUserByEmail" do
|
||||
let(:suspend_until) { 10.days.from_now }
|
||||
let(:reason) { "banned for spam" }
|
||||
|
||||
fab!(:automation) do
|
||||
Fabricate(:automation, script: DiscourseAutomation::Scripts::SUSPEND_USER_BY_EMAIL)
|
||||
end
|
||||
fab!(:user)
|
||||
|
||||
before do
|
||||
automation.upsert_field!("suspend_until", "date_time", { value: suspend_until })
|
||||
automation.upsert_field!("reason", "text", { value: reason })
|
||||
end
|
||||
|
||||
describe "using fields" do
|
||||
it "suspends the user" do
|
||||
expect(user.suspended?).to be(false)
|
||||
|
||||
expect { automation.trigger!("email" => user.email) }.to change { UserHistory.count }.by(1)
|
||||
|
||||
user.reload
|
||||
|
||||
expect(user.suspended?).to be(true)
|
||||
expect(user.suspended_till).to be_within_one_minute_of(suspend_until)
|
||||
|
||||
user_history = UserHistory.last
|
||||
expect(user_history.details).to eq(reason)
|
||||
expect(user_history.acting_user_id).to eq(Discourse.system_user.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "trigger override" do
|
||||
let(:reason) { "very bad behavior" }
|
||||
let(:suspend_until) { 20.days.from_now }
|
||||
|
||||
it "suspends the user" do
|
||||
expect(user.suspended?).to be(false)
|
||||
|
||||
expect {
|
||||
automation.trigger!(
|
||||
"email" => user.email,
|
||||
"reason" => reason,
|
||||
"suspend_until" => suspend_until,
|
||||
)
|
||||
}.to change { UserHistory.count }.by(1)
|
||||
|
||||
user.reload
|
||||
|
||||
expect(user.suspended?).to be(true)
|
||||
expect(user.suspended_till).to be_within_one_minute_of(suspend_until)
|
||||
expect(UserHistory.last.details).to eq(reason)
|
||||
end
|
||||
end
|
||||
end
|
74
plugins/automation/spec/scripts/topic_required_words_spec.rb
Normal file
74
plugins/automation/spec/scripts/topic_required_words_spec.rb
Normal file
@ -0,0 +1,74 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "TopicRequiredWords" do
|
||||
fab!(:user)
|
||||
fab!(:category) { Fabricate(:category, user: user) }
|
||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||
fab!(:automation) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::TOPIC_REQUIRED_WORDS,
|
||||
trigger: DiscourseAutomation::Triggers::TOPIC,
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
SiteSetting.discourse_automation_enabled = true
|
||||
topic.upsert_custom_fields(discourse_automation_ids: automation.id)
|
||||
automation.upsert_field!("words", "text_list", { value: %w[#foo #bar] })
|
||||
end
|
||||
|
||||
describe "editing/creating a post" do
|
||||
before do
|
||||
automation.upsert_field!("restricted_topic", "text", { value: topic.id }, target: "trigger")
|
||||
end
|
||||
|
||||
context "when topic has a topic_required_words automation associated" do
|
||||
context "when post has at least a required word" do
|
||||
it "validates the post" do
|
||||
post_creator = PostCreator.new(user, topic_id: topic.id, raw: "this is quite cool #foo")
|
||||
post = post_creator.create
|
||||
expect(post.valid?).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "when post has no required word" do
|
||||
it "doesn’t validate the post" do
|
||||
post_creator = PostCreator.new(user, topic_id: topic.id, raw: "this is quite cool")
|
||||
post = post_creator.create
|
||||
expect(post.valid?).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when topic has no topic_required_words automation associated" do
|
||||
context "when post has no required word" do
|
||||
it "validates the post" do
|
||||
no_automation_topic = create_topic(category: category)
|
||||
post_creator =
|
||||
PostCreator.new(user, topic_id: no_automation_topic.id, raw: "this is quite cool")
|
||||
post = post_creator.create
|
||||
expect(post.valid?).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "moving posts" do
|
||||
fab!(:admin)
|
||||
fab!(:first_post) { Fabricate(:post, topic: topic, raw: "this is quite cool #foo") }
|
||||
fab!(:post) { Fabricate(:post, topic: topic, raw: "this is quite cool #bar") }
|
||||
fab!(:destination_topic) { Fabricate(:post).topic }
|
||||
|
||||
it "works" do
|
||||
expect do
|
||||
topic.move_posts(admin, [post.id], destination_topic_id: destination_topic.id)
|
||||
end.to_not raise_error
|
||||
|
||||
expect(topic.posts.where(post_type: Post.types[:regular]).size).to eq(1)
|
||||
expect(destination_topic.posts.where(post_type: Post.types[:regular]).size).to eq(2)
|
||||
end
|
||||
end
|
||||
end
|
129
plugins/automation/spec/scripts/user_global_notice_spec.rb
Normal file
129
plugins/automation/spec/scripts/user_global_notice_spec.rb
Normal file
@ -0,0 +1,129 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "UserGlobalNotice" do
|
||||
before { SiteSetting.discourse_automation_enabled = true }
|
||||
|
||||
context "when triggered by a stalled topic" do
|
||||
fab!(:automation_1) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::USER_GLOBAL_NOTICE,
|
||||
trigger: "stalled_topic",
|
||||
)
|
||||
end
|
||||
|
||||
fab!(:topic_1) { Fabricate(:topic) }
|
||||
|
||||
before do
|
||||
automation_1.upsert_field!("stalled_after", "choices", { value: "PT1H" }, target: "trigger")
|
||||
automation_1.upsert_field!("notice", "message", { value: "foo bar" }, target: "script")
|
||||
automation_1.upsert_field!("level", "choices", { value: "error" }, target: "script")
|
||||
end
|
||||
|
||||
describe "script" do
|
||||
describe "StalledTopic trigger" do
|
||||
it "creates a notice for the topic owner" do
|
||||
expect do
|
||||
automation_1.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::STALLED_TOPIC,
|
||||
"topic" => topic_1,
|
||||
)
|
||||
end.to change { DiscourseAutomation::UserGlobalNotice.count }.by(1)
|
||||
|
||||
user_notice = DiscourseAutomation::UserGlobalNotice.last
|
||||
expect(user_notice.user_id).to eq(topic_1.user_id)
|
||||
expect(user_notice.level).to eq("error")
|
||||
expect(user_notice.notice).to eq("foo bar")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "creates and destroy global notices" do
|
||||
post = Fabricate(:post, created_at: 1.day.ago)
|
||||
|
||||
expect { Jobs::StalledTopicTracker.new.execute }.to change {
|
||||
DiscourseAutomation::UserGlobalNotice.count
|
||||
}.by(1)
|
||||
|
||||
expect {
|
||||
PostCreator.create!(post.user, topic_id: post.topic_id, raw: "lorem ipsum dolor sit amet")
|
||||
}.to change { DiscourseAutomation::UserGlobalNotice.count }.by(-1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when triggered by a first accepted solution" do
|
||||
fab!(:automation_1) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::USER_GLOBAL_NOTICE,
|
||||
trigger: "first_accepted_solution",
|
||||
)
|
||||
end
|
||||
|
||||
fab!(:user_1) { Fabricate(:user, username: "user_solved_1") }
|
||||
fab!(:post_1) { Fabricate(:post, user: user_1) }
|
||||
|
||||
before do
|
||||
automation_1.upsert_field!(
|
||||
"notice",
|
||||
"message",
|
||||
{ value: "notice for {{username}}" },
|
||||
target: "script",
|
||||
)
|
||||
automation_1.upsert_field!("level", "choices", { value: "success" }, target: "script")
|
||||
end
|
||||
|
||||
it "creates a notice for the solution author" do
|
||||
expect do
|
||||
automation_1.trigger!(
|
||||
"kind" => "first_accepted_solution",
|
||||
"accepted_post_id" => post_1.id,
|
||||
"usernames" => [post_1.user.username],
|
||||
"placeholders" => {
|
||||
"post_url" => Discourse.base_url + post_1.url,
|
||||
},
|
||||
)
|
||||
end.to change { DiscourseAutomation::UserGlobalNotice.count }.by(1)
|
||||
|
||||
user_notice = DiscourseAutomation::UserGlobalNotice.last
|
||||
expect(user_notice.user_id).to eq(post_1.user.id)
|
||||
expect(user_notice.level).to eq("success")
|
||||
expect(user_notice.notice).to eq("notice for user_solved_1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "on_reset" do
|
||||
fab!(:topic_1) { Fabricate(:topic) }
|
||||
|
||||
fab!(:automation_1) do
|
||||
Fabricate(:automation, script: DiscourseAutomation::Scripts::USER_GLOBAL_NOTICE)
|
||||
end
|
||||
|
||||
fab!(:automation_2) do
|
||||
Fabricate(:automation, script: DiscourseAutomation::Scripts::USER_GLOBAL_NOTICE)
|
||||
end
|
||||
|
||||
before do
|
||||
[automation_1, automation_2].each do |automation|
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::STALLED_TOPIC,
|
||||
"topic" => topic_1,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it "destroys all existing notices" do
|
||||
klass = DiscourseAutomation::UserGlobalNotice
|
||||
|
||||
expect(klass.exists?(identifier: automation_1.id)).to eq(true)
|
||||
expect(klass.exists?(identifier: automation_2.id)).to eq(true)
|
||||
|
||||
automation_1.scriptable.on_reset.call(automation_1)
|
||||
|
||||
expect(klass.exists?(identifier: automation_1.id)).to eq(false)
|
||||
expect(klass.exists?(identifier: automation_2.id)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,390 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "UserGroupMembershipThroughBadge" do
|
||||
fab!(:user)
|
||||
fab!(:other_users) { Fabricate.times(5, :user) }
|
||||
fab!(:badge)
|
||||
fab!(:target_group) { Fabricate(:group, title: "Target Title", flair_icon: "ad") }
|
||||
|
||||
fab!(:automation) do
|
||||
Fabricate(
|
||||
:automation,
|
||||
script: DiscourseAutomation::Scripts::USER_GROUP_MEMBERSHIP_THROUGH_BADGE,
|
||||
)
|
||||
end
|
||||
|
||||
before { BadgeGranter.enable_queue }
|
||||
after do
|
||||
BadgeGranter.disable_queue
|
||||
BadgeGranter.clear_queue!
|
||||
end
|
||||
|
||||
def target_group_member?(user_ids)
|
||||
GroupUser.exists?(group_id: target_group.id, user_id: user_ids)
|
||||
end
|
||||
|
||||
def owns_badge?(user_ids)
|
||||
UserBadge.exists?(user_id: user_ids, badge_id: badge.id)
|
||||
end
|
||||
|
||||
context "with invalid field values" do
|
||||
before do
|
||||
@original_logger = Rails.logger
|
||||
Rails.logger = @fake_logger = FakeLogger.new
|
||||
end
|
||||
|
||||
after { Rails.logger = @original_logger }
|
||||
|
||||
context "with an unknown badge" do
|
||||
let(:unknown_badge_id) { -1 }
|
||||
|
||||
before do
|
||||
automation.upsert_field!("badge", "choices", { value: unknown_badge_id }, target: "script")
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
end
|
||||
|
||||
it "logs warning message and does nothing" do
|
||||
expect(@fake_logger.warnings).to include(
|
||||
"[discourse-automation] Couldn’t find badge with id #{unknown_badge_id}",
|
||||
)
|
||||
expect(user.reload.groups).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "with a non-existent group" do
|
||||
before do
|
||||
automation.upsert_field!("badge", "choices", { value: badge.id }, target: "script")
|
||||
automation.upsert_field!("group", "group", { value: target_group.id }, target: "script")
|
||||
end
|
||||
|
||||
it "logs warning message and does nothing" do
|
||||
target_group.destroy
|
||||
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
|
||||
expect(@fake_logger.warnings).to include(
|
||||
"[discourse-automation] Couldn’t find group with id #{target_group.id}",
|
||||
)
|
||||
expect(user.reload.groups).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with valid field values" do
|
||||
before do
|
||||
automation.upsert_field!("badge", "choices", { value: badge.id }, target: "script")
|
||||
automation.upsert_field!("group", "group", { value: target_group.id }, target: "script")
|
||||
end
|
||||
|
||||
context "when triggered with a user" do
|
||||
context "when user has badge" do
|
||||
before { BadgeGranter.grant(badge, user) }
|
||||
|
||||
it "adds user to group" do
|
||||
expect(target_group_member?([user.id])).to eq(false)
|
||||
|
||||
expect do
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
end.to change { target_group.users.count }.by(1)
|
||||
|
||||
expect(target_group_member?([user.id])).to eq(true)
|
||||
expect(owns_badge?([user.id])).to eq(true)
|
||||
end
|
||||
|
||||
it "does nothing if user is an existing group member" do
|
||||
target_group.add(user)
|
||||
user.reload
|
||||
current_membership = user.group_users.find_by(group_id: target_group.id)
|
||||
|
||||
expect(current_membership).not_to be_nil
|
||||
|
||||
expect do
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
end.not_to change { target_group.reload.users.count }
|
||||
|
||||
expect(GroupUser.find_by(group_id: target_group.id, user_id: user.id)).to eq(
|
||||
current_membership,
|
||||
)
|
||||
expect(owns_badge?([user.id])).to eq(true)
|
||||
end
|
||||
|
||||
it "does not add other badge owners" do
|
||||
other_users.each { |u| BadgeGranter.grant(badge, u) }
|
||||
|
||||
expect(badge.user_badges.count).to eq(6)
|
||||
expect(target_group.users.count).to eq(0)
|
||||
|
||||
expect do
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
end.to change { target_group.reload.users.count }.by(1)
|
||||
|
||||
expect(target_group_member?([user.id])).to eq(true)
|
||||
expect(owns_badge?([user.id])).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user does not have badge" do
|
||||
it "does not add user to group" do
|
||||
expect(target_group_member?([user.id])).to eq(false)
|
||||
expect(owns_badge?([user.id])).to eq(false)
|
||||
|
||||
expect do
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
end.not_to change { target_group.users.count }
|
||||
|
||||
expect(target_group_member?([user.id])).to eq(false)
|
||||
expect(owns_badge?([user.id])).to eq(false)
|
||||
end
|
||||
|
||||
it "does not add other badge owners" do
|
||||
other_users.each { |u| BadgeGranter.grant(badge, u) }
|
||||
|
||||
expect(badge.user_badges.count).to eq(5)
|
||||
expect(target_group.users.count).to eq(0)
|
||||
|
||||
expect do
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
end.not_to change { target_group.reload.users.count }
|
||||
|
||||
expect(target_group_member?([user.id])).to eq(false)
|
||||
expect(owns_badge?([user.id])).to eq(false)
|
||||
expect(target_group.users.count).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when triggered without a user" do
|
||||
let(:badge_owners) { other_users.first(3) }
|
||||
let(:non_badge_owners) { other_users.last(2) }
|
||||
let(:badge_owner_ids) { badge_owners.map(&:id) }
|
||||
let(:non_badge_owner_ids) { non_badge_owners.map(&:id) }
|
||||
|
||||
before { badge_owners.each { |u| BadgeGranter.grant(badge, u) } }
|
||||
|
||||
it "adds all users with badge to group" do
|
||||
expect(target_group_member?(badge_owner_ids)).to eq(false)
|
||||
expect(target_group_member?(non_badge_owner_ids)).to eq(false)
|
||||
|
||||
expect do
|
||||
automation.trigger!("kind" => DiscourseAutomation::Triggers::RECURRING)
|
||||
end.to change { target_group.reload.users.count }.by(badge_owners.size)
|
||||
|
||||
expect(target_group_member?(badge_owner_ids)).to eq(true)
|
||||
expect(target_group_member?(non_badge_owner_ids)).to eq(false)
|
||||
end
|
||||
|
||||
it "skips existing group members with badge" do
|
||||
badge_owners.each { |u| target_group.add(u) }
|
||||
|
||||
expect(target_group_member?(badge_owner_ids)).to eq(true)
|
||||
expect(target_group_member?(non_badge_owner_ids)).to eq(false)
|
||||
|
||||
expect do
|
||||
automation.trigger!("kind" => DiscourseAutomation::Triggers::RECURRING)
|
||||
end.not_to change { target_group.reload.users.count }
|
||||
|
||||
expect(target_group_member?(badge_owner_ids)).to eq(true)
|
||||
expect(target_group_member?(non_badge_owner_ids)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "with remove_members_without_badge = true" do
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"remove_members_without_badge",
|
||||
"boolean",
|
||||
{ value: true },
|
||||
target: "script",
|
||||
)
|
||||
other_users.each { |u| target_group.add(u) }
|
||||
end
|
||||
|
||||
it "removes existing members without badge" do
|
||||
expect(target_group_member?(other_users.map(&:id))).to eq(true)
|
||||
|
||||
expect do
|
||||
automation.trigger!("kind" => DiscourseAutomation::Triggers::RECURRING)
|
||||
end.to change { target_group.reload.users.count }.by(-other_users.count)
|
||||
|
||||
expect(target_group_member?(other_users.map(&:id))).to eq(false)
|
||||
end
|
||||
|
||||
it "keeps existing members with badge" do
|
||||
BadgeGranter.grant(badge, user)
|
||||
target_group.add(user)
|
||||
|
||||
expect(target_group_member?(other_users.map(&:id))).to eq(true)
|
||||
expect(owns_badge?(other_users.map(&:id))).to eq(false)
|
||||
expect(target_group_member?([user.id])).to eq(true)
|
||||
expect(owns_badge?([user.id])).to eq(true)
|
||||
|
||||
expect do
|
||||
automation.trigger!("kind" => DiscourseAutomation::Triggers::RECURRING)
|
||||
end.to change { target_group.reload.users.count }
|
||||
|
||||
expect(target_group_member?(other_users.map(&:id))).to eq(false)
|
||||
expect(owns_badge?(other_users.map(&:id))).to eq(false)
|
||||
expect(target_group_member?([user.id])).to eq(true)
|
||||
expect(owns_badge?([user.id])).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "with remove_members_without_badge = false" do
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"remove_members_without_badge",
|
||||
"boolean",
|
||||
{ value: false },
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
it "keeps existing members without badge" do
|
||||
other_users.each { |u| target_group.add(u) }
|
||||
|
||||
expect(target_group_member?(other_users.map(&:id))).to eq(true)
|
||||
expect(owns_badge?(other_users.map(&:id))).to eq(false)
|
||||
|
||||
expect do
|
||||
automation.trigger!("kind" => DiscourseAutomation::Triggers::RECURRING)
|
||||
end.not_to change { target_group.reload.users.count }
|
||||
|
||||
expect(target_group_member?(other_users.map(&:id))).to eq(true)
|
||||
expect(owns_badge?(other_users.map(&:id))).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "with update_user_title_and_flair = true" do
|
||||
before do
|
||||
BadgeGranter.grant(badge, user)
|
||||
automation.upsert_field!(
|
||||
"update_user_title_and_flair",
|
||||
"boolean",
|
||||
{ value: true },
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
it "sets user title and flair" do
|
||||
expect(user.title).to be_nil
|
||||
expect(user.flair_group_id).to be_nil
|
||||
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
|
||||
user.reload
|
||||
expect(user.title).to eq("Target Title")
|
||||
expect(user.flair_group_id).to eq(target_group.id)
|
||||
end
|
||||
|
||||
it "updates existing user title and flair" do
|
||||
existing_flair_group = Fabricate(:group)
|
||||
user.update(title: "Existing Title", flair_group_id: existing_flair_group.id)
|
||||
|
||||
expect(user.title).to eq("Existing Title")
|
||||
expect(user.flair_group_id).to eq(existing_flair_group.id)
|
||||
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
|
||||
user.reload
|
||||
expect(user.title).to eq("Target Title")
|
||||
expect(user.flair_group_id).to eq(target_group.id)
|
||||
|
||||
user_badge = UserBadge.find_by(user_id: user.id, badge_id: badge.id)
|
||||
user_badge.destroy
|
||||
|
||||
automation.upsert_field!(
|
||||
"remove_members_without_badge",
|
||||
"boolean",
|
||||
{ value: true },
|
||||
target: "script",
|
||||
)
|
||||
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
|
||||
user.reload
|
||||
expect(user.title).to be_nil
|
||||
expect(user.flair_group_id).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "with update_user_title_and_flair = false" do
|
||||
before do
|
||||
BadgeGranter.grant(badge, user)
|
||||
automation.upsert_field!(
|
||||
"update_user_title_and_flair",
|
||||
"boolean",
|
||||
{ value: false },
|
||||
target: "script",
|
||||
)
|
||||
end
|
||||
|
||||
it "does not update existing user title and flair" do
|
||||
existing_flair_group = Fabricate(:group)
|
||||
user.update(title: "Existing Title", flair_group_id: existing_flair_group.id)
|
||||
|
||||
expect(user.title).to eq("Existing Title")
|
||||
expect(user.flair_group_id).to eq(existing_flair_group.id)
|
||||
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
|
||||
user.reload
|
||||
expect(user.title).to eq("Existing Title")
|
||||
expect(user.flair_group_id).to eq(existing_flair_group.id)
|
||||
|
||||
user_badge = UserBadge.find_by(user_id: user.id, badge_id: badge.id)
|
||||
user_badge.destroy
|
||||
|
||||
automation.upsert_field!(
|
||||
"remove_members_without_badge",
|
||||
"boolean",
|
||||
{ value: true },
|
||||
target: "script",
|
||||
)
|
||||
|
||||
automation.trigger!(
|
||||
"kind" => DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN,
|
||||
"user" => user,
|
||||
)
|
||||
|
||||
user.reload
|
||||
expect(user.title).to eq("Existing Title")
|
||||
expect(user.flair_group_id).to eq(existing_flair_group.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
42
plugins/automation/spec/scripts/zapier_webhook_spec.rb
Normal file
42
plugins/automation/spec/scripts/zapier_webhook_spec.rb
Normal file
@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../discourse_automation_helper"
|
||||
|
||||
describe "ZapierWebhook" do
|
||||
fab!(:topic)
|
||||
|
||||
fab!(:automation) { Fabricate(:automation, script: DiscourseAutomation::Scripts::ZAPIER_WEBHOOK) }
|
||||
|
||||
context "with valid webhook url" do
|
||||
before do
|
||||
automation.upsert_field!(
|
||||
"webhook_url",
|
||||
"text",
|
||||
{ value: "https://hooks.zapier.com/hooks/catch/foo/bar" },
|
||||
)
|
||||
end
|
||||
|
||||
it "enqueues the zapier call" do
|
||||
expect { automation.trigger! }.to change {
|
||||
Jobs::DiscourseAutomationCallZapierWebhook.jobs.length
|
||||
}.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "with invalid webhook url" do
|
||||
before do
|
||||
@orig_logger = Rails.logger
|
||||
Rails.logger = @fake_logger = FakeLogger.new
|
||||
end
|
||||
|
||||
after { Rails.logger = @orig_logger }
|
||||
|
||||
it "logs an error and do nothing" do
|
||||
expect { automation.trigger! }.not_to change {
|
||||
Jobs::DiscourseAutomationCallZapierWebhook.jobs.length
|
||||
}
|
||||
|
||||
expect(Rails.logger.warnings.first).to match(/is not a valid Zapier/)
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user