368 lines
14 KiB
Ruby
368 lines
14 KiB
Ruby
require 'spec_helper'
|
|
|
|
describe IcecastMount do
|
|
|
|
let(:icecast_mount) { FactoryGirl.create(:icecast_mount) }
|
|
let(:output) { StringIO.new }
|
|
let(:builder) { ::Builder::XmlMarkup.new(:target => output, :indent => 1) }
|
|
|
|
it "save error" do
|
|
mount = IcecastMount.new
|
|
mount.save.should be_false
|
|
mount.errors[:name].should == ["can't be blank", "must start with /"]
|
|
end
|
|
|
|
|
|
it "save" do
|
|
mount = IcecastMount.new
|
|
mount.name = "/" + Faker::Lorem.characters(10)
|
|
mount.stream_name = Faker::Lorem.characters(10)
|
|
mount.stream_description = Faker::Lorem.characters(10)
|
|
mount.stream_url = Faker::Lorem.characters(10)
|
|
mount.genre = Faker::Lorem.characters(10)
|
|
mount.source_username = Faker::Lorem.characters(10)
|
|
mount.source_pass = Faker::Lorem.characters(10)
|
|
mount.intro = Faker::Lorem.characters(10)
|
|
mount.fallback_mount = Faker::Lorem.characters(10)
|
|
mount.on_connect = Faker::Lorem.characters(10)
|
|
mount.on_disconnect = Faker::Lorem.characters(10)
|
|
mount.fallback_override = true
|
|
mount.fallback_when_full = true
|
|
mount.max_listeners = 1000
|
|
mount.max_listener_duration = 3600
|
|
mount.authentication = FactoryGirl.create(:icecast_user_authentication)
|
|
mount.server = FactoryGirl.create(:icecast_server_with_overrides)
|
|
|
|
mount.save!
|
|
|
|
mount.dumpXml(builder)
|
|
|
|
output.rewind
|
|
|
|
xml = Nokogiri::XML(output)
|
|
xml.css('mount mount-name').text.should == mount.name
|
|
xml.css('mount username').text.should == mount.source_username
|
|
xml.css('mount password').text.should == mount.source_pass
|
|
xml.css('mount max-listeners').text.should == mount.max_listeners.to_s
|
|
xml.css('mount max-listener-duration').text.should == mount.max_listener_duration.to_s
|
|
xml.css('mount intro').text.should == mount.intro
|
|
xml.css('mount fallback-mount').text.should == mount.fallback_mount
|
|
xml.css('mount fallback-override').text.should == mount.fallback_override.to_s
|
|
xml.css('mount fallback-when-full').text.should == mount.fallback_when_full.to_s
|
|
xml.css('mount stream-name').text.should == mount.stream_name
|
|
xml.css('mount stream-description').text.should == mount.stream_description
|
|
xml.css('mount stream-url').text.should == mount.stream_url
|
|
xml.css('mount genre').text.should == mount.genre
|
|
xml.css('mount bitrate').length.should == 0
|
|
xml.css('mount charset').text == mount.charset
|
|
xml.css('mount public').text == mount.is_public.to_s
|
|
xml.css('mount type').text == mount.mime_type
|
|
xml.css('mount subtype').text == mount.subtype
|
|
xml.css('mount burst-size').length.should == 0
|
|
xml.css('mount mp3-metadata-interval').length.should == 0
|
|
xml.css('mount hidden').text.should == mount.hidden.to_s
|
|
xml.css('mount on-connect').text.should == mount.on_connect
|
|
xml.css('mount on-disconnect').text.should == mount.on_disconnect
|
|
xml.css('mount dump-file').length.should == 0
|
|
xml.css('mount authentication').length.should == 1 # no reason to test futher; it's tested in that model
|
|
end
|
|
|
|
describe "override xml over mount template" do
|
|
let(:mount) {FactoryGirl.create(:iceast_mount_with_template)}
|
|
|
|
it "should allow override by mount" do
|
|
mount.dumpXml(builder)
|
|
output.rewind
|
|
xml = Nokogiri::XML(output)
|
|
xml.css('mount mount-name').text.should == mount.name
|
|
xml.css('mount username').text.should == mount.source_username
|
|
xml.css('mount bitrate').text.should == mount.bitrate.to_s
|
|
xml.css('mount type').text.should == mount.mount_template.mime_type
|
|
xml.css('mount stream-url').text.should == mount.stream_url
|
|
|
|
# now see the stream_url, and bitrate, go back to the template's value because we set it to nil
|
|
mount.bitrate = nil
|
|
mount.stream_url = nil
|
|
mount.save!
|
|
|
|
output = StringIO.new
|
|
builder = ::Builder::XmlMarkup.new(:target => output, :indent => 1)
|
|
mount.dumpXml(builder)
|
|
output.rewind
|
|
xml = Nokogiri::XML(output)
|
|
xml.css('mount bitrate').text.should == mount.mount_template.bitrate.to_s
|
|
xml.css('mount stream-url').text.should == mount.mount_template.stream_url
|
|
end
|
|
end
|
|
|
|
describe "poke configs" do
|
|
let(:server) { a = FactoryGirl.create(:icecast_server_with_overrides); a.config_updated; IcecastServer.find(a.id) }
|
|
|
|
before(:each) do
|
|
server.mounts << FactoryGirl.create(:icecast_mount, server: server)
|
|
server.save!
|
|
server.config_updated
|
|
server.reload
|
|
server.config_changed.should == 0
|
|
end
|
|
|
|
it "success via server" do
|
|
server.mounts.first.save!
|
|
server.reload
|
|
server.config_changed.should == 1
|
|
end
|
|
|
|
it "success when deleted" do
|
|
server.mounts.first.destroy
|
|
server.reload
|
|
server.config_changed.should == 1
|
|
end
|
|
end
|
|
|
|
describe "icecast server callbacks" do
|
|
it "source up" do
|
|
icecast_mount.source_up
|
|
end
|
|
end
|
|
|
|
describe "listener/source" do
|
|
let(:mount) {FactoryGirl.create(:iceast_mount_with_template)}
|
|
|
|
describe "listeners" do
|
|
it "listener_add" do
|
|
mount.listener_add
|
|
mount.listeners.should == 1
|
|
end
|
|
|
|
it "listener_remove when at 0" do
|
|
mount.listener_remove
|
|
mount.listeners.should == 0
|
|
end
|
|
|
|
it "listener_remove" do
|
|
mount.listener_add
|
|
mount.listener_remove
|
|
mount.listeners.should == 0
|
|
end
|
|
end
|
|
|
|
describe "sources" do
|
|
it "source_up" do
|
|
mount.source_up
|
|
mount.sourced.should == true
|
|
end
|
|
end
|
|
|
|
describe "sources" do
|
|
it "source_down" do
|
|
mount.source_up
|
|
mount.source_down
|
|
mount.sourced.should == false
|
|
end
|
|
end
|
|
end
|
|
describe "build_session_mount" do
|
|
|
|
let(:server1) {FactoryGirl.create(:icecast_server_minimal)}
|
|
let(:server2) {FactoryGirl.create(:icecast_server_with_overrides)}
|
|
let(:server3) {FactoryGirl.create(:icecast_server_with_overrides)}
|
|
let(:hidden_music_session) { FactoryGirl.create(:active_music_session, :fan_access => false)}
|
|
let(:public_music_session) { FactoryGirl.create(:active_music_session, :fan_access => true)}
|
|
let(:public_music_session2) { FactoryGirl.create(:active_music_session, :fan_access => true)}
|
|
let(:public_music_session3) { FactoryGirl.create(:active_music_session, :fan_access => true)}
|
|
|
|
before(:each) do
|
|
|
|
end
|
|
|
|
it "no fan access means no mount" do
|
|
mount = IcecastMount.build_session_mount(hidden_music_session.music_session, hidden_music_session, IcecastServer.find_best_server_for_user(hidden_music_session.creator))
|
|
mount.should be_nil
|
|
end
|
|
|
|
it "with no servers" do
|
|
IcecastServer.count.should == 0
|
|
mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator))
|
|
mount.should be_nil
|
|
end
|
|
|
|
it "with a server that has a mount template" do
|
|
server1.mount_template.should_not be_nil
|
|
mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator))
|
|
mount.should_not be_nil
|
|
mount.save!
|
|
end
|
|
|
|
it "with a server that already has an associated mount" do
|
|
server1.mount_template.should_not be_nil
|
|
mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator))
|
|
mount.save!
|
|
|
|
mount = IcecastMount.build_session_mount(public_music_session2.music_session, public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator))
|
|
mount.save!
|
|
server1.reload
|
|
server1.mounts.length.should == 2
|
|
end
|
|
|
|
it "picks a second server once the 1st has been chosen" do
|
|
server1.touch
|
|
|
|
mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator))
|
|
mount.listeners = 1 # affect the weight
|
|
mount.save!
|
|
|
|
server2.touch
|
|
|
|
mount = IcecastMount.build_session_mount(public_music_session2.music_session, public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator))
|
|
mount.save!
|
|
server1.reload
|
|
server1.mounts.length.should == 1
|
|
server2.reload
|
|
server2.mounts.length.should == 1
|
|
end
|
|
|
|
it "picks the 1st server again once the 2nd has higher weight" do
|
|
server1.touch
|
|
|
|
mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator))
|
|
mount.listeners = 1 # affect the weight
|
|
mount.save!
|
|
|
|
server2.touch
|
|
|
|
mount = IcecastMount.build_session_mount(public_music_session2.music_session, public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator))
|
|
mount.sourced = 1
|
|
mount.save!
|
|
|
|
mount = IcecastMount.build_session_mount(public_music_session3.music_session, public_music_session3, IcecastServer.find_best_server_for_user(public_music_session3.creator))
|
|
mount.listeners = 1
|
|
mount.save!
|
|
|
|
server1.reload
|
|
server1.mounts.length.should == 2
|
|
server2.reload
|
|
server2.mounts.length.should == 1
|
|
end
|
|
end
|
|
|
|
describe "source_changes are deleted if source_direction transitions" do
|
|
|
|
|
|
it "no change if same source_direction" do
|
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, source_direction: true)
|
|
change1 = FactoryGirl.create(:icecast_source_change, mount: mount, source_direction: true, success:true)
|
|
|
|
mount.source_changes.size.should == 1
|
|
mount.source_direction = true
|
|
mount.save!
|
|
mount = IcecastMount.find(mount.id)
|
|
mount.source_changes.size.should == 1
|
|
end
|
|
|
|
it "NOT deleted on transition to down" do
|
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, source_direction: true)
|
|
change1 = FactoryGirl.create(:icecast_source_change, mount: mount, source_direction: true, success:true)
|
|
|
|
mount.source_changes.size.should == 1
|
|
mount.source_direction = false
|
|
mount.save!
|
|
mount = IcecastMount.find(mount.id)
|
|
mount.source_changes.size.should == 1
|
|
end
|
|
|
|
it "not deleted on transition to up" do
|
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, source_direction: false)
|
|
change1 = FactoryGirl.create(:icecast_source_change, mount: mount, source_direction: true, success:true)
|
|
|
|
|
|
mount.source_changes.size.should == 1
|
|
mount.source_direction = true
|
|
mount.save!
|
|
mount.reload
|
|
mount.source_changes.size.should == 1
|
|
end
|
|
end
|
|
describe "state" do
|
|
|
|
def success_state(reason, detail = nil)
|
|
{success: true, reason: reason, detail:detail}
|
|
end
|
|
|
|
def fail_state(reason, detail = nil)
|
|
{success: false, reason: reason, detail:detail}
|
|
end
|
|
|
|
let(:sourced_mount) {FactoryGirl.create(:iceast_mount_with_music_session, sourced: true, sourced_needs_changing_at: nil, listeners: 1)}
|
|
let(:mount_needing_source) {FactoryGirl.create(:iceast_mount_with_music_session, sourced: false, sourced_needs_changing_at: Time.now, listeners: 1)}
|
|
|
|
it "happy sourced mount" do
|
|
sourced_mount.state.should == success_state('source_up')
|
|
end
|
|
|
|
it "just transitioned" do
|
|
mount_needing_source.state.should == success_state('transition_down', 'initial')
|
|
end
|
|
|
|
it "transition timeout" do
|
|
#change1 = FactoryGirl.create(:icecast_source_change, mount: mount, source_direction: true, success:true)
|
|
mount_needing_source.sourced_needs_changing_at = (APP_CONFIG.source_changes_missing_secs + 1).seconds.ago
|
|
mount_needing_source.save!
|
|
|
|
mount_needing_source.state.should == fail_state('transition_timeout_down', 'initial')
|
|
end
|
|
|
|
describe "client attempted transition and" do
|
|
let!(:change1) { FactoryGirl.create(:icecast_source_change, mount: mount_needing_source, source_direction: true, success:true) }
|
|
|
|
it "happy sourced mount" do
|
|
mount_needing_source.sourced = true
|
|
mount_needing_source.save!
|
|
mount_needing_source.state.should == success_state('source_up', change1.user.id)
|
|
end
|
|
|
|
it "succeeded recently in correct transition" do
|
|
mount_needing_source.state.should == success_state('transition_down')
|
|
end
|
|
|
|
it "succeeded a while ago in correct transition" do
|
|
change1.update_attribute(:created_at, (APP_CONFIG.source_changes_missing_secs + 1).seconds.ago)
|
|
mount_needing_source.state.should == fail_state('transition_timeout_down')
|
|
end
|
|
|
|
it "failed recently in correct transition" do
|
|
change1.success = false
|
|
change1.reason = 'bleh'
|
|
change1.source_direction = true
|
|
change1.save!
|
|
mount_needing_source.state.should == fail_state('source_wrong_down', change1.reason)
|
|
end
|
|
|
|
it "failed a while ago in correct transition" do
|
|
change1.success = false
|
|
change1.reason = 'bleh'
|
|
change1.source_direction = true
|
|
change1.created_at = (APP_CONFIG.source_changes_missing_secs + 1).seconds.ago
|
|
change1.save!
|
|
mount_needing_source.state.should == fail_state('source_wrong_down', change1.reason)
|
|
end
|
|
|
|
it "failed recently in wrong transition" do
|
|
change1.success = false
|
|
change1.reason = 'bleh'
|
|
change1.source_direction = false
|
|
change1.save!
|
|
mount_needing_source.state.should == fail_state('transition_down')
|
|
end
|
|
|
|
it "failed a while ago in wrong transition" do
|
|
change1.success = false
|
|
change1.reason = 'bleh'
|
|
change1.source_direction = false
|
|
change1.save!
|
|
mount_needing_source.sourced_needs_changing_at = (APP_CONFIG.source_changes_missing_secs + 1).seconds.ago
|
|
mount_needing_source.save!
|
|
mount_needing_source.state.should == fail_state('source_wrong_down', 'no_client')
|
|
end
|
|
end
|
|
end
|
|
end |