require_relative '../spec_helper' module RUPNP module CP describe RemoteDevice do include EM::SpecHelper DESCRIPTIONS = ['this is not a UPnP description', 'UPnP', '', '', '', '09', '19', '19'] let(:location) { 'http://127.0.0.1:1234/root_description.xml' } let(:uuid) { UUID.generate } let(:max_age) { 1800 } let(:notification) { { 'cache-control' => "max-age=#{max_age}", 'date' => Time.now.strftime("%a, %d %b %Y %H:%M:%S %Z"), 'ext' => '', 'location' => location, 'server' => 'OS/1.0 UPnP/1.1 TEST/1.0', 'st' => 'upnp:rootdevice', 'usn' => "uuid:#{uuid}::upnp:rootdevice", 'bootid.upnp.org' => 10, 'configid.upnp.org' => 23, } } let(:rd) { RemoteDevice.new(double('control_point'), notification)} context '#fetch' do it "should fail when notification has no BOOTID.UPNP.ORG field" do notification.delete 'bootid.upnp.org' em do rd.errback do |dev, msg| expect(dev).to eq(rd) expect(msg).to match(/no BOOTID/) done end rd.callback { fail 'RemoteDevice#fetch should not work' } rd.fetch end end it "should accept headers without BOOTID for UPnP 1.0 response" do notification.delete 'bootid.upnp.org' notification['server'] = 'OS/1.0 UPnP/1.0 TEST/1.0' em do stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.0 TEST/1.0'}, :body => generate_device_description(uuid)) rd.errback { fail 'RemoteDevice#fetch should work' } rd.callback { done } rd.fetch end end it "should fail when location is unreachable" do em do stub_request(:get, location).to_timeout rd.errback do |dev, msg| expect(dev).to eq(rd) expect(msg).to match(/Failed getting description/) done end rd.callback { fail 'RemoteDevice#fetch should not work' } rd.fetch end end it "should fail when description header is not a UPnP 1.x response" do desc = generate_device_description(uuid) em do stub_request(:get, location). to_return(:body => generate_device_description(uuid), :headers => { 'SERVER' => 'Linux/1.2 Apache/1.0' }, :body => desc) rd.errback do |dev, msg| expect(dev).to eq(rd) expect(msg).to match(/Failed getting description/) stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/0.9 TEST/1.0'}, :body => desc) rd.errback do |dev, msg| expect(dev).to eq(rd) expect(msg).to match(/Failed getting description/) done end rd.fetch end rd.callback { fail 'RemoteDevice#fetch should not work' } rd.fetch end end it "should fail when description does not conform to UPnP spec" do DESCRIPTIONS.each do |desc| em do stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.1 TEST/1.0'}, :body => desc) rd.errback do |dev, msg| expect(dev).to eq(rd) expect(msg).to match(/Bad description/) done end rd.callback { fail 'RemoteDevice#fetch should not work' } rd.fetch end end end it "should fetch its description" do em do stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.1 TEST/1.0'}, :body => generate_device_description(uuid)) rd.errback { fail 'RemoteDevice#fetch should work' } rd.callback do done end rd.fetch end end it "should extract services if any" do dev_desc = generate_device_description(uuid, :device_type => 'MediaServer') scpd = generate_scpd em do stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.1 TEST/1.0'}, :body => dev_desc) stub_request(:get, 'http://127.0.0.1:1234/cd/description.xml'). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.1 TEST/1.0'}, :body => scpd) stub_request(:get, 'http://127.0.0.1:1234/cd/control'). to_return(:status => 404) rd.errback { |d, msg| fail msg } rd.callback do expect(rd.services).to have(1).item done end rd.fetch end end it "should not fail when a service cannot be extracted" do dev_desc = generate_device_description(uuid, :device_type => 'MediaServer') em do stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.1 TEST/1.0'}, :body => dev_desc) stub_request(:get, 'http://127.0.0.1:1234/cd/description.xml'). to_return(:status => 404) rd.errback { |d, msg| fail msg } rd.callback do expect(rd.services).to have(0).item done end rd.fetch end end end context "#update" do it 'should update expiration date' do em do stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.1 TEST/1.0'}, :body => generate_device_description(uuid)) rd.errback { fail 'RemoteDevice#fetch should work' } rd.callback do not2 = notification.dup expiration_old = Time.parse(notification['date']) + max_age not2['date'] = (Time.now + 5).strftime("%a, %d %b %Y %H:%M:%S %Z") expiration_new = Time.parse(not2['date']) + max_age expect(not2['date']).not_to eq(notification['date']) expect { rd.update(not2) }.to change { rd.expiration }. from(expiration_old).to(expiration_new) done end rd.fetch end end it 'should update BOOTID' do em do stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.1 TEST/1.0' }, :body => generate_device_description(uuid)) rd.errback { fail 'RemoteDevice#fetch should work' } rd.callback do not2 = notification.merge('nextbootid.upnp.org' => 15) expect { rd.update(not2) }.to change{ rd.boot_id }.from(10).to(15) done end rd.fetch end end it 'should update CONFIGID.UPNP.ORG' do em do stub_request(:get, location). to_return(:headers => { 'SERVER' => 'OS/1.0 UPnP/1.1 TEST/1.0' }, :body => generate_device_description(uuid)) rd.errback { fail 'RemoteDevice#fetch should work' } rd.callback do not2 = notification.merge('configid.upnp.org' => 47) expect { rd.update(not2) }.to change{ rd.config_id }. from(23).to(47) done end rd.fetch end end end end end end