require "test_helper"

describe "Metric via playground" do
  it "knows all loaded metrics" do
    metric "Yawns/sec", "Cheers/sec"
    assert Vanity.playground.metrics.keys.include?(:yawns_sec)
    assert Vanity.playground.metrics.keys.include?(:cheers_sec)
  end

  it "loads metric definitions" do
    File.write("tmp/experiments/metrics/yawns_sec.rb", <<-RUBY)
        metric "Yawns/sec" do
          def xmts
            "x"
          end
        end
    RUBY
    assert_equal "x", Vanity.playground.metric(:yawns_sec).xmts
  end

  it "bubbles up loaded metrics" do
    File.write("tmp/experiments/metrics/yawns_sec.rb", "fail 'yawn!'")
    assert_raises NameError do
      Vanity.playground.metric(:yawns_sec)
    end
  end

  it "map identifier from file name" do
    File.write("tmp/experiments/metrics/yawns_sec.rb", <<-RUBY)
        metric "yawns/hour" do
        end
    RUBY
    assert Vanity.playground.metric(:yawns_sec)
  end

  it "fails tracking unknown metric" do
    assert_raises NameError do
      Vanity.playground.track! :yawns_sec
    end
  end

  it "reloading metrics" do
    metric "Yawns/sec", "Cheers/sec"
    Vanity.playground.metric(:yawns_sec)
    Vanity.playground.metric(:cheers_sec)
    assert_equal 2, Vanity.playground.metrics.size
    metrics = Vanity.playground.metrics.values
    Vanity.playground.reload!
    assert_equal 0, Vanity.playground.metrics.size
    refute_equal metrics, Vanity.playground.metrics.values
  end

  it "ignores undefined metrics in database" do
    metric "Yawns/sec"
    Vanity.playground.reload!
    assert Vanity.playground.metrics.empty?
  end

  it "bootstraps the metric" do
    File.write("tmp/experiments/metrics/yawns_sec.rb", <<-RUBY)
        metric "yawns/hour" do
        end
    RUBY
    Vanity.playground.track!(:yawns_sec)
    Vanity.playground.track!(:yawns_sec)
    assert Vanity.playground.connection.get_metric_last_update_at(:yawns_sec)
  end
end

describe "Metric tracking" do
  it "disabled when metrics are disabled" do
    not_collecting!
    metric "Yawns/sec", "Cheers/sec"
    Vanity.playground.track! :yawns_sec
    Vanity.playground.track! :cheers_sec
  end

  it "can count" do
    metric "Yawns/sec", "Cheers/sec"
    4.times { Vanity.playground.track! :yawns_sec }
    2.times { Vanity.playground.track! :cheers_sec }
    yawns = Vanity.playground.metric(:yawns_sec).values(today, today).first
    cheers = Vanity.playground.metric(:cheers_sec).values(today, today).first
    assert yawns == 2 * cheers
  end

  it "can tell the time" do
    metric "Yawns/sec"
    Timecop.freeze((today - 4).to_time) { 4.times { Vanity.playground.track! :yawns_sec } }
    Timecop.freeze((today - 2).to_time) { 2.times { Vanity.playground.track! :yawns_sec } }
    1.times { Vanity.playground.track! :yawns_sec }
    boredom = Vanity.playground.metric(:yawns_sec).values(today - 5, today)
    assert_equal [0, 4, 0, 2, 0, 1], boredom
  end

  it "with no value" do
    metric "Yawns/sec", "Cheers/sec", "Looks"
    Vanity.playground.track! :yawns_sec, 0
    Vanity.playground.track! :cheers_sec
    assert_equal 0, Vanity.playground.metric(:yawns_sec).values(today, today).sum
    assert_equal 1, Vanity.playground.metric(:cheers_sec).values(today, today).sum
  end

  it "with count" do
    metric "Yawns/sec"
    Timecop.freeze((today - 4).to_time) { Vanity.playground.track! :yawns_sec, 4 }
    Timecop.freeze((today - 2).to_time) { Vanity.playground.track! :yawns_sec, 2 }
    Vanity.playground.track! :yawns_sec
    boredom = Vanity.playground.metric(:yawns_sec).values(today - 5, today)
    assert_equal [0, 4, 0, 2, 0, 1], boredom
  end

  it "runs hook" do
    metric "Many Happy Returns"
    total = 0
    Vanity.playground.metric(:many_happy_returns).hook do |metric_id, timestamp, count|
      assert_equal :many_happy_returns, metric_id
      assert_in_delta Time.now.to_i, timestamp.to_i, 1
      total += count
    end
    Vanity.playground.track! :many_happy_returns, 6
    assert_equal 6, total
  end

  it "doesn't runs hook when metrics disabled" do
    not_collecting!
    metric "Many Happy Returns"
    total = 0
    Vanity.playground.metric(:many_happy_returns).hook do |_metric_id, _timestamp, count|
      total += count
    end
    Vanity.playground.track! :many_happy_returns, 6
    assert_equal 0, total
  end

  it "runs multiple hooks" do
    metric "Many Happy Returns"
    returns = 0
    Vanity.playground.metric(:many_happy_returns).hook { returns += 1 }
    Vanity.playground.metric(:many_happy_returns).hook { returns += 1 }
    Vanity.playground.metric(:many_happy_returns).hook { returns += 1 }
    Vanity.playground.track! :many_happy_returns
    assert_equal 3, returns
  end

  it "destroy wipes metrics" do
    metric "Many Happy Returns"
    Vanity.playground.track! :many_happy_returns, 3
    assert_equal [3], Vanity.playground.metric(:many_happy_returns).values(today, today)
    Vanity.playground.metric(:many_happy_returns).destroy!
    assert_equal [0], Vanity.playground.metric(:many_happy_returns).values(today, today)
  end
end

describe "Metric name" do
  it "can be whatever" do
    File.write("tmp/experiments/metrics/yawns_sec.rb", <<-RUBY)
        metric "Yawns per second" do
        end
    RUBY
    assert_equal "Yawns per second", Vanity.playground.metric(:yawns_sec).name
  end
end

describe "Metric description" do
  it "metric with description" do
    File.write("tmp/experiments/metrics/yawns_sec.rb", <<-RUBY)
        metric "Yawns/sec" do
          description "Am I that boring?"
        end
    RUBY
    assert_equal "Am I that boring?", Vanity::Metric.description(Vanity.playground.metric(:yawns_sec))
  end

  it "metric without description" do
    File.write("tmp/experiments/metrics/yawns_sec.rb", <<-RUBY)
        metric "Yawns/sec" do
        end
    RUBY
    assert_nil Vanity::Metric.description(Vanity.playground.metric(:yawns_sec))
  end

  it "metric with no method description" do
    metric = Object.new
    assert_nil Vanity::Metric.description(metric)
  end
end

describe "Metric bounds" do
  it "metric with bounds" do
    File.write("tmp/experiments/metrics/sky_is_limit.rb", <<-RUBY)
        metric "Sky is limit" do
          def bounds
            [6,12]
          end
        end
    RUBY
    assert_equal [6, 12], Vanity::Metric.bounds(Vanity.playground.metric(:sky_is_limit))
  end

  it "metric without bounds" do
    metric "Sky is limit"
    assert_equal [nil, nil], Vanity::Metric.bounds(Vanity.playground.metric(:sky_is_limit))
  end

  it "metric with no method bounds" do
    metric = Object.new
    assert_equal [nil, nil], Vanity::Metric.bounds(metric)
  end
end

describe "Metric last_update_at" do
  it "for new metric" do
    metric "Coolness"
    metric = Vanity.playground.metric(:coolness)
    assert_nil metric.last_update_at
  end

  it "with data point" do
    metric "Coolness"
    metric = Vanity.playground.metric(:coolness)
    metric.track!
    Timecop.freeze Time.now + 1.day do
      metric.track!
    end
    assert_in_delta metric.last_update_at.to_i, (Time.now + 1.day).to_i, 1
  end
end

describe "Metric data" do
  it "explicit dates" do
    metric "Yawns/sec"
    Timecop.freeze((today - 4).to_time) { Vanity.playground.track! :yawns_sec, 4 }
    Timecop.freeze((today - 2).to_time) { Vanity.playground.track! :yawns_sec, 2 }
    Vanity.playground.track! :yawns_sec
    boredom = Vanity::Metric.data(Vanity.playground.metric(:yawns_sec), Date.today - 5, Date.today)
    assert_equal [[today - 5, 0], [today - 4, 4], [today - 3, 0], [today - 2, 2], [today - 1, 0], [today, 1]], boredom
  end

  it "start date only" do
    metric "Yawns/sec"
    Timecop.freeze((today - 4).to_time) { Vanity.playground.track! :yawns_sec, 4 }
    Timecop.freeze((today - 2).to_time) { Vanity.playground.track! :yawns_sec, 2 }
    Vanity.playground.track! :yawns_sec
    boredom = Vanity::Metric.data(Vanity.playground.metric(:yawns_sec), Date.today - 4)
    assert_equal [[today - 4, 4], [today - 3, 0], [today - 2, 2], [today - 1, 0], [today, 1]], boredom
  end

  it "start date and duration" do
    metric "Yawns/sec"
    Timecop.freeze((today - 4).to_time) { Vanity.playground.track! :yawns_sec, 4 }
    Timecop.freeze((today - 2).to_time) { Vanity.playground.track! :yawns_sec, 2 }
    Vanity.playground.track! :yawns_sec
    boredom = Vanity::Metric.data(Vanity.playground.metric(:yawns_sec), 5)
    assert_equal [[today - 4, 4], [today - 3, 0], [today - 2, 2], [today - 1, 0], [today, 1]], boredom
  end

  it "no data" do
    metric "Yawns/sec"
    boredom = Vanity::Metric.data(Vanity.playground.metric(:yawns_sec))
    assert_equal 90, boredom.size
    assert_equal [today - 89, 0], boredom.first
    assert_equal [today, 0], boredom.last
  end

  it "using custom values method" do
    File.write("tmp/experiments/metrics/hours_in_day.rb", <<-RUBY)
        metric "Hours in day" do
          def values(from, to)
            (from..to).map { |d| 24 }
          end
        end
    RUBY
    data = Vanity::Metric.data(Vanity.playground.metric(:hours_in_day))
    assert_equal [24] * 90, data.map(&:last)
  end
end
