# frozen_string_literal: true RSpec.describe PushNotificationPusher do it "returns badges url by default" do expect(PushNotificationPusher.get_badge).to eq("/assets/push-notifications/discourse.png") end it "returns custom badges url" do upload = Fabricate(:upload) SiteSetting.push_notifications_icon = upload expect(PushNotificationPusher.get_badge).to eq(UrlHelper.absolute(upload.url)) end context "with user" do fab!(:user) let(:topic_title) { "Topic" } let(:post_url) { "https://example.com/t/1/2" } let(:username) { "system" } def create_subscription data = <<~JSON { "endpoint": "endpoint", "keys": { "p256dh": "p256dh", "auth": "auth" } } JSON PushSubscription.create!(user_id: user.id, data: data) end def execute_push(notification_type: 1, post_number: 1) PushNotificationPusher.push( user, { topic_title: topic_title, username: username, excerpt: "description", topic_id: 1, post_url: post_url, notification_type: notification_type, post_number: post_number, }, ) end it "correctly guesses an image if missing" do message = execute_push(notification_type: -1) expect(message[:icon]).to eq("/assets/push-notifications/discourse.png") end it "correctly finds image if exists" do message = execute_push(notification_type: 1) expect(message[:icon]).to eq("/assets/push-notifications/mentioned.png") end it "sends notification in user's locale" do SiteSetting.allow_user_locale = true user.update!(locale: "pt_BR") TranslationOverride.upsert!( "pt_BR", "discourse_push_notifications.popup.mentioned", "pt_BR notification", ) WebPush .expects(:payload_send) .with { |*args| JSON.parse(args.first[:message])["title"] == "pt_BR notification" } .once create_subscription execute_push end it "triggers a DiscourseEvent with user and message arguments" do WebPush.expects(:payload_send) create_subscription pn_sent_event = DiscourseEvent.track_events { message = execute_push }.first expect(pn_sent_event[:event_name]).to eq(:push_notification_sent) expect(pn_sent_event[:params].first).to eq(user) expect(pn_sent_event[:params].second[:url]).to eq(post_url) end it "deletes subscriptions which are erroring regularly" do start = freeze_time sub = create_subscription response = Struct.new(:body, :inspect, :message).new("test", "test", "failed") error = WebPush::ResponseError.new(response, "localhost") WebPush.expects(:payload_send).raises(error).times(4) # 3 failures in more than 24 hours 3.times do execute_push freeze_time 1.minute.from_now end sub.reload expect(sub.error_count).to eq(3) expect(sub.first_error_at).to eq_time(start) freeze_time(2.days.from_now) execute_push expect(PushSubscription.where(id: sub.id).exists?).to eq(false) end it "deletes invalid subscriptions during send" do missing_endpoint = PushSubscription.create!( user_id: user.id, data: { p256dh: "public ECDH key", keys: { auth: "private ECDH key" } }.to_json, ) missing_p256dh = PushSubscription.create!( user_id: user.id, data: { endpoint: "endpoint 1", keys: { auth: "private ECDH key" } }.to_json, ) missing_auth = PushSubscription.create!( user_id: user.id, data: { endpoint: "endpoint 2", keys: { p256dh: "public ECDH key" } }.to_json, ) valid_subscription = PushSubscription.create!( user_id: user.id, data: { endpoint: "endpoint 3", keys: { p256dh: "public ECDH key", auth: "private ECDH key", }, }.to_json, ) expect(PushSubscription.where(user_id: user.id)).to contain_exactly( missing_endpoint, missing_p256dh, missing_auth, valid_subscription, ) WebPush .expects(:payload_send) .with( has_entries(endpoint: "endpoint 3", p256dh: "public ECDH key", auth: "private ECDH key"), ) .once execute_push expect(PushSubscription.where(user_id: user.id)).to contain_exactly(valid_subscription) end it "handles timeouts" do WebPush.expects(:payload_send).raises(Net::ReadTimeout.new) subscription = create_subscription expect { execute_push }.to_not raise_exception subscription.reload expect(subscription.error_count).to eq(1) end describe "`watching_category_or_tag` notifications" do it "Uses the 'watching_first_post' translation when new topic was created" do message = execute_push( notification_type: Notification.types[:watching_category_or_tag], post_number: 1, ) expect(message[:title]).to eq( I18n.t( "discourse_push_notifications.popup.watching_first_post", site_title: SiteSetting.title, topic: topic_title, username: username, ), ) end it "Uses the 'posted' translation when new post was created" do message = execute_push( notification_type: Notification.types[:watching_category_or_tag], post_number: 2, ) expect(message[:title]).to eq( I18n.t( "discourse_push_notifications.popup.posted", site_title: SiteSetting.title, topic: topic_title, username: username, ), ) end end describe "push_notification_pusher_title_payload modifier" do let(:modifier_block) do Proc.new do |payload| payload[:username] = "super_hijacked" payload end end it "Allows modifications to the payload passed to the translation" do plugin_instance = Plugin::Instance.new plugin_instance.register_modifier(:push_notification_pusher_title_payload, &modifier_block) message = execute_push(notification_type: Notification.types[:mentioned], post_number: 2) expect(message[:title]).to eq( I18n.t( "discourse_push_notifications.popup.mentioned", site_title: SiteSetting.title, topic: topic_title, username: "super_hijacked", ), ) ensure DiscoursePluginRegistry.unregister_modifier( plugin_instance, :push_notification_pusher_title_payload, &modifier_block ) end end end end