diff --git a/app/commands/user/set_discord_roles.rb b/app/commands/user/set_discord_roles.rb index 220a24b07f..f7985a65bc 100644 --- a/app/commands/user/set_discord_roles.rb +++ b/app/commands/user/set_discord_roles.rb @@ -11,7 +11,9 @@ def call [MAINTAINER_ROLE_ID, user.maintainer?], [SUPERMENTOR_ROLE_ID, user.supermentor?], [MENTOR_ROLE_ID, user.mentor?], - [INSIDERS_ROLE_ID, user.insider?] + [INSIDERS_ROLE_ID, user.insider?], + [BOOTCAMP_ATTENDEE_ROLE_ID, user.bootcamp_attendee?], + [BOOTCAMP_MENTOR_ROLE_ID, user.bootcamp_mentor?] ].each do |role_id, condition| add_or_remove!(role_id, condition) rescue RestClient::NotFound @@ -47,6 +49,8 @@ def remove_role!(url) SUPERMENTOR_ROLE_ID = "1085196488436633681".freeze MENTOR_ROLE_ID = "1192435804602105966".freeze INSIDERS_ROLE_ID = "1096024168639766578".freeze + BOOTCAMP_ATTENDEE_ROLE_ID = "1326564387284320276".freeze + BOOTCAMP_MENTOR_ROLE_ID = "1305143333433249867".freeze AUTH_HEADER = "Bot #{Exercism.secrets.discord_bot_token}".freeze private_constant :API_URL, :GUILD_ID, :MAINTAINER_ROLE_ID, :SUPERMENTOR_ROLE_ID, :AUTH_HEADER end diff --git a/app/commands/user/set_discourse_groups.rb b/app/commands/user/set_discourse_groups.rb index 3529c1ae39..feeb920765 100644 --- a/app/commands/user/set_discourse_groups.rb +++ b/app/commands/user/set_discourse_groups.rb @@ -7,6 +7,8 @@ def call set_trust_level! set_pm_enabled! set_insiders! + set_bootcamp_attendee! + set_bootcamp_mentor! rescue DiscourseApi::NotFoundError # If the external user can't be found, then the # oauth didn't complete so there's nothing to do. @@ -33,6 +35,22 @@ def set_insiders! end end + def set_bootcamp_attendee! + if user.bootcamp_attendee? + add_to_group!(BOOTCAMP_ATTENDEES_GROUP_NAME) + else + remove_from_group!(BOOTCAMP_ATTENDEES_GROUP_NAME) + end + end + + def set_bootcamp_mentor! + if user.bootcamp_mentor? + add_to_group!(BOOTCAMP_MENTORS_GROUP_NAME) + else + remove_from_group!(BOOTCAMP_MENTORS_GROUP_NAME) + end + end + def add_to_group!(group_name) client.group_add(group_id(group_name), user_id: [discourse_user_id]) rescue DiscourseApi::UnprocessableEntity @@ -63,4 +81,6 @@ def client = Exercism.discourse_client PM_ENABLED_GROUP_NAME = "pm-enabled".freeze INSIDERS_GROUP_NAME = "insiders".freeze + BOOTCAMP_ATTENDEES_GROUP_NAME = "bootcamp_attendees".freeze + BOOTCAMP_MENTORS_GROUP_NAME = "bootcamp_mentors".freeze end diff --git a/db/migrate/20250108145756_add_bootcamp_roles_to_user_data.rb b/db/migrate/20250108145756_add_bootcamp_roles_to_user_data.rb new file mode 100644 index 0000000000..595e7c6e4d --- /dev/null +++ b/db/migrate/20250108145756_add_bootcamp_roles_to_user_data.rb @@ -0,0 +1,8 @@ +class AddBootcampRolesToUserData < ActiveRecord::Migration[7.0] + def change + return if Rails.env.production? + + add_column :user_data, :bootcamp_attendee, :boolean, null: false, default: false + add_column :user_data, :bootcamp_mentor, :boolean, null: false, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 195a96fd59..fd60de07e8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_12_16_055937) do +ActiveRecord::Schema[7.0].define(version: 2025_01_08_145756) do create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false @@ -1393,6 +1393,8 @@ t.integer "seniority", limit: 1 t.string "bootcamp_affiliate_coupon_code" t.string "bootcamp_free_coupon_code" + t.boolean "bootcamp_attendee", default: false, null: false + t.boolean "bootcamp_mentor", default: false, null: false t.index ["discord_uid"], name: "index_user_data_on_discord_uid", unique: true t.index ["first_donated_at", "show_on_supporters_page", "user_id"], name: "index_user_data__supporters-page" t.index ["first_donated_at"], name: "index_user_data_on_first_donated_at" diff --git a/test/commands/user/set_discord_roles_test.rb b/test/commands/user/set_discord_roles_test.rb index 9f30276701..fcfd610709 100644 --- a/test/commands/user/set_discord_roles_test.rb +++ b/test/commands/user/set_discord_roles_test.rb @@ -5,6 +5,8 @@ class User::SetDiscordRolesTest < ActiveSupport::TestCase uid = '111' user = create :user, :not_mentor, discord_uid: uid + RestClient.unstub(:delete) + RestClient.expects(:delete).with( "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", Authorization: "Bot #{Exercism.secrets.discord_bot_token}" @@ -21,6 +23,14 @@ class User::SetDiscordRolesTest < ActiveSupport::TestCase "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", Authorization: "Bot #{Exercism.secrets.discord_bot_token}" ) + RestClient.expects(:delete).with( + "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1326564387284320276", + Authorization: "Bot #{Exercism.secrets.discord_bot_token}" + ) + RestClient.expects(:delete).with( + "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1305143333433249867", + Authorization: "Bot #{Exercism.secrets.discord_bot_token}" + ) User::SetDiscordRoles.(user) end @@ -35,21 +45,11 @@ class User::SetDiscordRolesTest < ActiveSupport::TestCase end test "adds maintainer role when user is maintainer" do + RestClient.stubs(:delete) + uid = SecureRandom.hex user = create :user, :not_mentor, :maintainer, discord_uid: uid - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196488436633681", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1096024168639766578", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) RestClient.expects(:put).with( "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", {}, @@ -59,47 +59,13 @@ class User::SetDiscordRolesTest < ActiveSupport::TestCase User::SetDiscordRoles.(user) end - test "removes maintainer role when user is not a maintainer" do - uid = SecureRandom.hex - user = create :user, :not_mentor, discord_uid: uid - - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196488436633681", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1096024168639766578", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - - User::SetDiscordRoles.(user) - end - test "adds mentor role when user is mentor" do + RestClient.stubs(:delete) + uid = SecureRandom.hex user = create :user, discord_uid: uid create(:user_track_mentorship, user:) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196488436633681", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1096024168639766578", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) RestClient.expects(:put).with( "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", {}, @@ -109,47 +75,12 @@ class User::SetDiscordRolesTest < ActiveSupport::TestCase User::SetDiscordRoles.(user) end - test "removes mentor role when user is not a mentor" do - uid = SecureRandom.hex - user = create :user, :not_mentor, discord_uid: uid - - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196488436633681", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1096024168639766578", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - - User::SetDiscordRoles.(user) - end - test "add supermentor role when user is supermentor" do + RestClient.stubs(:delete) + uid = SecureRandom.hex user = create :user, :not_mentor, :supermentor, discord_uid: uid - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1096024168639766578", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:put).with( "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196488436633681", {}, @@ -159,49 +90,30 @@ class User::SetDiscordRolesTest < ActiveSupport::TestCase User::SetDiscordRoles.(user) end - test "removes supermentor role when user is not a supermentor" do + test "adds insiders role when user is insider" do + RestClient.stubs(:delete) + uid = SecureRandom.hex - user = create :user, :not_mentor, discord_uid: uid + user = create :user, :not_mentor, :insider, discord_uid: uid - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( + RestClient.expects(:put).with( "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1096024168639766578", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196488436633681", + {}, Authorization: "Bot #{Exercism.secrets.discord_bot_token}" ) User::SetDiscordRoles.(user) end - test "adds insiders role when user is insider" do + test "adds bootcamp attendee role when user is bootcamp attendee" do + RestClient.stubs(:delete) + uid = SecureRandom.hex - user = create :user, :not_mentor, :insider, discord_uid: uid + user = create :user, :not_mentor, discord_uid: uid + user.update!(bootcamp_attendee: true) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196488436633681", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) RestClient.expects(:put).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1096024168639766578", + "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1326564387284320276", {}, Authorization: "Bot #{Exercism.secrets.discord_bot_token}" ) @@ -209,24 +121,16 @@ class User::SetDiscordRolesTest < ActiveSupport::TestCase User::SetDiscordRoles.(user) end - test "removes insiders role when user is not an insider" do + test "adds bootcamp mentor role when user is bootcamp attendee" do + RestClient.stubs(:delete) + uid = SecureRandom.hex user = create :user, :not_mentor, discord_uid: uid + user.update!(bootcamp_mentor: true) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196376058646559", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1085196488436633681", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1192435804602105966", - Authorization: "Bot #{Exercism.secrets.discord_bot_token}" - ) - RestClient.expects(:delete).with( - "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1096024168639766578", + RestClient.expects(:put).with( + "https://discord.com/api/guilds/854117591135027261/members/#{uid}/roles/1305143333433249867", + {}, Authorization: "Bot #{Exercism.secrets.discord_bot_token}" ) @@ -237,7 +141,7 @@ class User::SetDiscordRolesTest < ActiveSupport::TestCase uid = SecureRandom.hex user = create :user, :not_mentor, :insider, discord_uid: uid - RestClient.expects(:delete).raises(RestClient::NotFound).times(3) + RestClient.expects(:delete).raises(RestClient::NotFound).times(5) RestClient.expects(:put).raises(RestClient::NotFound) User::SetDiscordRoles.(user) diff --git a/test/commands/user/set_discourse_groups_test.rb b/test/commands/user/set_discourse_groups_test.rb index 1f8e117a2d..497ac99ef3 100644 --- a/test/commands/user/set_discourse_groups_test.rb +++ b/test/commands/user/set_discourse_groups_test.rb @@ -14,6 +14,9 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase stub_user_requests(user) stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests + stub_request(:put, "https://forum.exercism.org/admin/users/#{DISCOURSE_USER_ID}/trust_level") User::SetDiscourseGroups.(user) @@ -23,6 +26,8 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase user = create :user, reputation: User::SetDiscourseGroups::MIN_REP_FOR_TRUST_LEVEL - 1 stub_user_requests(user) stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests User::SetDiscourseGroups.(user) end @@ -32,6 +37,9 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase stub_user_requests(user) stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests + group_json = { group: { id: PM_ENABLED_GROUP_ID } }.to_json stub_request(:get, "https://forum.exercism.org/groups/#{PM_ENABLED_GROUP_NAME}.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength stub_request(:put, "https://forum.exercism.org/admin/groups/#{PM_ENABLED_GROUP_ID}/members.json") @@ -46,6 +54,9 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase user = create :user, reputation: User::SetDiscourseGroups::MIN_REP_FOR_PM_ENABLED stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests + user_json = { user: { id: DISCOURSE_USER_ID, groups: { id: PM_ENABLED_GROUP_ID } } }.to_json group_json = { group: { id: PM_ENABLED_GROUP_ID } }.to_json stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength @@ -63,6 +74,9 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase # Need this for the trust level stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests + user_json = { user: { id: DISCOURSE_USER_ID } }.to_json stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength stub_request(:put, "https://forum.exercism.org/admin/users/#{DISCOURSE_USER_ID}/trust_level") @@ -74,6 +88,9 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase user = create :user, :insider stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests + user_json = { user: { id: DISCOURSE_USER_ID } }.to_json group_json = { group: { id: INSIDERS_GROUP_ID } }.to_json stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength @@ -87,6 +104,9 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase user = create :user, :insider stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests + user_json = { user: { id: DISCOURSE_USER_ID, groups: { id: INSIDERS_GROUP_ID } } }.to_json group_json = { group: { id: INSIDERS_GROUP_ID } }.to_json stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength @@ -103,6 +123,9 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase user = create :user, insiders_status: :ineligible stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests + user_json = { user: { id: DISCOURSE_USER_ID } }.to_json group_json = { group: { id: INSIDERS_GROUP_ID } }.to_json stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength @@ -119,6 +142,9 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase user = create :user, insiders_status: :ineligible stub_insider_group_requests + stub_bootcamp_attendee_group_requests + stub_bootcamp_mentor_group_requests + user_json = { user: { id: DISCOURSE_USER_ID, groups: { id: INSIDERS_GROUP_ID } } }.to_json group_json = { group: { id: INSIDERS_GROUP_ID } }.to_json stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength @@ -131,6 +157,68 @@ class User::SetDiscourseGroupsTest < ActiveSupport::TestCase User::SetDiscourseGroups.(user) end + test "bootcamp attendee user is added to attendees group" do + user = create :user + user.update!(bootcamp_attendee: true) + + stub_insider_group_requests + stub_bootcamp_mentor_group_requests + + user_json = { user: { id: DISCOURSE_USER_ID } }.to_json + group_json = { group: { id: INSIDERS_GROUP_ID } }.to_json + stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:get, "https://forum.exercism.org/groups/#{BOOTCAMP_ATTENDEES_GROUP_NAME}.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:put, "https://forum.exercism.org/admin/groups/#{BOOTCAMP_ATTENDEES_GROUP_NAME}/members.json") + + User::SetDiscourseGroups.(user) + end + + test "non-bootcamp attendee user is removed from bootcamp attendees group" do + user = create :user, insiders_status: :ineligible + + stub_insider_group_requests + stub_bootcamp_mentor_group_requests + + user_json = { user: { id: DISCOURSE_USER_ID } }.to_json + group_json = { group: { id: INSIDERS_GROUP_ID } }.to_json + stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:get, "https://forum.exercism.org/groups/#{BOOTCAMP_ATTENDEES_GROUP_NAME}.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:put, "https://forum.exercism.org/admin/groups/#{BOOTCAMP_ATTENDEES_GROUP_NAME}/members.json") + + User::SetDiscourseGroups.(user) + end + + test "bootcamp mentor user is added to mentor group" do + user = create :user + user.update!(bootcamp_mentor: true) + + stub_insider_group_requests + stub_bootcamp_attendee_group_requests + + user_json = { user: { id: DISCOURSE_USER_ID } }.to_json + group_json = { group: { id: INSIDERS_GROUP_ID } }.to_json + stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:get, "https://forum.exercism.org/groups/#{BOOTCAMP_MENTORS_GROUP_NAME}.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:put, "https://forum.exercism.org/admin/groups/#{BOOTCAMP_MENTORS_GROUP_NAME}/members.json") + + User::SetDiscourseGroups.(user) + end + + test "non-bootcamp mentor user is removed from bootcamp mentor group" do + user = create :user, insiders_status: :ineligible + + stub_insider_group_requests + stub_bootcamp_attendee_group_requests + + user_json = { user: { id: DISCOURSE_USER_ID } }.to_json + group_json = { group: { id: INSIDERS_GROUP_ID } }.to_json + stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: user_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:get, "https://forum.exercism.org/groups/#{BOOTCAMP_MENTORS_GROUP_NAME}.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:put, "https://forum.exercism.org/admin/groups/#{BOOTCAMP_MENTORS_GROUP_NAME}/members.json") + + User::SetDiscourseGroups.(user) + end + def stub_user_requests(user) json = { user: { id: DISCOURSE_USER_ID } }.to_json stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength @@ -143,6 +231,20 @@ def stub_insider_group_requests stub_request(:delete, "https://forum.exercism.org/admin/groups/#{INSIDERS_GROUP_ID}/members.json?user_ids=#{DISCOURSE_USER_ID}") end + def stub_bootcamp_attendee_group_requests + group_json = { group: { id: BOOTCAMP_ATTENDEES_GROUP_ID } }.to_json + stub_request(:get, "https://forum.exercism.org/groups/#{BOOTCAMP_ATTENDEES_GROUP_NAME}.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:put, "https://forum.exercism.org/admin/groups/#{BOOTCAMP_ATTENDEES_GROUP_ID}/members.json") + stub_request(:delete, "https://forum.exercism.org/admin/groups/#{BOOTCAMP_ATTENDEES_GROUP_ID}/members.json?user_ids=#{DISCOURSE_USER_ID}") + end + + def stub_bootcamp_mentor_group_requests + group_json = { group: { id: BOOTCAMP_MENTORS_GROUP_ID } }.to_json + stub_request(:get, "https://forum.exercism.org/groups/#{BOOTCAMP_MENTORS_GROUP_NAME}.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:put, "https://forum.exercism.org/admin/groups/#{BOOTCAMP_MENTORS_GROUP_ID}/members.json") + stub_request(:delete, "https://forum.exercism.org/admin/groups/#{BOOTCAMP_MENTORS_GROUP_ID}/members.json?user_ids=#{DISCOURSE_USER_ID}") + end + DISCOURSE_USER_ID = 123 PM_ENABLED_GROUP_ID = 456 @@ -150,4 +252,10 @@ def stub_insider_group_requests INSIDERS_GROUP_ID = 789 INSIDERS_GROUP_NAME = "insiders".freeze + + BOOTCAMP_ATTENDEES_GROUP_ID = 140 + BOOTCAMP_ATTENDEES_GROUP_NAME = "bootcamp_attendees".freeze + + BOOTCAMP_MENTORS_GROUP_ID = 141 + BOOTCAMP_MENTORS_GROUP_NAME = "bootcamp_mentors".freeze end diff --git a/test/controllers/discourse_controller_test.rb b/test/controllers/discourse_controller_test.rb index d1c6b8cbc8..f0bf1de2a0 100644 --- a/test/controllers/discourse_controller_test.rb +++ b/test/controllers/discourse_controller_test.rb @@ -43,10 +43,21 @@ class DiscourseControllerTest < ActionDispatch::IntegrationTest sso.expects(:to_url).returns(resulting_url) DiscourseApi::SingleSignOn.expects(:parse).with("who=why", Exercism.secrets.discourse_oauth_secret).returns(sso) + stub_request(:get, "https://forum.exercism.org/users/by-external/#{user.id}").to_return(status: 200, body: { user: { id: 123 } }.to_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength stub_request(:get, "https://forum.exercism.org/groups/insiders.json").to_return(status: 200, body: { group: { id: 1 } }.to_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength stub_request(:delete, "https://forum.exercism.org/admin/groups/1/members.json?user_ids=123") + group_json = { group: { id: 140 } }.to_json + stub_request(:get, "https://forum.exercism.org/groups/bootcamp_attendees.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:put, "https://forum.exercism.org/admin/groups/140/members.json") + stub_request(:delete, "https://forum.exercism.org/admin/groups/140/members.json?user_ids=123") + + group_json = { group: { id: 141 } }.to_json + stub_request(:get, "https://forum.exercism.org/groups/bootcamp_mentors.json").to_return(status: 200, body: group_json, headers: { "content-type": "application/json; charset=utf-8" }) # rubocop:disable Layout/LineLength + stub_request(:put, "https://forum.exercism.org/admin/groups/141/members.json") + stub_request(:delete, "https://forum.exercism.org/admin/groups/141/members.json?user_ids=123") + sign_in!(user) Bullet.profile do