require "spec_helper" describe Lita::Robot, lita: true do subject { described_class.new(registry) } before { registry.register_adapter(:shell, Lita::Adapters::Shell) } it "triggers a loaded event after initialization" do expect_any_instance_of(described_class).to receive(:trigger).with(:loaded, room_ids: []) subject end context "when there are previously persisted rooms" do before { %w(#foo #bar).each { |id| Lita.redis.sadd("persisted_rooms", id) } } it "receives the room_ids in the payload" do expect_any_instance_of(described_class).to receive(:trigger).with( :loaded, room_ids: %w(#foo #bar).sort, ) subject end end it "can have its name changed" do subject.name = "Bongo" expect(subject.name).to eq("Bongo") end it "can have its mention name changed" do subject.mention_name = "wongo" expect(subject.mention_name).to eq("wongo") end it "exposes Adapter#mention_format" do expect(subject.mention_format(subject.mention_name)).to eq("Lita:") end it "exposes Adapter#roster" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:roster) subject.roster(instance_double("Lita::Room")) end it "exposes Adapter#chat_service" do expect { subject.chat_service }.not_to raise_error end context "with registered handlers" do let(:handler1) { Class.new(Lita::Handler) { namespace :test } } let(:handler2) { Class.new(Lita::Handler) { namespace :test } } before do registry.register_handler(handler1) registry.register_handler(handler2) end describe "#receive" do it "dispatches messages to every registered handler" do expect(handler1).to receive(:dispatch).with(subject, "foo") expect(handler2).to receive(:dispatch).with(subject, "foo") subject.receive("foo") end end describe "#trigger" do it "triggers the supplied event on all registered handlers" do expect(handler1).to receive(:trigger).with(subject, :foo, bar: "baz") expect(handler2).to receive(:trigger).with(subject, :foo, bar: "baz") subject.trigger(:foo, bar: "baz") end end end describe "#run" do let(:thread) { instance_double("Thread", :abort_on_exception= => true, join: nil) } # For some reason RSpec is no longer capturing tracebacks to # stderr, so we do it manually to avoid cluttering the test output. around do |example| stderr = $stderr $stderr = StringIO.new example.run $stderr = stderr end before do allow_any_instance_of(Lita::Adapters::Shell).to receive(:run) allow_any_instance_of(Puma::Server).to receive(:run) allow_any_instance_of(Puma::Server).to receive(:add_tcp_listener) allow(Thread).to receive(:new) do |&block| block.call thread end end it "starts the adapter" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:run) subject.run end it "starts the web server" do expect_any_instance_of(Puma::Server).to receive(:run) subject.run end it "rescues interrupts and calls #shut_down" do allow_any_instance_of( Lita::Adapters::Shell ).to receive(:run).and_raise(Interrupt) expect_any_instance_of(Lita::Adapters::Shell).to receive(:shut_down) subject.run end it "logs and quits if the specified adapter can't be found" do registry.config.robot.adapter = :does_not_exist expect(Lita.logger).to receive(:fatal).with(/Unknown adapter/) expect { subject.run }.to raise_error(SystemExit) end it "logs and aborts if the web server's port is in use" do allow_any_instance_of(Puma::Server).to receive(:add_tcp_listener).and_raise(Errno::EADDRINUSE) expect(Lita.logger).to receive(:fatal).with(/web server/) expect { subject.run }.to raise_error(SystemExit) end it "logs and aborts if the web server's port is privileged" do allow_any_instance_of(Puma::Server).to receive(:add_tcp_listener).and_raise(Errno::EACCES) expect(Lita.logger).to receive(:fatal).with(/web server/) expect { subject.run }.to raise_error(SystemExit) end end describe "#join" do before do allow_any_instance_of(Lita::Adapters::Shell).to receive(:join) end context "when a Room object exists" do let!(:room) { Lita::Room.create_or_update(1, name: "#lita.io") } it "passes the room ID to the adapter when a string argument is provided" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:join).with("1") subject.join("#lita.io") end it "passes the room ID to the adapter when a Room argument is provided" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:join).with("1") subject.join(room) end it "adds the room ID to the persisted list" do subject.join("#lita.io") expect(subject.persisted_rooms).to include("1") end end context "when no Room object exists" do it "delegates to the adapter with the raw argument" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:join).with("#lita.io") subject.join("#lita.io") end end end describe "#part" do before do allow_any_instance_of(Lita::Adapters::Shell).to receive(:join) allow_any_instance_of(Lita::Adapters::Shell).to receive(:part) end context "when a Room object exists" do let!(:room) { Lita::Room.create_or_update(1, name: "#lita.io") } it "passes the room ID to the adapter when a string argument is provided" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:part).with("1") subject.part("#lita.io") end it "passes the room ID to the adapter when a Room argument is provided" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:part).with("1") subject.part(room) end it "removes the room ID from the persisted list" do subject.join("#lita.io") subject.part("#lita.io") expect(subject.persisted_rooms).not_to include("1") end end context "when no Room object exists" do it "delegates to the adapter with the raw argument" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:part).with("#lita.io") subject.part("#lita.io") end end end describe "#send_message" do let(:source) { instance_double("Lita::Source") } it "delegates to the adapter" do expect_any_instance_of( Lita::Adapters::Shell ).to receive(:send_messages).with( source, %w(foo bar) ) subject.send_messages(source, "foo", "bar") end end describe "#send_message_with_mention" do let(:user) { instance_double("Lita::User", mention_name: "carl") } let(:source) { instance_double("Lita::Source", private_message?: false, user: user) } it "calls #send_message with the strings, prefixed with the user's mention name" do allow_any_instance_of(Lita::Adapters::Shell).to receive(:mention_format).with( "carl" ).and_return("carl:") expect_any_instance_of(Lita::Adapters::Shell).to receive(:send_messages).with( source, ["carl: foo", "carl: bar"] ) subject.send_messages_with_mention(source, "foo", "bar") end it "strips whitespace from both sides of the formatted mention name" do allow_any_instance_of(Lita::Adapters::Shell).to receive(:mention_format).with( "carl" ).and_return(" carl: ") expect_any_instance_of(Lita::Adapters::Shell).to receive(:send_messages).with( source, ["carl: foo", "carl: bar"] ) subject.send_messages_with_mention(source, "foo", "bar") end it "calls #send_message directly if the original message was sent privately" do allow(source).to receive(:private_message?).and_return(true) expect_any_instance_of(Lita::Adapters::Shell).to receive(:send_messages).with( source, %w(foo bar) ) subject.send_messages_with_mention(source, "foo", "bar") end end describe "#set_topic" do let(:source) { instance_double("Lita::Source") } it "delegates to the adapter" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:set_topic).with( source, "New topic" ) subject.set_topic(source, "New topic") end end describe "#shut_down" do before { allow_any_instance_of(Lita::Adapters::Shell).to receive(:puts) } it "gracefully stops the adapter" do expect_any_instance_of(Lita::Adapters::Shell).to receive(:shut_down) subject.shut_down end it "triggers events for shut_down_started and shut_down_complete" do expect(subject).to receive(:trigger).with(:shut_down_started).ordered expect(subject).to receive(:trigger).with(:shut_down_complete).ordered subject.shut_down end end end