Commit ba398235a740b4902fc2993b45a8e2f82b4530dd
Exists in
master
and in
1 other branch
Merge branch 'notifications' of https://github.com/amaabca/errbit into amaabca-notifications
Conflicts: Gemfile.lock
Showing
25 changed files
with
224 additions
and
24 deletions
Show diff stats
Gemfile
... | ... | @@ -5,6 +5,9 @@ gem 'rails', '3.2.8' |
5 | 5 | gem 'nokogiri' |
6 | 6 | gem 'mongoid', '~> 2.4.10' |
7 | 7 | |
8 | +# force SSL | |
9 | +gem 'rack-ssl', :require => 'rack/ssl' | |
10 | + | |
8 | 11 | gem 'haml' |
9 | 12 | gem 'htmlentities', "~> 4.3.0" |
10 | 13 | |
... | ... | @@ -30,6 +33,7 @@ gem 'kaminari' |
30 | 33 | gem 'rack-ssl-enforcer' |
31 | 34 | gem 'fabrication', "~> 1.3.0" # Both for tests, and loading demo data |
32 | 35 | gem 'rails_autolink', '~> 1.0.9' |
36 | +gem 'campy' | |
33 | 37 | |
34 | 38 | platform :ruby do |
35 | 39 | gem 'mongo', '= 1.6.2' |
... | ... | @@ -45,7 +49,6 @@ group :development, :test do |
45 | 49 | gem 'webmock', :require => false |
46 | 50 | unless ENV["CI"] |
47 | 51 | gem 'ruby-debug', :platform => :mri_18 |
48 | - gem 'debugger', :platform => :mri_19 | |
49 | 52 | end |
50 | 53 | # gem 'rpm_contrib' |
51 | 54 | # gem 'newrelic_rpm' | ... | ... |
Gemfile.lock
... | ... | @@ -40,6 +40,8 @@ GEM |
40 | 40 | bson_ext (1.6.2) |
41 | 41 | bson (~> 1.6.2) |
42 | 42 | builder (3.0.3) |
43 | + campy (0.1.3) | |
44 | + multi_json (~> 1.0) | |
43 | 45 | capistrano (2.13.3) |
44 | 46 | highline |
45 | 47 | net-scp (>= 1.0.0) |
... | ... | @@ -62,13 +64,6 @@ GEM |
62 | 64 | rdoc |
63 | 65 | daemons (1.1.8) |
64 | 66 | database_cleaner (0.6.7) |
65 | - debugger (1.2.0) | |
66 | - columnize (>= 0.3.1) | |
67 | - debugger-linecache (~> 1.1.1) | |
68 | - debugger-ruby_core_source (~> 1.1.3) | |
69 | - debugger-linecache (1.1.2) | |
70 | - debugger-ruby_core_source (>= 1.1.1) | |
71 | - debugger-ruby_core_source (1.1.3) | |
72 | 67 | devise (1.5.3) |
73 | 68 | bcrypt-ruby (~> 3.0) |
74 | 69 | orm_adapter (~> 0.0.3) |
... | ... | @@ -289,10 +284,10 @@ DEPENDENCIES |
289 | 284 | actionmailer_inline_css (~> 1.3.0) |
290 | 285 | bson (= 1.6.2) |
291 | 286 | bson_ext (= 1.6.2) |
287 | + campy | |
292 | 288 | capistrano |
293 | 289 | capybara |
294 | 290 | database_cleaner (~> 0.6.0) |
295 | - debugger | |
296 | 291 | devise (~> 1.5.3) |
297 | 292 | email_spec |
298 | 293 | execjs |
... | ... | @@ -313,6 +308,7 @@ DEPENDENCIES |
313 | 308 | omniauth-github |
314 | 309 | oruen_redmine_client |
315 | 310 | pivotal-tracker |
311 | + rack-ssl | |
316 | 312 | rack-ssl-enforcer |
317 | 313 | rails (= 3.2.8) |
318 | 314 | rails_autolink (~> 1.0.9) | ... | ... |
3.19 KB
3.19 KB
2.8 KB
app/assets/javascripts/form.js
... | ... | @@ -8,6 +8,9 @@ $(function(){ |
8 | 8 | if($('div.issue_tracker.nested').length) |
9 | 9 | activateTypeSelector('issue_tracker', 'tracker_params'); |
10 | 10 | |
11 | + if($('div.notification_service.nested').length) | |
12 | + activateTypeSelector('notification_service', 'notification_params'); | |
13 | + | |
11 | 14 | $('body').addClass('has-js'); |
12 | 15 | $('.label_radio').click(function(){ |
13 | 16 | activateLabelIcons(); | ... | ... |
app/assets/stylesheets/application.css.erb
app/assets/stylesheets/errbit.css
... | ... | @@ -535,10 +535,11 @@ a.button.active { |
535 | 535 | display: inline-block; |
536 | 536 | } |
537 | 537 | |
538 | -/* Watchers and Issue Tracker Forms */ | |
539 | -div.watcher.nested .watcher_params, div.issue_tracker.nested .tracker_params { | |
538 | +/* Watchers / Issue Tracker / Notification Forms */ | |
539 | +div.watcher.nested .watcher_params, div.issue_tracker.nested .tracker_params, div.notification_service.nested .notification_params { | |
540 | 540 | display: none; |
541 | 541 | } |
542 | + | |
542 | 543 | div.nested .chosen { |
543 | 544 | display: block !important; |
544 | 545 | } |
... | ... | @@ -546,35 +547,35 @@ div.nested .choose { |
546 | 547 | margin-bottom: 0.5em; |
547 | 548 | } |
548 | 549 | |
549 | -div.issue_tracker.nested .choose { | |
550 | +div.issue_tracker.nested .choose, div.notification_service.nested .choose { | |
550 | 551 | background-color: #ebebeb; |
551 | 552 | border: 1px solid #dddddd; |
552 | 553 | margin: 0 0 15px; |
553 | 554 | padding: 12px; |
554 | 555 | } |
555 | -div.issue_tracker.nested img { | |
556 | +div.issue_tracker.nested img, div.notification_service.nested img { | |
556 | 557 | vertical-align: middle; |
557 | 558 | } |
558 | 559 | |
559 | 560 | /* Icons for Issue Tracker Radio Buttons */ |
560 | -div.issue_tracker.nested label.label_radio { | |
561 | +div.issue_tracker.nested label.label_radio, div.notification_service.nested label.label_radio { | |
561 | 562 | color: #929292; |
562 | 563 | padding-left: 33px; |
563 | 564 | margin-bottom: 6px; |
564 | 565 | margin-right: 8px; |
565 | 566 | line-height: 30px; |
566 | 567 | } |
567 | -div.issue_tracker.nested .choose { | |
568 | +div.issue_tracker.nested .choose, div.notification_service.nested .choose { | |
568 | 569 | padding-bottom: 6px; |
569 | 570 | } |
570 | -div.issue_tracker.nested label.label_radio:hover { | |
571 | +div.issue_tracker.nested label.label_radio:hover, div.notification_service.nested label.label_radio:hover { | |
571 | 572 | color: #696969; |
572 | 573 | } |
573 | -div.issue_tracker.nested .label_radio input { | |
574 | +div.issue_tracker.nested .label_radio input, div.notification_service.nested .label_radio input { | |
574 | 575 | position: absolute; left: -9999px; |
575 | 576 | } |
576 | 577 | |
577 | -div.issue_tracker.nested label.r_on, div.issue_tracker.nested label.r_on:hover { | |
578 | +div.issue_tracker.nested label.r_on, div.issue_tracker.nested label.r_on:hover, div.notification_service.nested label.r_on, div.notification_service.nested label.r_on:hover { | |
578 | 579 | color: #191919; |
579 | 580 | } |
580 | 581 | ... | ... |
app/assets/stylesheets/issue_tracker_icons.css.erb
1 | 1 | /* Issue Tracker inactive, select, create and goto icons */ |
2 | 2 | <% trackers = IssueTracker.subclasses.map{|t| t.label } << 'none' %> |
3 | + | |
3 | 4 | <% trackers.each do |tracker| %> |
4 | 5 | div.issue_tracker.nested label.<%= tracker %> { |
5 | 6 | background: url(/assets/<%= tracker %>_inactive.png) no-repeat; |
... | ... | @@ -14,3 +15,4 @@ div.issue_tracker.nested label.r_on.<%= tracker %> { |
14 | 15 | background: transparent url(/assets/<%= tracker %>_goto.png) 6px 5px no-repeat; |
15 | 16 | } |
16 | 17 | <% end %> |
18 | + | ... | ... |
app/assets/stylesheets/notification_service_icons.css.erb
0 → 100644
... | ... | @@ -0,0 +1,18 @@ |
1 | + /* Notification Service inactive, select, create and goto icons */ | |
2 | +<% notification_services = NotificationService.subclasses.map{|t| t.label } << 'none' %> | |
3 | + | |
4 | +<% notification_services.each do |notification_service| %> | |
5 | +div.notification_service.nested label.<%= notification_service %> { | |
6 | + background: url(/assets/<%= notification_service %>_inactive.png) no-repeat; | |
7 | +} | |
8 | +div.notification_service.nested label.r_on.<%= notification_service %> { | |
9 | + background: url(/assets/<%= notification_service %>_create.png) no-repeat; | |
10 | +} | |
11 | +#action-bar a.<%= notification_service %>_create { | |
12 | + background: transparent url(/assets/<%= notification_service %>_create.png) 6px 5px no-repeat; | |
13 | +} | |
14 | +#action-bar a.<%= notification_service %>_goto { | |
15 | + background: transparent url(/assets/<%= notification_service %>_goto.png) 6px 5px no-repeat; | |
16 | +} | |
17 | +<% end %> | |
18 | + | ... | ... |
app/controllers/apps_controller.rb
... | ... | @@ -29,12 +29,14 @@ class AppsController < InheritedResources::Base |
29 | 29 | def create |
30 | 30 | @app = App.new(params[:app]) |
31 | 31 | initialize_subclassed_issue_tracker |
32 | + initialize_subclassed_notification_service | |
32 | 33 | create! |
33 | 34 | end |
34 | 35 | |
35 | 36 | def update |
36 | 37 | @app = resource |
37 | 38 | initialize_subclassed_issue_tracker |
39 | + initialize_subclassed_notification_service | |
38 | 40 | update! |
39 | 41 | end |
40 | 42 | |
... | ... | @@ -70,6 +72,7 @@ class AppsController < InheritedResources::Base |
70 | 72 | end |
71 | 73 | |
72 | 74 | def initialize_subclassed_issue_tracker |
75 | + # set the app's issue tracker | |
73 | 76 | if params[:app][:issue_tracker_attributes] && tracker_type = params[:app][:issue_tracker_attributes][:type] |
74 | 77 | if IssueTracker.subclasses.map(&:name).concat(["IssueTracker"]).include?(tracker_type) |
75 | 78 | @app.issue_tracker = tracker_type.constantize.new(params[:app][:issue_tracker_attributes]) |
... | ... | @@ -77,6 +80,15 @@ class AppsController < InheritedResources::Base |
77 | 80 | end |
78 | 81 | end |
79 | 82 | |
83 | + def initialize_subclassed_notification_service | |
84 | + # set the app's notification service | |
85 | + if params[:app][:notification_service_attributes] && notification_type = params[:app][:notification_service_attributes][:type] | |
86 | + if NotificationService.subclasses.map(&:name).concat(["NotificationService"]).include?(notification_type) | |
87 | + @app.notification_service = notification_type.constantize.new(params[:app][:notification_service_attributes]) | |
88 | + end | |
89 | + end | |
90 | + end | |
91 | + | |
80 | 92 | def begin_of_association_chain |
81 | 93 | # Filter the @apps collection to apps watched by the current user, unless user is an admin. |
82 | 94 | # If user is an admin, then no filter is applied, and all apps are shown. |
... | ... | @@ -90,6 +102,7 @@ class AppsController < InheritedResources::Base |
90 | 102 | def plug_params app |
91 | 103 | app.watchers.build if app.watchers.none? |
92 | 104 | app.issue_tracker = IssueTracker.new unless app.issue_tracker_configured? |
105 | + app.notification_service = NotificationService.new unless app.notification_service_configured? | |
93 | 106 | app.copy_attributes_from(params[:copy_attributes_from]) if params[:copy_attributes_from] |
94 | 107 | end |
95 | 108 | ... | ... |
app/helpers/apps_helper.rb
... | ... | @@ -16,6 +16,11 @@ module AppsHelper |
16 | 16 | @any_github_repos |
17 | 17 | end |
18 | 18 | |
19 | + def any_notification_services? | |
20 | + detect_any_apps_with_attributes unless @any_notification_services | |
21 | + @any_notification_services | |
22 | + end | |
23 | + | |
19 | 24 | def any_issue_trackers? |
20 | 25 | detect_any_apps_with_attributes unless @any_issue_trackers |
21 | 26 | @any_issue_trackers |
... | ... | @@ -29,11 +34,12 @@ module AppsHelper |
29 | 34 | private |
30 | 35 | |
31 | 36 | def detect_any_apps_with_attributes |
32 | - @any_github_repos = @any_issue_trackers = @any_deploys = false | |
37 | + @any_github_repos = @any_issue_trackers = @any_deploys = @any_notification_services = false | |
33 | 38 | @apps.each do |app| |
34 | 39 | @any_github_repos ||= app.github_repo? |
35 | 40 | @any_issue_trackers ||= app.issue_tracker_configured? |
36 | 41 | @any_deploys ||= !!app.last_deploy_at |
42 | + @any_notification_services ||= app.notification_service_configured? | |
37 | 43 | end |
38 | 44 | end |
39 | 45 | end | ... | ... |
app/models/app.rb
... | ... | @@ -17,6 +17,8 @@ class App |
17 | 17 | embeds_many :watchers |
18 | 18 | embeds_many :deploys |
19 | 19 | embeds_one :issue_tracker |
20 | + embeds_one :notification_service | |
21 | + | |
20 | 22 | has_many :problems, :inverse_of => :app, :dependent => :destroy |
21 | 23 | |
22 | 24 | before_validation :generate_api_key, :on => :create |
... | ... | @@ -33,7 +35,8 @@ class App |
33 | 35 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } |
34 | 36 | accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, |
35 | 37 | :reject_if => proc { |attrs| !IssueTracker.subclasses.map(&:to_s).include?(attrs[:type].to_s) } |
36 | - | |
38 | + accepts_nested_attributes_for :notification_service, :allow_destroy => true, | |
39 | + :reject_if => proc { |attrs| !NotificationService.subclasses.map(&:to_s).include?(attrs[:type].to_s) } | |
37 | 40 | |
38 | 41 | # Processes a new error report. |
39 | 42 | # |
... | ... | @@ -121,6 +124,11 @@ class App |
121 | 124 | !!(issue_tracker && issue_tracker.class < IssueTracker && issue_tracker.project_id.present?) |
122 | 125 | end |
123 | 126 | |
127 | + def notification_service_configured? | |
128 | + !!(notification_service && notification_service.class < NotificationService && notification_service.api_token.present?) | |
129 | + end | |
130 | + | |
131 | + | |
124 | 132 | def notification_recipients |
125 | 133 | if notify_all_users |
126 | 134 | (User.all.map(&:email).reject(&:blank?) + watchers.map(&:address)).uniq |
... | ... | @@ -137,7 +145,7 @@ class App |
137 | 145 | self.send("#{k}=", copy_app.send(k)) |
138 | 146 | end |
139 | 147 | # Clone the embedded objects that can be changed via apps/edit (ignore errs & deploys, etc.) |
140 | - %w(watchers issue_tracker).each do |relation| | |
148 | + %w(watchers issue_tracker notification_service).each do |relation| | |
141 | 149 | if obj = copy_app.send(relation) |
142 | 150 | self.send("#{relation}=", obj.is_a?(Array) ? obj.map(&:clone) : obj.clone) |
143 | 151 | end | ... | ... |
app/models/issue_tracker.rb
app/models/notice_observer.rb
... | ... | @@ -4,6 +4,11 @@ class NoticeObserver < Mongoid::Observer |
4 | 4 | def after_create notice |
5 | 5 | return unless should_notify? notice |
6 | 6 | |
7 | + # if the app has a notficiation service, fire it off | |
8 | + unless notice.app.notification_service.nil? | |
9 | + notice.app.notification_service.create_notification(notice.problem) | |
10 | + end | |
11 | + | |
7 | 12 | Mailer.err_notification(notice).deliver |
8 | 13 | end |
9 | 14 | |
... | ... | @@ -15,5 +20,4 @@ class NoticeObserver < Mongoid::Observer |
15 | 20 | (Errbit::Config.per_app_email_at_notices && app.email_at_notices || Errbit::Config.email_at_notices).include?(notice.problem.notices_count) && |
16 | 21 | app.notification_recipients.any? |
17 | 22 | end |
18 | - | |
19 | 23 | end | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +class NotificationService | |
2 | + include Mongoid::Document | |
3 | + | |
4 | + field :room_id, :type => String | |
5 | + field :api_token, :type => String | |
6 | + field :subdomain, :type => String | |
7 | + | |
8 | + embedded_in :app, :inverse_of => :notification_service | |
9 | + | |
10 | + validate :check_params | |
11 | + | |
12 | + # Subclasses are responsible for overwriting this method. | |
13 | + def check_params; true; end | |
14 | + | |
15 | + def notification_description(problem) | |
16 | + "[#{ problem.environment }][#{ problem.where }] #{problem.message.to_s.truncate(100)}" | |
17 | + end | |
18 | + | |
19 | + # Allows us to set the issue tracker class from a single form. | |
20 | + def type; self._type; end | |
21 | + def type=(t); self._type=t; end | |
22 | + | |
23 | + # Retrieve tracker label from either class or instance. | |
24 | + Label = '' | |
25 | + def self.label; self::Label; end | |
26 | + def label; self.class.label; end | |
27 | +end | ... | ... |
... | ... | @@ -0,0 +1,29 @@ |
1 | +class NotificationService::CampfireService < NotificationService | |
2 | + Label = "campfire" | |
3 | + Fields = [ | |
4 | + [:subdomain, { | |
5 | + :placeholder => "Campfire Subdomain" | |
6 | + }], | |
7 | + [:api_token, { | |
8 | + :placeholder => "API Token" | |
9 | + }], | |
10 | + [:room_id, { | |
11 | + :placeholder => "Room ID", | |
12 | + :label => "Room ID" | |
13 | + }], | |
14 | + ] | |
15 | + | |
16 | + def check_params | |
17 | + if Fields.detect {|f| self[f[0]].blank? } | |
18 | + errors.add :base, 'You must specify your Campfire Subdomain, API token and Room ID' | |
19 | + end | |
20 | + end | |
21 | + | |
22 | + def create_notification(problem) | |
23 | + # build the campfire client | |
24 | + campy = Campy::Room.new(:account => subdomain, :token => api_token, :room_id => room_id) | |
25 | + | |
26 | + # post the issue to the campfire room | |
27 | + campy.speak "[errbit] http://#{Errbit::Config.host}/apps/#{problem.app.id.to_s} #{notification_description problem}" | |
28 | + end | |
29 | +end | |
0 | 30 | \ No newline at end of file | ... | ... |
app/views/apps/_fields.html.haml
... | ... | @@ -0,0 +1,25 @@ |
1 | +%fieldset | |
2 | + %legend Notification Service | |
3 | + = f.fields_for :notification_service do |w| | |
4 | + %div.notification_service.nested | |
5 | + %div.choose | |
6 | + = label_tag :type_none, :for => label_for_attr(w, 'type_notificationservice'), :class => "label_radio none" do | |
7 | + = w.radio_button :type, "NotificationService", 'data-section' => 'none' | |
8 | + (None) | |
9 | + - NotificationService.subclasses.each do |notification_service| | |
10 | + = label_tag "type_#{notification_service.label}:", :for => label_for_attr(w, "type_#{notification_service.name.downcase.gsub(':','')}"), :class => "label_radio #{notification_service.label}" do | |
11 | + = w.radio_button :type, notification_service.name, 'data-section' => notification_service.label | |
12 | + = notification_service.name[/::(.*)Service/,1].titleize | |
13 | + | |
14 | + %div.notification_params.none{:class => (w.object && !(w.object.class < NotificationService)) ? 'chosen' : nil} | |
15 | + - NotificationService.subclasses.each do |notification_service| | |
16 | + %div.notification_params{:class => (w.object.is_a?(notification_service) ? 'chosen ' : '') << notification_service.label} | |
17 | + - notification_service::Fields.each do |field, field_info| | |
18 | + = w.label field, field_info[:label] || field.to_s.titleize | |
19 | + - field_type = field == :password ? :password_field : :text_field | |
20 | + = w.send field_type, field, :placeholder => field_info[:placeholder], :value => w.object.send(field) | |
21 | + | |
22 | + .image_preloader | |
23 | + - (NotificationService.subclasses.map{|t| t.label } << 'none').each do |notification_service| | |
24 | + = image_tag "#{notification_service}_inactive.png" | |
25 | + = image_tag "#{notification_service}_create.png" | ... | ... |
app/views/apps/index.html.haml
... | ... | @@ -8,6 +8,8 @@ |
8 | 8 | %th Name |
9 | 9 | - if any_github_repos? |
10 | 10 | %th GitHub Repo |
11 | + - if any_notification_services? | |
12 | + %th Notification Service | |
11 | 13 | - if any_issue_trackers? |
12 | 14 | %th Tracker |
13 | 15 | - if any_deploys? |
... | ... | @@ -21,6 +23,10 @@ |
21 | 23 | %td.github_repo |
22 | 24 | - if app.github_repo? |
23 | 25 | = link_to(app.github_repo, app.github_url, :target => '_blank') |
26 | + - if any_notification_services? | |
27 | + %td.notification_service | |
28 | + - if app.notification_service_configured? | |
29 | + = image_tag("#{app.notification_service.label}_goto.png") | |
24 | 30 | - if any_issue_trackers? |
25 | 31 | %td.issue_tracker |
26 | 32 | - if app.issue_tracker_configured? | ... | ... |
config/environments/production.rb
spec/fabricators/issue_tracker_fabricator.rb
... | ... | @@ -0,0 +1,10 @@ |
1 | +Fabricator :notification_service do | |
2 | + app! | |
3 | + room_id { sequence :word } | |
4 | + api_token { sequence :word } | |
5 | + subdomain { sequence :word } | |
6 | +end | |
7 | + | |
8 | +%w(campfire).each do |t| | |
9 | + Fabricator "#{t}_notification_service".to_sym, :from => :notification_service, :class_name => "NotificationService::#{t.camelcase}Service" | |
10 | +end | ... | ... |
spec/models/notice_observer_spec.rb
... | ... | @@ -25,7 +25,6 @@ describe NoticeObserver do |
25 | 25 | end |
26 | 26 | |
27 | 27 | describe "email notifications for a resolved issue" do |
28 | - | |
29 | 28 | before do |
30 | 29 | Errbit::Config.per_app_email_at_notices = true |
31 | 30 | @app = Fabricate(:app_with_watcher, :email_at_notices => [1]) |
... | ... | @@ -43,4 +42,28 @@ describe NoticeObserver do |
43 | 42 | Fabricate(:notice, :err => @err) |
44 | 43 | end |
45 | 44 | end |
45 | + | |
46 | + describe "should send a notification if a notification service is configured" do | |
47 | + let(:app) { app = Fabricate(:app, :email_at_notices => [1], :notification_service => Fabricate(:campfire_notification_service))} | |
48 | + let(:err) { Fabricate(:err, :problem => Fabricate(:problem, :app => app, :notices_count => 100)) } | |
49 | + | |
50 | + before do | |
51 | + Errbit::Config.per_app_email_at_notices = true | |
52 | + end | |
53 | + | |
54 | + after do | |
55 | + Errbit::Config.per_app_email_at_notices = false | |
56 | + end | |
57 | + | |
58 | + it "should create a campfire notification" do | |
59 | + err.problem.stub(:notices_count) { 1 } | |
60 | + app.notification_service.stub!(:create_notification).and_return(true) | |
61 | + app.stub!(:notification_recipients => %w('ryan@system88.com')) | |
62 | + app.notification_service.should_receive(:create_notification) | |
63 | + | |
64 | + Notice.create!(:err => err, :message => 'FooError: Too Much Bar', :server_environment => {'environment-name' => 'production'}, | |
65 | + :backtrace => [{ :error => 'Le Broken' }], :notifier => { 'name' => 'Notifier', 'version' => '1', 'url' => 'http://toad.com' }) | |
66 | + end | |
67 | + end | |
68 | + | |
46 | 69 | end | ... | ... |
spec/models/notification_service/campfire_service_spec.rb
0 → 100644
... | ... | @@ -0,0 +1,21 @@ |
1 | +require 'spec_helper' | |
2 | + | |
3 | +describe NotificationService::CampfireService do | |
4 | + it "it should send a notification to campfire" do | |
5 | + # setup | |
6 | + notice = Fabricate :notice | |
7 | + notification_service = Fabricate :campfire_notification_service, :app => notice.app | |
8 | + problem = notice.problem | |
9 | + | |
10 | + #campy stubbing | |
11 | + campy = mock('CampfireService') | |
12 | + Campy::Room.stub(:new).and_return(campy) | |
13 | + campy.stub(:speak) { true } | |
14 | + | |
15 | + #assert | |
16 | + campy.should_receive(:speak) | |
17 | + | |
18 | + notification_service.create_notification(problem) | |
19 | + end | |
20 | +end | |
21 | + | ... | ... |