require "spec_helper" module Hexx describe Service do around :each do |example| class Test < Service attr_reader :on_something, :something private :on_something, :something end example.run Hexx.send :remove_const, :Test end let!(:described_class) { Test } it "extends Hexx::Dependable" do expect(described_class).to be_kind_of Hexx::Dependable end it "includes ActiveModel::Validations" do expect(subject).to be_kind_of ActiveModel::Validations end it "includes Whisper::Publisher" do expect(subject).to be_kind_of Wisper::Publisher end describe ".params" do it "returns an array" do expect(described_class.params).to be_kind_of Array end end describe ".new" do let(:value) { "text" } before { described_class.send :allow_params, :name } subject do described_class.new(name: value, wrong: "wrong").with_callbacks end it "stringifies params' keys" do expect(subject.params["name"]).to eq value expect(subject.params[:name]).to be_nil end it "whitelists params" do expect(subject.params["wrong"]).to be_nil end it "define individual params' getters" do expect(subject.name).to eq value end it "defines individual params' setters" do subject.name = :another_name expect(subject.name).to eq :another_name expect(subject.params["name"]).to eq :another_name end end describe "#with_callbacks" do context "without a prefix" do subject { described_class.new.with_callbacks } it "makes all private methods public" do expect(subject.respond_to? :something).to be_truthy expect(subject.respond_to? :on_something).to be_truthy expect { subject.something }.not_to raise_error expect { subject.on_something }.not_to raise_error end end context "with a prefix" do subject { described_class.new.with_callbacks prefix: :on } it "makes prefixed private methods public" do expect(subject.respond_to? :on_something).to be_truthy expect { subject.on_something }.not_to raise_error end it "doesn't make other private methods public" do expect(subject.respond_to? :something).to be_falsey expect { subject.something }.to raise_error end end end describe "#run" do it "is defined" do expect(subject).to respond_to :run end end describe "#params" do it "exists" do expect { subject.params }.not_to raise_error end end describe "#messages" do it "returns an array" do expect(subject.messages).to be_kind_of Array end end describe "helpers" do let!(:service) { Service.new } subject { service.with_callbacks } describe ".allow_params" do before { described_class.send :allow_params, :name } it "sets class params" do expect(described_class.params).to eq %w(name) end it "defines instance attributes" do expect(described_class.instance_methods).to be_include :name expect(described_class.instance_methods).to be_include :name= end end describe "#validate!" do it "passes when service is valid" do expect { subject.validate! }.not_to raise_error end it "fails when service is invalid" do allow(service).to receive(:valid?).and_return false expect { subject.validate! } .to raise_error { Service::Invalid.new(service) } end end describe "#t" do let(:scope) { %w(activemodel messages models hexx/service) } let(:traslation) { I18n.t(:text, scope: scope, name: "name") } it "translates symbols in the service scope" do expect(subject.t(:text, name: "name")).to eq traslation end it "doesn't translate the string" do expect(subject.t("text")).to eq "text" end end describe "#messages=" do it "sets #messages" do expect { subject.messages = ["text"] } .to change { subject.messages }.to ["text"] end end describe "#add_message" do before { subject.add_message :info, :text } let(:message) { subject.messages.first } it "adds a new message" do expect(message).to be_kind_of Service::Message expect(message.type).to eq "info" end it "translates a symbol" do translation = subject.t :text expect(message.text).to eq translation end end describe "#on_error" do let!(:message) { Service::Message.new(type: "error", text: "error") } it "fails with Service::Invalid" do expect { subject.on_error [message] } .to raise_error { Service::Invalid } end it "adds errors to the service" do begin; subject.on_error [message]; rescue; end expect(subject.errors.messages).to eq(base: ["error"]) end end describe "#escape" do let(:listener) { double "listener" } before { subject.subscribe listener } it "yields a block" do value = "Hello!" result = subject.escape { value } expect(result).to eq value end context "when a block raises Service::Invalid error" do let!(:exception) { Service::Invalid.new(Service.new) } it "re-raises the exception" do expect { subject.escape { fail exception } } .to raise_error { exception } end end context "when a block raises StandardError" do let(:run) { subject.escape { fail "text" } } let(:message) { Service::Message.new type: "error", text: "text" } it "re-raises the Service::Invalid" do expect { run }.to raise_error { Service::Invalid } end it "adds error to the messages" do begin run rescue => err expect(err.messages).to eq [message] end end end end describe "#run_service" do let!(:other) { double "other", subscribe: nil, run: nil } before { allow(described_class).to receive(:new).and_return other } it "if wrong class given it fails with TypeError" do expect { subject.run_service String, :on_string } .to raise_error { TypeError } end it "creates a service object" do options = { "name" => "some name" } expect(described_class).to receive(:new).with options subject.run_service described_class, :on_service, options end it "subscribes self for the service notifications" do expect(other).to receive(:subscribe) do |listener, params| expect(listener).to eq subject.with_callbacks expect(params).to eq(prefix: :on_service) end subject.run_service described_class, :on_service end it "runs the service object after subscriptions" do expect(other).to receive(:subscribe).ordered expect(other).to receive(:run).ordered subject.run_service described_class, :on_service end end end end end