From 2b11cb7abbb19a9898b61f06b275ce384d1ed257 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sun, 9 Mar 2014 13:38:46 +0000 Subject: [PATCH 1/9] VRFS-925 ga event processing --- db/manifest | 1 + db/up/bands_did_session.sql | 2 + ruby/lib/jam_ruby/models/band.rb | 6 + ruby/lib/jam_ruby/models/connection.rb | 12 ++ ruby/lib/jam_ruby/models/music_session.rb | 14 +++ ruby/lib/jam_ruby/models/recording.rb | 6 + .../jam_ruby/resque/google_analytics_event.rb | 115 ++++++++++++++---- .../resque/google_analytics_event_spec.rb | 49 ++++++++ ruby/spec/spec_helper.rb | 27 ++++ 9 files changed, 207 insertions(+), 25 deletions(-) create mode 100644 db/up/bands_did_session.sql create mode 100644 ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb diff --git a/db/manifest b/db/manifest index 47d143af9..77b44e5ca 100755 --- a/db/manifest +++ b/db/manifest @@ -130,3 +130,4 @@ scores_better_test_data.sql connection_client_type.sql add_countries_regions_and_cities.sql plays_refactor.sql +bands_did_session.sql diff --git a/db/up/bands_did_session.sql b/db/up/bands_did_session.sql new file mode 100644 index 000000000..62bb9222c --- /dev/null +++ b/db/up/bands_did_session.sql @@ -0,0 +1,2 @@ +ALTER TABLE bands ADD COLUMN did_real_session boolean default false; + diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb index ea14cabe5..7a5822581 100644 --- a/ruby/lib/jam_ruby/models/band.rb +++ b/ruby/lib/jam_ruby/models/band.rb @@ -258,6 +258,12 @@ module JamRuby name end + def in_real_session?(session) + b_members = self.users.sort_by(&:id).map(&:id) + s_members = session.users.sort_by(&:id).map(&:id) + (b_members - s_members).blank? + end + private def require_at_least_one_genre diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb index c0a6c124b..a4ffa60b2 100644 --- a/ruby/lib/jam_ruby/models/connection.rb +++ b/ruby/lib/jam_ruby/models/connection.rb @@ -18,6 +18,7 @@ module JamRuby validate :can_join_music_session, :if => :joining_session? after_save :require_at_least_one_track_when_in_session, :if => :joining_session? after_create :did_create + before_save :report_add_participant include AASM IDLE_STATE = :idle @@ -127,6 +128,17 @@ module JamRuby self.user.update_lat_lng(self.ip_address) if self.user && self.ip_address end + def report_add_participant + if self.music_session_id_changed? && + self.music_session.present? && + self.connected? && + self.as_musician? && + 0 < (count = self.music_session.connected_participant_count) + GoogleAnalyticsEvent.report_session_participant(count) + end + true + end + private def require_at_least_one_track_when_in_session if tracks.count == 0 diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index 70eb50e7f..c44ff8387 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -24,6 +24,8 @@ module JamRuby has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" + after_create :started_session + validate :require_at_least_one_genre, :limit_max_genres after_save :sync_music_session_history @@ -400,6 +402,18 @@ module JamRuby self.save!(:validate => false) end + def connected_participant_count + Connection.where(:music_session_id => self.id, + :aasm_state => Connection::CONNECT_STATE.to_s, + :as_musician => true) + .count + end + + def started_session + GoogleAnalyticsEvent.track_session_duration(self) + GoogleAnalyticsEvent.track_band_real_session(self) + end + private def require_at_least_one_genre diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 65ab57968..cb4d11d0d 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -28,6 +28,7 @@ module JamRuby before_save :sanitize_active_admin before_create :add_to_feed + after_create :check_first_band_recording def add_to_feed feed = Feed.new @@ -39,6 +40,11 @@ module JamRuby self.band_id = nil if self.band_id == '' end + def check_first_band_recording + GoogleAnalyticsEvent.report_band_recording(self.band) + true + end + def comment_count self.comments.size end diff --git a/ruby/lib/jam_ruby/resque/google_analytics_event.rb b/ruby/lib/jam_ruby/resque/google_analytics_event.rb index 30c08084b..bca201c3e 100644 --- a/ruby/lib/jam_ruby/resque/google_analytics_event.rb +++ b/ruby/lib/jam_ruby/resque/google_analytics_event.rb @@ -1,43 +1,108 @@ -class GoogleAnalyticsEvent +require 'resque' - @queue = 'google_analytics_event' +module JamRuby + class GoogleAnalyticsEvent - @@log = Logging.logger[GoogleAnalyticsEvent] + @queue = :google_analytics_event - def self.perform(category, action) + CAT_SESS_SIZE = 'SessionSize' + ACTION_SESS_SIZE = 'Size' + CAT_SESS_DUR = 'SessionDuration' + ACTION_SESS_DUR = 'Duration' + CAT_BAND = 'Band' + ACTION_BAND_SESS = 'Session' + ACTION_BAND_REC = 'Recording' - @@log.info("starting (#{category}, #{action})") + @@log = Logging.logger[GoogleAnalyticsEvent] - run(category, action) + SESSION_INTERVALS = [1, 5, 10, 15, 30, 45, 60, 90, 120, 180] # minutes + QUEUE_SESSION_TRACKER = :session_tracker - @@log.info("done (#{category}, #{action})") + class SessionDurationTracker + @queue = QUEUE_SESSION_TRACKER - end - - def self.enqueue(category, event) - begin - Resque.enqueue(AudioMixer, category, event) - true - rescue - # implies redis is down. but since there is no retry logic with this, we should at least log a warn in case we've configured something wrong - @@log.warn("unable to enqueue") - false + def self.perform(session_id, interval_idx) + return unless session = MusicSession.find(session_id) + interval_idx += 1 + GoogleAnalyticsEvent.enqueue(CAT_SESS_DUR, ACTION_SESS_DUR, interval_idx) + + if SESSION_INTERVALS.count-1 > interval_idx + next_time = session.created_at + SESSION_INTERVALS[interval_idx].minutes + Resque.enqueue_at(next_time, + self, + :session_id => session_id, + :interval_idx => interval_idx) + end + end end - end - def self.run(category, action) + class BandSessionTracker + @queue = QUEUE_SESSION_TRACKER - raise "no google analytics tracking ID" unless APP_CONFIG.ga_ua + def self.perform(session_id) + return unless session = MusicSession.find(session_id) + band = session.band + if band.in_real_session(session)? + GoogleAnalyticsEvent.enqueue(CAT_BAND, ACTION_BAND_SESS) + band.update_attributes!(did_real_session: true) + end if band + end + end - params = { + def self.track_session_duration(session) + Resque.enqueue_at(SESSION_INTERVALS[0].minute.from_now, + SessionDurationTracker, + :session_id => session.id, + :interval_idx => 0) + end + + BAND_SESSION_MIN_DURATION = 15 # minutes + + def self.track_band_real_session(session) + if session.band && !session.band.did_real_session? + Resque.enqueue_at(BAND_SESSION_MIN_DURATION.minutes.from_now, + BandSessionTracker, + :session_id => session.id) + end + end + + def self.report_band_recording(band) + if band && 1 == Recording.where(:band_id => band.id).count + self.enqueue(CAT_BAND, ACTION_BAND_REC) + end + end + + def self.report_session_participant(participant_count) + self.enqueue(CAT_SESS_SIZE, ACTION_SESS_SIZE, participant_count) + end + + def self.enqueue(category, event, data=nil) + begin + Resque.enqueue(GoogleAnalyticsEvent, category, event, data) + true + rescue + # implies redis is down. but since there is no retry logic with this, we should at least log a warn in case we've configured something wrong + @@log.warn("unable to enqueue") + false + end + end + + def self.perform(category, action, data) + @@log.info("starting (#{category}, #{action})") + raise "no google analytics tracking ID" unless APP_CONFIG.ga_ua + params = { v: APP_CONFIG.ga_ua_version, tid: APP_CONFIG.ga_ua, cid: APP_CONFIG.ga_anonymous_client_id, t: "event", ec: category, - ea: action - } + ea: action, + el: 'data', + ev: data.to_s + } + RestClient.post(APP_CONFIG.ga_endpoint, params: params, timeout: 8, open_timeout: 8) + @@log.info("done (#{category}, #{action})") + end - RestClient.post(APP_CONFIG.ga_endpoint, params: params, timeout: 8, open_timeout: 8) end -end \ No newline at end of file +end diff --git a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb new file mode 100644 index 000000000..5c9c113f0 --- /dev/null +++ b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests +describe GoogleAnalyticsEvent do + + let(:ga) { GoogleAnalyticsEvent.new } + + describe "report session participant" do + it "queues job when session participant joins" do + pending + end + end + + describe "track session duration" do + + it "queues jobs for session duration intervals" do + pending + end + + it "stop queueing jobs if session is deleted" do + pending + end + end + + describe "track band real session" do + it "queues jobs for band real session tracking" do + pending + end + + it "stop queueing jobs if band session is deleted" do + pending + end + + it "updates band real session attributes" do + pending + end + end + + describe "report band recording" do + it "queues job when first band recording is made" do + pending + end + + it "does not queues job when band has a recording already" do + pending + end + end + +end diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 46034742c..0a0b444ce 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -64,6 +64,7 @@ Spork.prefork do # # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| + config.color_enabled = true config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true @@ -101,6 +102,32 @@ Spork.prefork do # the seed, which is printed after each run. # --seed 1234 config.order = 'random' + + REDIS_PID = "#{Rails.root}/tmp/pids/redis-test.pid" + REDIS_CACHE_PATH = "#{Rails.root}/tmp/cache/" + config.before(:suite) do + redis_options = { + "daemonize" => 'yes', + "pidfile" => REDIS_PID, + "port" => 9736, + "timeout" => 300, + "save 900" => 1, + "save 300" => 1, + "save 60" => 10000, + "dbfilename" => "dump.rdb", + "dir" => REDIS_CACHE_PATH, + "loglevel" => "debug", + "logfile" => "stdout", + "databases" => 16 + }.map { |k, v| "#{k} #{v}" }.join("\n") + `echo '#{redis_options}' | redis-server -` + end + config.after(:suite) do + %x{ + cat #{REDIS_PID} | xargs kill -QUIT + rm -f #{REDIS_CACHE_PATH}dump.rdb + } + end end end From 848702e04da8f5f616c930f95d052f85ed0345d8 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sun, 9 Mar 2014 13:45:35 +0000 Subject: [PATCH 2/9] VRFS-925 added bands_did_session.sql to bottom --- db/manifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/manifest b/db/manifest index 5a2082d2c..20a1a3da6 100755 --- a/db/manifest +++ b/db/manifest @@ -130,6 +130,6 @@ scores_better_test_data.sql connection_client_type.sql add_countries_regions_and_cities.sql plays_refactor.sql -bands_did_session.sql fix_max_mind_isp_and_geo.sql update_get_work_for_client_type.sql +bands_did_session.sql From 6ba8256d1a20835f6097e88d1d12e33c8d16bb49 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Mon, 10 Mar 2014 08:58:03 +0000 Subject: [PATCH 3/9] VRFS-925 fixed typo for testing --- ruby/lib/jam_ruby.rb | 1 + ruby/lib/jam_ruby/resque/google_analytics_event.rb | 2 +- ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 6d3903af7..9d2c59552 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -37,6 +37,7 @@ require "jam_ruby/resque/scheduled/audiomixer_retry" require "jam_ruby/resque/scheduled/icecast_config_retry" require "jam_ruby/resque/scheduled/icecast_source_check" require "jam_ruby/resque/scheduled/cleanup_facebook_signup" +require "jam_ruby/resque/google_analytics_event" require "jam_ruby/mq_router" require "jam_ruby/base_manager" require "jam_ruby/connection_manager" diff --git a/ruby/lib/jam_ruby/resque/google_analytics_event.rb b/ruby/lib/jam_ruby/resque/google_analytics_event.rb index bca201c3e..642338900 100644 --- a/ruby/lib/jam_ruby/resque/google_analytics_event.rb +++ b/ruby/lib/jam_ruby/resque/google_analytics_event.rb @@ -42,7 +42,7 @@ module JamRuby def self.perform(session_id) return unless session = MusicSession.find(session_id) band = session.band - if band.in_real_session(session)? + if band.in_real_session?(session) GoogleAnalyticsEvent.enqueue(CAT_BAND, ACTION_BAND_SESS) band.update_attributes!(did_real_session: true) end if band diff --git a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb index 5c9c113f0..353dcc651 100644 --- a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb +++ b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' # these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests +=begin describe GoogleAnalyticsEvent do let(:ga) { GoogleAnalyticsEvent.new } @@ -47,3 +48,4 @@ describe GoogleAnalyticsEvent do end end +=end From 66b66a926b1d7ffcc163869477e8b84d4646a1c0 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Mon, 10 Mar 2014 09:02:23 +0000 Subject: [PATCH 4/9] VRFS-925 updating position of bands_did_session.sql --- db/manifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/manifest b/db/manifest index 243cb4fc0..d6790337a 100755 --- a/db/manifest +++ b/db/manifest @@ -132,5 +132,5 @@ add_countries_regions_and_cities.sql plays_refactor.sql fix_max_mind_isp_and_geo.sql update_get_work_for_client_type.sql -bands_did_session.sql events.sql +bands_did_session.sql From 5a4bff14d2e35b8495f3b1223e4610e0a62cc552 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Tue, 8 Apr 2014 17:15:49 +0000 Subject: [PATCH 5/9] VRFS-925 running tests for server side GA events --- ruby/Gemfile | 3 +- ruby/lib/jam_ruby/models/connection.rb | 2 +- ruby/lib/jam_ruby/models/recording.rb | 8 +- .../jam_ruby/resque/google_analytics_event.rb | 33 +++-- ruby/spec/factories.rb | 1 + .../resque/google_analytics_event_spec.rb | 118 ++++++++++++------ ruby/spec/spec_helper.rb | 31 ++--- 7 files changed, 121 insertions(+), 75 deletions(-) diff --git a/ruby/Gemfile b/ruby/Gemfile index f81545af0..6ad832f29 100644 --- a/ruby/Gemfile +++ b/ruby/Gemfile @@ -54,7 +54,8 @@ group :test do gem 'spork', '0.9.0' gem 'database_cleaner', '0.7.0' gem 'faker' - gem 'resque_spec' + gem 'resque_spec', :path => "/home/jam/src/resque_spec/" + gem 'timecop' end # Specify your gem's dependencies in jam_ruby.gemspec diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb index a4ffa60b2..51a6befd5 100644 --- a/ruby/lib/jam_ruby/models/connection.rb +++ b/ruby/lib/jam_ruby/models/connection.rb @@ -18,7 +18,7 @@ module JamRuby validate :can_join_music_session, :if => :joining_session? after_save :require_at_least_one_track_when_in_session, :if => :joining_session? after_create :did_create - before_save :report_add_participant + after_save :report_add_participant include AASM IDLE_STATE = :idle diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 9dbdd8fef..58ef9d126 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -28,7 +28,6 @@ module JamRuby before_save :sanitize_active_admin before_create :add_to_feed - after_create :check_first_band_recording def add_to_feed feed = Feed.new @@ -40,11 +39,6 @@ module JamRuby self.band_id = nil if self.band_id == '' end - def check_first_band_recording - GoogleAnalyticsEvent.report_band_recording(self.band) - true - end - def comment_count self.comments.size end @@ -152,6 +146,8 @@ module JamRuby recording.band = music_session.band if recording.save + GoogleAnalyticsEvent.report_band_recording(recording.band) + music_session.connections.each do |connection| connection.tracks.each do |track| recording.recorded_tracks << RecordedTrack.create_from_track(track, recording) diff --git a/ruby/lib/jam_ruby/resque/google_analytics_event.rb b/ruby/lib/jam_ruby/resque/google_analytics_event.rb index 642338900..264eeda3d 100644 --- a/ruby/lib/jam_ruby/resque/google_analytics_event.rb +++ b/ruby/lib/jam_ruby/resque/google_analytics_event.rb @@ -28,27 +28,11 @@ module JamRuby if SESSION_INTERVALS.count-1 > interval_idx next_time = session.created_at + SESSION_INTERVALS[interval_idx].minutes - Resque.enqueue_at(next_time, - self, - :session_id => session_id, - :interval_idx => interval_idx) + Resque.enqueue_at(next_time, self, :session_id => session_id, :interval_idx => interval_idx) end end end - class BandSessionTracker - @queue = QUEUE_SESSION_TRACKER - - def self.perform(session_id) - return unless session = MusicSession.find(session_id) - band = session.band - if band.in_real_session?(session) - GoogleAnalyticsEvent.enqueue(CAT_BAND, ACTION_BAND_SESS) - band.update_attributes!(did_real_session: true) - end if band - end - end - def self.track_session_duration(session) Resque.enqueue_at(SESSION_INTERVALS[0].minute.from_now, SessionDurationTracker, @@ -56,11 +40,24 @@ module JamRuby :interval_idx => 0) end + class BandSessionTracker + @queue = QUEUE_SESSION_TRACKER + + def self.perform(session_id) + return unless session = MusicSession.find(session_id) + band = session.band + if band.in_real_session?(session) + band.update_attributes!(did_real_session: true) + GoogleAnalyticsEvent.enqueue(CAT_BAND, ACTION_BAND_SESS, nil) + end if band + end + end + BAND_SESSION_MIN_DURATION = 15 # minutes def self.track_band_real_session(session) if session.band && !session.band.did_real_session? - Resque.enqueue_at(BAND_SESSION_MIN_DURATION.minutes.from_now, + Resque.enqueue_in(BAND_SESSION_MIN_DURATION.minutes, BandSessionTracker, :session_id => session.id) end diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 0c32a5dbb..2b5b85d88 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -160,6 +160,7 @@ FactoryGirl.define do association :owner, factory: :user association :music_session, factory: :music_session + association :band, factory: :band factory :recording_with_track do before(:create) { |recording| diff --git a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb index 353dcc651..b63859c4f 100644 --- a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb +++ b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb @@ -1,51 +1,99 @@ require 'spec_helper' -# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests -=begin describe GoogleAnalyticsEvent do let(:ga) { GoogleAnalyticsEvent.new } - describe "report session participant" do - it "queues job when session participant joins" do - pending + describe "track band analytics" do + before :each do + ResqueSpec.reset! end + it 'reports first recording' do + pending + user = FactoryGirl.create(:user) + band = FactoryGirl.create(:band) + music_session = FactoryGirl.create(:music_session, + :creator => user, + :musician_access => true, + :band => band) + recording = Recording.start(music_session, user) + expect(Recording.where(:band_id => band.id).count).to eq(1) + + GoogleAnalyticsEvent.should have_queued(GoogleAnalyticsEvent::CAT_BAND, + GoogleAnalyticsEvent::ACTION_BAND_REC, + nil) + end + + it 'reports first real session' do + pending + ResqueSpec.reset! + JamRuby::GoogleAnalyticsEvent::BandSessionTracker.should have_schedule_size_of(0) + user = FactoryGirl.create(:user) + user1 = FactoryGirl.create(:user) + band = FactoryGirl.create(:band) + band.users << user + band.users << user1 + band.reload + music_session = FactoryGirl.create(:music_session, :creator => user, + :musician_access => true, :band => band) + expect(band.band_musicians.count).to eq(2) + expect(band.did_real_session).to eq(false) + connection = FactoryGirl.create(:connection, :user => user, :as_musician => true, + :aasm_state => Connection::CONNECT_STATE.to_s, + :music_session => music_session) + connection = FactoryGirl.create(:connection, :user => user1, :as_musician => true, + :aasm_state => Connection::CONNECT_STATE.to_s, + :music_session => music_session) + music_session.reload + expect(music_session.connected_participant_count).to eq(2) + + JamRuby::GoogleAnalyticsEvent::BandSessionTracker.should have_schedule_size_of_at_least(1) + JamRuby::GoogleAnalyticsEvent.should have_queue_size_of(0) + Timecop.travel((GoogleAnalyticsEvent::BAND_SESSION_MIN_DURATION + 1).minutes.from_now) + + band.reload; music_session.reload + expect(band.in_real_session?(music_session)).to eq(true) + JamRuby::GoogleAnalyticsEvent.should have_queue_size_of_at_least(1) + # JamRuby::GoogleAnalyticsEvent.should have_queued(GoogleAnalyticsEvent::CAT_BAND, GoogleAnalyticsEvent::ACTION_BAND_SESS, nil) + + band.reload + expect(band.did_real_session).to eq(true) + end + end - describe "track session duration" do - - it "queues jobs for session duration intervals" do + describe "track session analytics" do + before :each do + ResqueSpec.reset! + end + it 'reports size increment' do pending + user = FactoryGirl.create(:user) + music_session = FactoryGirl.create(:music_session, + :creator => user, + :musician_access => true) + connection = FactoryGirl.create(:connection, :user => user, + :as_musician => true, + :aasm_state => Connection::CONNECT_STATE.to_s, + :music_session => music_session) + GoogleAnalyticsEvent.should have_queued(GoogleAnalyticsEvent::CAT_SESS_SIZE, + GoogleAnalyticsEvent::ACTION_SESS_SIZE, + music_session.connected_participant_count) end - it "stop queueing jobs if session is deleted" do - pending - end - end - - describe "track band real session" do - it "queues jobs for band real session tracking" do - pending - end - - it "stop queueing jobs if band session is deleted" do - pending - end - - it "updates band real session attributes" do - pending - end - end - - describe "report band recording" do - it "queues job when first band recording is made" do - pending - end - - it "does not queues job when band has a recording already" do - pending + it 'reports duration' do + # pending + user = FactoryGirl.create(:user) + JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of_at_least(0) + music_session = FactoryGirl.create(:music_session, + :creator => user, + :musician_access => true) + JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of_at_least(1) + # Timecop.travel((GoogleAnalyticsEvent::BAND_SESSION_MIN_DURATION + 1).minutes.from_now) + # GoogleAnalyticsEvent.should have_queued(GoogleAnalyticsEvent::CAT_SESS_SIZE, + # GoogleAnalyticsEvent::ACTION_SESS_SIZE, + # music_session.connected_participant_count) end end end -=end diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 3c42de696..1d65f5330 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -23,6 +23,9 @@ require 'spork' require 'database_cleaner' require 'factories' +require 'timecop' +require 'resque_spec/scheduler' + # uncomment this to see active record logs #ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base) @@ -107,20 +110,20 @@ Spork.prefork do REDIS_CACHE_PATH = "#{Rails.root}/tmp/cache/" config.before(:suite) do redis_options = { - "daemonize" => 'yes', - "pidfile" => REDIS_PID, - "port" => 9736, - "timeout" => 300, - "save 900" => 1, - "save 300" => 1, - "save 60" => 10000, - "dbfilename" => "dump.rdb", - "dir" => REDIS_CACHE_PATH, - "loglevel" => "debug", - "logfile" => "stdout", - "databases" => 16 - }.map { |k, v| "#{k} #{v}" }.join("\n") - `echo '#{redis_options}' | redis-server -` + "--daemonize" => 'yes', + "--pidfile" => REDIS_PID, + "--port" => 9736, + "--timeout" => 300, + "--save 900" => 1, + "--save 300" => 1, + "--save 60" => 10000, + "--dbfilename" => "dump.rdb", + "--dir" => REDIS_CACHE_PATH, + "--loglevel" => "debug", + "--logfile" => "stdout", + "--databases" => 16 + }.map { |k, v| "#{k} #{v}" }.join(" ") + `redis-server #{redis_options}` end config.after(:suite) do %x{ From 9ddbc6ba5ca4b4b02869bd371590a893ae6fe233 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Mon, 14 Apr 2014 10:57:21 +0000 Subject: [PATCH 6/9] VRFS-925 finishing up tests --- .../jam_ruby/resque/google_analytics_event.rb | 16 ++++--- .../resque/google_analytics_event_spec.rb | 48 +++++++++++-------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/ruby/lib/jam_ruby/resque/google_analytics_event.rb b/ruby/lib/jam_ruby/resque/google_analytics_event.rb index 264eeda3d..deb85cde1 100644 --- a/ruby/lib/jam_ruby/resque/google_analytics_event.rb +++ b/ruby/lib/jam_ruby/resque/google_analytics_event.rb @@ -16,15 +16,17 @@ module JamRuby @@log = Logging.logger[GoogleAnalyticsEvent] SESSION_INTERVALS = [1, 5, 10, 15, 30, 45, 60, 90, 120, 180] # minutes + QUEUE_BAND_TRACKER = :band_tracker QUEUE_SESSION_TRACKER = :session_tracker class SessionDurationTracker @queue = QUEUE_SESSION_TRACKER - def self.perform(session_id, interval_idx) - return unless session = MusicSession.find(session_id) + def self.perform(args={}) + session_id, interval_idx = args['session_id'], args['interval_idx'].to_i + return unless session_id && session = MusicSession.find(session_id) + GoogleAnalyticsEvent.enqueue(CAT_SESS_DUR, ACTION_SESS_DUR, SESSION_INTERVALS[interval_idx]) interval_idx += 1 - GoogleAnalyticsEvent.enqueue(CAT_SESS_DUR, ACTION_SESS_DUR, interval_idx) if SESSION_INTERVALS.count-1 > interval_idx next_time = session.created_at + SESSION_INTERVALS[interval_idx].minutes @@ -41,13 +43,13 @@ module JamRuby end class BandSessionTracker - @queue = QUEUE_SESSION_TRACKER + @queue = QUEUE_BAND_TRACKER def self.perform(session_id) return unless session = MusicSession.find(session_id) band = session.band if band.in_real_session?(session) - band.update_attributes!(did_real_session: true) + band.update_attribute(:did_real_session, true) GoogleAnalyticsEvent.enqueue(CAT_BAND, ACTION_BAND_SESS, nil) end if band end @@ -57,9 +59,9 @@ module JamRuby def self.track_band_real_session(session) if session.band && !session.band.did_real_session? - Resque.enqueue_in(BAND_SESSION_MIN_DURATION.minutes, + Resque.enqueue_at(BAND_SESSION_MIN_DURATION.minutes.from_now, BandSessionTracker, - :session_id => session.id) + session.id) end end diff --git a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb index b63859c4f..aeef82f0a 100644 --- a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb +++ b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb @@ -5,11 +5,8 @@ describe GoogleAnalyticsEvent do let(:ga) { GoogleAnalyticsEvent.new } describe "track band analytics" do - before :each do - ResqueSpec.reset! - end it 'reports first recording' do - pending + ResqueSpec.reset! user = FactoryGirl.create(:user) band = FactoryGirl.create(:band) music_session = FactoryGirl.create(:music_session, @@ -25,7 +22,6 @@ describe GoogleAnalyticsEvent do end it 'reports first real session' do - pending ResqueSpec.reset! JamRuby::GoogleAnalyticsEvent::BandSessionTracker.should have_schedule_size_of(0) user = FactoryGirl.create(:user) @@ -46,16 +42,21 @@ describe GoogleAnalyticsEvent do :music_session => music_session) music_session.reload expect(music_session.connected_participant_count).to eq(2) + expect(band.did_real_session).to eq(false) - JamRuby::GoogleAnalyticsEvent::BandSessionTracker.should have_schedule_size_of_at_least(1) - JamRuby::GoogleAnalyticsEvent.should have_queue_size_of(0) - Timecop.travel((GoogleAnalyticsEvent::BAND_SESSION_MIN_DURATION + 1).minutes.from_now) - - band.reload; music_session.reload - expect(band.in_real_session?(music_session)).to eq(true) - JamRuby::GoogleAnalyticsEvent.should have_queue_size_of_at_least(1) - # JamRuby::GoogleAnalyticsEvent.should have_queued(GoogleAnalyticsEvent::CAT_BAND, GoogleAnalyticsEvent::ACTION_BAND_SESS, nil) + ResqueSpec.queues["#{GoogleAnalyticsEvent::QUEUE_BAND_TRACKER}_scheduled"].select do |qq| + qq[:class] == GoogleAnalyticsEvent::BandSessionTracker.name + end.count.should eq(1) + # GoogleAnalyticsEvent::BandSessionTracker.should have_schedule_size_of_at_least(1) + GoogleAnalyticsEvent.should_not have_queued(GoogleAnalyticsEvent::CAT_BAND, GoogleAnalyticsEvent::ACTION_BAND_SESS, nil) + Timecop.freeze((GoogleAnalyticsEvent::BAND_SESSION_MIN_DURATION + 1).minutes.from_now) + qname = "#{ResqueSpec.queue_name(JamRuby::GoogleAnalyticsEvent::BandSessionTracker)}_scheduled" + expect(ResqueSpec.peek(qname).present?).to eq(true) + ResqueSpec.perform_next(qname) + GoogleAnalyticsEvent.should have_queued(GoogleAnalyticsEvent::CAT_BAND, + GoogleAnalyticsEvent::ACTION_BAND_SESS, + nil) band.reload expect(band.did_real_session).to eq(true) end @@ -67,7 +68,6 @@ describe GoogleAnalyticsEvent do ResqueSpec.reset! end it 'reports size increment' do - pending user = FactoryGirl.create(:user) music_session = FactoryGirl.create(:music_session, :creator => user, @@ -82,17 +82,23 @@ describe GoogleAnalyticsEvent do end it 'reports duration' do - # pending user = FactoryGirl.create(:user) - JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of_at_least(0) + JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of(0) music_session = FactoryGirl.create(:music_session, :creator => user, :musician_access => true) - JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of_at_least(1) - # Timecop.travel((GoogleAnalyticsEvent::BAND_SESSION_MIN_DURATION + 1).minutes.from_now) - # GoogleAnalyticsEvent.should have_queued(GoogleAnalyticsEvent::CAT_SESS_SIZE, - # GoogleAnalyticsEvent::ACTION_SESS_SIZE, - # music_session.connected_participant_count) + GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of(1) + + GoogleAnalyticsEvent::SESSION_INTERVALS.each do |interval| + Timecop.travel((interval + 1).minutes.from_now) + qname = "#{ResqueSpec.queue_name(JamRuby::GoogleAnalyticsEvent::SessionDurationTracker)}_scheduled" + next unless ResqueSpec.peek(qname).present? + ResqueSpec.perform_next(qname) + GoogleAnalyticsEvent.should have_queued(GoogleAnalyticsEvent::CAT_SESS_DUR, + GoogleAnalyticsEvent::ACTION_SESS_DUR, + interval) + end + GoogleAnalyticsEvent.should have_queue_size_of(GoogleAnalyticsEvent::SESSION_INTERVALS.count - 1) end end From 08951258a829b55d7f8c0f67b83603569353feaf Mon Sep 17 00:00:00 2001 From: Anthony Davis Date: Mon, 14 Apr 2014 15:52:26 -0500 Subject: [PATCH 7/9] VRFS-905 - adding bands integration tests --- web/spec/features/bands_spec.rb | 143 +++++++++++++++++++++++++++++--- web/spec/support/utilities.rb | 18 ++++ 2 files changed, 148 insertions(+), 13 deletions(-) diff --git a/web/spec/features/bands_spec.rb b/web/spec/features/bands_spec.rb index d8e2b9b78..81166d368 100644 --- a/web/spec/features/bands_spec.rb +++ b/web/spec/features/bands_spec.rb @@ -16,16 +16,16 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do #end end + let(:fan) { FactoryGirl.create(:fan) } let(:user) { FactoryGirl.create(:user) } let(:finder) { FactoryGirl.create(:user) } before(:each) do UserMailer.deliveries.clear - navigate_band_setup end - def navigate_band_setup - sign_in_poltergeist(user) + def navigate_band_setup login=user + sign_in_poltergeist(login) wait_until_curtain_gone find('div.homecard.profile').trigger(:click) find('#profile-bands-link').trigger(:click) @@ -33,26 +33,143 @@ describe "Bands", :js => true, :type => :feature, :capybara_feature => true do expect(page).to have_selector('#band-setup-title') end - it "have validation errors shown, but then can navigate past and eventually save" do - find('#btn-band-setup-next').trigger(:click) - find('#tdBandName .error-text li', text: "can't be blank") - find('#tdBandBiography .error-text li', text: "can't be blank") - find('#tdBandGenres .error-text li', text: "At least 1 genre is required.") + def complete_band_setup_form(band, biography, params={}) + navigate_band_setup unless URI.parse(current_url).fragment == '/band/setup/new' + params['band-name'] ||= band || "Default band name" + params['band-biography'] ||= biography || "Default band biography" within('#band-setup-form') do - fill_in "band-name", with: "The Band" - fill_in "band-biography", with: "Biography" + params.each do |field, value| + fill_in field, with: "#{value}" + end first('#band-genres input[type="checkbox"]').trigger(:click) end sleep 1 # work around race condition find('#btn-band-setup-next').trigger(:click) find('h2', text: 'Step 2: Add Band Members') - find('#btn-band-setup-save').trigger(:click) - find('#band-profile-name', text: "The Band") - find('#band-profile-biography', text: "Biography") end + + context "band profile - new band setup" do + it "displays 'Set up your band' link to user" do + sign_in_poltergeist user + view_profile_of user + find('#profile-bands-link').trigger(:click) + expect(page).to have_selector('#band-setup-link') + end + + it "does not display band setup link when viewed by other user" do + in_client(fan) do + sign_in_poltergeist fan + view_profile_of user + find('#profile-bands-link').trigger(:click) + + expect(page).to_not have_selector('#band-setup-link') + end + end + + it "indicates required fields and user may eventually complete" do + navigate_band_setup + find('#btn-band-setup-next').trigger(:click) + expect(page).to have_selector('#tdBandName .error-text li', text: "can't be blank") + expect(page).to have_selector('#tdBandBiography .error-text li', text: "can't be blank") + expect(page).to have_selector('#tdBandGenres .error-text li', text: "At least 1 genre is required.") + + complete_band_setup_form("Band name", "Band biography") + + expect(page).to have_selector('#band-profile-name', text: "Band name") + expect(page).to have_selector('#band-profile-biography', text: "Band biography") + + end + + it "limits genres to 3" do + navigate_band_setup + within('#band-setup-form') do + fill_in 'band-name', with: "whatever" + fill_in 'band-biography', with: "a good story" + all('#band-genres input[type="checkbox"]').each_with_index do |cb, i| + cb.trigger(:click) unless i > 3 + end + end + sleep 1 + find('#btn-band-setup-next').trigger(:click) + expect(page).to have_selector('#tdBandGenres .error-text li', text: "No more than 3 genres are allowed.") + end + + it "handles max-length field input" do + pending "update this after VRFS-1610 is resolved" + max = { + name: 1024, + bio: 4000, + website: 1024 # unsure what the max is, see VRFS-1610 + } + navigate_band_setup + band_name = 'a'*(max[:name] + 1) + band_bio = 'b'*(max[:bio] + 1) + band_website = 'c'*(max[:website] + 1) + complete_band_setup_form(band_name, band_bio, 'band-website' => band_website) + + expect(page).to have_selector('#band-profile-name', text: band_name.slice(0, max[:name])) + expect(page).to have_selector('#band-profile-biography', text: band_bio.slice(0, max[:bio])) + end + + it "handles special characters in text fields" do + pending "update this after VRFS-1609 is resolved" + navigate_band_setup + band_name = garbage(3) + ' ' + garbage(50) + band_bio = garbage(500) + band_website = garbage(500) + complete_band_setup_form(band_name, band_bio, 'band-website' => band_website) + + expect(page).to have_selector('#band-profile-name', text: band_name) + expect(page).to have_selector('#band-profile-biography', text: band_bio) + end + + it "another user receives invite notification during Band Setup" + end + + + context "about view" do + it "displays the band's information to another user" + #photo + #name + #website address + #country, state, city + #biography/description + #genres chosen + #number of followers, recordings, sessions + #actions: follow button + + it "allows a user to follow the band" + end + + context "members view" do + it "photo and name links to the musician's profile page" + it "displays photo, name, location, instruments played" + it "displays a hover bubble containing more info on musician" + it "displays any pending band invitations when viewed by current band member" + + end + + context "history view" do + it "shows public info" + it "does not show private info to non-band user" + it "shows private info to band user" + end + + context "social view" do + it "displays musicians and fans who follow band" + end + + context "band profile - editing" do + it "about page shows the current band's info when 'Edit Profile' is clicked" + it "members page shows 'Edit Members' button and user can remove member" + it "non-member cannot Edit Profile" + it "non-member cannot Edit Members" + end + + it "band shows up in sidebar search result" end diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index 47e3099ac..b053f6514 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -406,6 +406,13 @@ def assert_all_tracks_seen(users=[]) end end +def view_profile_of user + id = user.kind_of?(JamRuby::User) ? user.id : user + # assume some user signed in already (allows reuse in multi-user tests) + visit "/client#/profile/#{id}" + wait_until_curtain_gone +end + def show_user_menu page.execute_script("$('ul.shortcuts').show()") #page.execute_script("JK.UserDropdown.menuHoverIn()") @@ -421,4 +428,15 @@ def send_key(selector, keycode = 13) keypress_script = "var e = $.Event('keyup', { keyCode: #{keycode} }); jQuery('#{selector}').trigger(e);" page.driver.execute_script(keypress_script) +end + +def special_characters + ["?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}"] +end + +def garbage length + output = '' + length.times { output << special_characters.sample } + output.gsub!(/<[\/|!|\?]/, '/<') # security risk -- avoids inputting tags until VRFS-1609 resolved + output.slice(0, length) end \ No newline at end of file From e5a6f01198192f084869163e6a223ca2d2c5b738 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 15 Apr 2014 00:14:02 -0400 Subject: [PATCH 8/9] VRFS-1529 hover bubbles should render above dialogs --- web/app/assets/stylesheets/client/hoverBubble.css.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/assets/stylesheets/client/hoverBubble.css.scss b/web/app/assets/stylesheets/client/hoverBubble.css.scss index 42faa0190..3d2efd754 100644 --- a/web/app/assets/stylesheets/client/hoverBubble.css.scss +++ b/web/app/assets/stylesheets/client/hoverBubble.css.scss @@ -4,7 +4,7 @@ background-color:#242323; border:solid 1px #ed3618; position:absolute; - z-index:999; + z-index:1100; &.musician-bubble { From e01409a0fb720eccf0659e1f33d4308afa5668da Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Tue, 15 Apr 2014 00:48:48 -0400 Subject: [PATCH 9/9] VRFS-1530 added missing semicolon (not sure if this will fix issue or not - unable to reproduce) --- web/app/assets/javascripts/hoverRecording.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/assets/javascripts/hoverRecording.js b/web/app/assets/javascripts/hoverRecording.js index 20a5b3994..3555b626e 100644 --- a/web/app/assets/javascripts/hoverRecording.js +++ b/web/app/assets/javascripts/hoverRecording.js @@ -73,7 +73,7 @@ instrumentHtml = '
'; $.each(val.instrument_ids, function(index, val) { instrumentHtml += '  '; - }) + }); instrumentHtml += '
'; musicianHtml += instrumentHtml;