require "spec_helper"
require "teaspoon/runner"
require "teaspoon/exceptions"
require "teaspoon/coverage"

describe Teaspoon::Runner do

  before do
    allow(Teaspoon.configuration).to receive(:formatters).and_return([])
  end

  describe "#initialize" do

    it "sets @suite_name and @failure_count" do
      subject = Teaspoon::Runner.new(:foo)
      expect(subject.instance_variable_get(:@suite_name)).to eq(:foo)
      expect(subject.failure_count).to eq(0)
    end

    it "instantiates formatters based on configuration" do
      allow(Teaspoon.configuration).to receive(:formatters).and_return(["dot", "xml"])
      Teaspoon::Formatters::XmlFormatter = Class.new do
        def initialize(_suite_name = :default, _output_file = nil) end
      end
      expect(subject.instance_variable_get(:@formatters)[0]).to be_a(Teaspoon::Formatters::DotFormatter)
      expect(subject.instance_variable_get(:@formatters)[1]).to be_a(Teaspoon::Formatters::XmlFormatter)
    end

    it "raises a Teaspoon::UnknownFormatter exception when a formatter isn't found" do
      allow(Teaspoon.configuration).to receive(:formatters).and_return(["bar"])
      expect { Teaspoon::Runner.new(:foo) }.to raise_error Teaspoon::UnknownFormatter, "Unknown formatter: \"bar\""
    end

  end

  describe "#process" do

    let(:formatter) { double }
    let(:coverage) { double(generate_reports: nil, check_thresholds: nil) }

    before do
      subject.instance_variable_set(:@formatters, [formatter])
    end

    it "notifies formatters when it understands the log" do
      expect(formatter).to receive(:foo)
      expect(formatter).to_not receive(:bar)
      subject.process('{"_teaspoon":true,"type":"foo"}')
      subject.process('{"_teaspoon":false,"type":"bar"}')
    end

    it "notifies formatters of console output when it doesn't understand the log" do
      expect(formatter).to receive(:console).with("_line_")
      expect(subject).to receive(:result_from_line).and_return(false)
      subject.process("_line_")
    end

    it "handles bad json" do
      expect(formatter).to receive(:console).with("{bad: true}")
      subject.process("{bad: true}")
    end

    it "handles json when it's not intended for it" do
      expect(formatter).to receive(:console).with('{"good": true}')
      subject.process('{"good": true}')
    end

    it "keeps a count of errors" do
      subject.process('{"_teaspoon":true,"type":"spec"}')
      subject.process('{"_teaspoon":true,"type":"spec", "status": "passed"}')
      subject.process('{"_teaspoon":true,"type":"spec", "status": "pending"}')
      subject.process('{"_teaspoon":true,"type":"error"}')
      subject.process('{"_teaspoon":true,"type":"results"}')
      expect(subject.failure_count).to be(1)
    end

    describe "with an exception" do

      it "notifies itself, and raises Teaspoon::RunnerException" do
        expect(subject).to receive(:on_exception).and_call_original
        expect { subject.process('{"_teaspoon":true,"type":"exception","message":"_message_"}') }.to raise_error Teaspoon::RunnerException, "_message_"
      end

    end

    describe "with a result" do

      before do
        allow(Teaspoon::Coverage).to receive(:new).and_return(coverage)
      end

      it "notifies itself" do
        expect(subject).to receive(:on_result)
        subject.process('{"_teaspoon":true,"type":"result"}')
      end

      it "resolves coverage" do
        expect(Teaspoon.configuration).to receive(:use_coverage).twice.and_return("_coverage_config_")
        expect(Teaspoon::Coverage).to receive(:new).with(:default, "_coverage_config_", "_coverage_").and_return(coverage)
        expect(coverage).to receive(:generate_reports).and_yield("_generated_reports_")
        expect(coverage).to receive(:check_thresholds).and_yield("_threshold_failures_")
        expect(subject).to receive(:notify_formatters).once.with("coverage", "_generated_reports_")
        expect(subject).to receive(:notify_formatters).once.with("threshold_failure", "_threshold_failures_")
        expect(subject).to receive(:notify_formatters).exactly(2).times.and_call_original
        subject.process('{"_teaspoon":true,"type":"result","coverage":"_coverage_"}')
        expect(subject.failure_count).to eq(1)
      end

    end

  end

end
