# frozen_string_literal: true

RSpec.describe Report do
  let(:user) { Fabricate(:user) }
  let(:category_1) { Fabricate(:category, user: user) }
  let(:category_2) { Fabricate(:category, parent_category: category_1, user: user) } # id: 2
  let(:category_3) { Fabricate(:category, user: user) }

  shared_examples "no data" do
    context "with no data" do
      it "returns an empty report" do
        expect(report.data).to be_blank
      end
    end
  end

  shared_examples "category filtering" do
    it "returns the filtered data" do
      expect(report.total).to eq 1
    end
  end

  shared_examples "category filtering on subcategories" do
    it "returns the filtered data" do
      expect(report.total).to eq(1)
    end
  end

  shared_examples "with data x/y" do
    it "returns today's data" do
      expect(report.data.select { |v| v[:x].today? }).to be_present
    end

    it "returns correct data for period" do
      expect(report.data[0][:y]).to eq 3
    end

    it "returns total" do
      expect(report.total).to eq 4
    end

    it "returns previous 30 day’s data" do
      expect(report.prev30Days).to be_present
    end
  end

  describe "counting" do
    describe "requests" do
      subject(:json) { Report.find("http_total_reqs").as_json }

      before do
        freeze_time_safe

        # today, an incomplete day:
        application_requests = [
          {
            date: 0.days.ago.to_time,
            req_type: ApplicationRequest.req_types["http_total"],
            count: 1,
          },
        ]

        # 60 complete days:
        30.times.each do |i|
          application_requests.concat(
            [
              {
                date: (i + 1).days.ago.to_time,
                req_type: ApplicationRequest.req_types["http_total"],
                count: 10,
              },
            ],
          )
        end
        30.times.each do |i|
          application_requests.concat(
            [
              {
                date: (31 + i).days.ago.to_time,
                req_type: ApplicationRequest.req_types["http_total"],
                count: 100,
              },
            ],
          )
        end

        ApplicationRequest.insert_all(application_requests)
      end

      it "counts the correct records" do
        expect(json[:data].size).to eq(31) # today and 30 full days
        expect(json[:data][0..-2].sum { |d| d[:y] }).to eq(300)
        expect(json[:prev30Days]).to eq(3000)
      end
    end

    describe "topics" do
      before do
        Report.clear_cache
        freeze_time_safe
        user = Fabricate(:user)
        topics =
          ((0..32).to_a + [60, 61, 62, 63]).map do |i|
            date = i.days.ago
            {
              user_id: user.id,
              last_post_user_id: user.id,
              title: "topic #{i}",
              category_id: SiteSetting.uncategorized_category_id,
              bumped_at: date,
              created_at: date,
              updated_at: date,
            }
          end
        Topic.insert_all(topics)
      end

      it "counts the correct records" do
        json = Report.find("topics").as_json
        expect(json[:data].size).to eq(31)
        expect(json[:prev30Days]).to eq(3)

        # lets make sure we can ask for the correct options for the report
        json =
          Report.find(
            "topics",
            start_date: 5.days.ago.beginning_of_day,
            end_date: 1.day.ago.end_of_day,
            facets: [:prev_period],
          ).as_json

        expect(json[:prev_period]).to eq(5)
        expect(json[:data].length).to eq(5)
        expect(json[:prev30Days]).to eq(nil)
      end
    end
  end

  describe "visits report" do
    let(:report) { Report.find("visits") }

    include_examples "no data"

    context "with visits" do
      let(:user) { Fabricate(:user) }

      it "returns a report with data" do
        freeze_time_safe
        user.user_visits.create(visited_at: 1.hour.from_now)
        user.user_visits.create(visited_at: 1.day.ago)
        user.user_visits.create(visited_at: 2.days.ago, mobile: true)
        user.user_visits.create(visited_at: 45.days.ago)
        user.user_visits.create(visited_at: 46.days.ago, mobile: true)

        expect(report.data).to be_present
        expect(report.data.count).to eq(3)
        expect(report.data.select { |v| v[:x].today? }).to be_present
        expect(report.prev30Days).to eq(2)
      end
    end
  end

  describe "mobile visits report" do
    let(:report) { Report.find("mobile_visits") }

    include_examples "no data"

    context "with visits" do
      let(:user) { Fabricate(:user) }

      it "returns a report with data" do
        freeze_time_safe
        user.user_visits.create(visited_at: 1.hour.from_now)
        user.user_visits.create(visited_at: 2.days.ago, mobile: true)
        user.user_visits.create(visited_at: 45.days.ago)
        user.user_visits.create(visited_at: 46.days.ago, mobile: true)

        expect(report.data).to be_present
        expect(report.data.count).to eq(1)
        expect(report.data.select { |v| v[:x].today? }).not_to be_present
        expect(report.prev30Days).to eq(1)
      end
    end
  end

  %i[signup topic post flag like email].each do |arg|
    describe "#{arg} report" do
      pluralized = arg.to_s.pluralize

      let(:report) { Report.find(pluralized) }

      context "with no #{pluralized}" do
        it "returns an empty report" do
          expect(report.data).to be_blank
        end
      end

      context "with #{pluralized}" do
        before(:each) do
          freeze_time_safe

          if arg == :flag
            user = Fabricate(:user, refresh_auto_groups: true)
            topic = Fabricate(:topic, user: user)
            builder = ->(dt) do
              PostActionCreator.create(
                user,
                Fabricate(:post, topic: topic, user: user),
                :spam,
                created_at: dt,
              )
            end
          elsif arg == :signup
            builder = ->(dt) { Fabricate(:user, created_at: dt) }
          else
            user = Fabricate(:user)
            factories = { email: :email_log }
            builder = ->(dt) { Fabricate(factories[arg] || arg, created_at: dt, user: user) }
          end

          [
            DateTime.now,
            1.hour.ago,
            1.hour.ago,
            1.day.ago,
            2.days.ago,
            30.days.ago,
            35.days.ago,
          ].each(&builder)
        end

        it "returns today's, total and previous 30 day's data" do
          expect(report.data.select { |v| v[:x].today? }).to be_present
          expect(report.total).to eq 7
          expect(report.prev30Days).to be_present
        end
      end
    end
  end

  %i[
    http_total
    http_2xx
    http_background
    http_3xx
    http_4xx
    http_5xx
    page_view_crawler
    page_view_logged_in
    page_view_anon
  ].each do |request_type|
    describe "#{request_type} request reports" do
      let(:report) do
        Report.find("#{request_type}_reqs", start_date: 10.days.ago.to_time, end_date: Time.now)
      end

      context "with no #{request_type} records" do
        it "returns an empty report" do
          expect(report.data).to be_blank
        end
      end

      context "with #{request_type}" do
        before do
          freeze_time_safe
          application_requests = [
            {
              date: 35.days.ago.to_time,
              req_type: ApplicationRequest.req_types[request_type.to_s],
              count: 35,
            },
            {
              date: 7.days.ago.to_time,
              req_type: ApplicationRequest.req_types[request_type.to_s],
              count: 8,
            },
            { date: Time.now, req_type: ApplicationRequest.req_types[request_type.to_s], count: 1 },
            {
              date: 1.day.ago.to_time,
              req_type: ApplicationRequest.req_types[request_type.to_s],
              count: 2,
            },
            {
              date: 2.days.ago.to_time,
              req_type: ApplicationRequest.req_types[request_type.to_s],
              count: 3,
            },
          ]
          ApplicationRequest.insert_all(application_requests)
        end

        it "returns a report with data" do
          # expected number of records
          expect(report.data.count).to eq 4

          # sorts the data from oldest to latest dates
          expect(report.data[0][:y]).to eq(8) # 7 days ago
          expect(report.data[1][:y]).to eq(3) # 2 days ago
          expect(report.data[2][:y]).to eq(2) # 1 day ago
          expect(report.data[3][:y]).to eq(1) # today

          # today's data
          expect(report.data.find { |value| value[:x] == Date.today }).to be_present

          # total data
          expect(report.total).to eq 49

          #previous 30 days of data
          expect(report.prev30Days).to eq 35
        end
      end
    end
  end

  describe "page_view_total_reqs" do
    before do
      freeze_time(Time.now.at_midnight)
      Theme.clear_default!
    end

    let(:report) { Report.find("page_view_total_reqs") }

    context "with no data" do
      it "works" do
        expect(report.data).to be_empty
      end
    end

    context "with data" do
      before do
        CachedCounting.reset
        CachedCounting.enable
        ApplicationRequest.enable
      end

      after do
        CachedCounting.reset
        ApplicationRequest.disable
        CachedCounting.disable
      end

      it "works and does not count browser or mobile pageviews" do
        3.times { ApplicationRequest.increment!(:page_view_crawler) }
        8.times { ApplicationRequest.increment!(:page_view_logged_in) }
        6.times { ApplicationRequest.increment!(:page_view_logged_in_browser) }
        2.times { ApplicationRequest.increment!(:page_view_logged_in_mobile) }
        2.times { ApplicationRequest.increment!(:page_view_anon) }
        1.times { ApplicationRequest.increment!(:page_view_anon_browser) }
        4.times { ApplicationRequest.increment!(:page_view_anon_mobile) }

        CachedCounting.flush

        expect(report.data.sum { |r| r[:y] }).to eq(13)
      end
    end
  end

  describe "user to user private messages with replies" do
    let(:report) { Report.find("user_to_user_private_messages_with_replies") }
    let(:user) { Fabricate(:user) }
    let(:topic) { Fabricate(:topic, created_at: 1.hour.ago, user: user) }

    it "topic report).to not include private messages" do
      Fabricate(:private_message_topic, created_at: 1.hour.ago, user: user)
      topic
      report = Report.find("topics")
      expect(report.data[0][:y]).to eq(1)
      expect(report.total).to eq(1)
    end

    it "post report).to not include private messages" do
      Fabricate(:private_message_post, created_at: 1.hour.ago)
      Fabricate(:post)
      report = Report.find("posts")
      expect(report.data[0][:y]).to eq 1
      expect(report.total).to eq 1
    end

    context "with no private messages" do
      it "returns an empty report" do
        expect(report.data).to be_blank
      end

      context "with some public posts" do
        it "returns an empty report" do
          Fabricate(:post, topic: topic, user: user)
          Fabricate(:post, topic: topic, user: user)
          expect(report.data).to be_blank
          expect(report.total).to eq 0
        end
      end
    end

    context "with some private messages" do
      before do
        Fabricate(:private_message_post, created_at: 25.hours.ago, user: user)
        Fabricate(:private_message_post, created_at: 1.hour.ago, user: user)
        Fabricate(:private_message_post, created_at: 1.hour.ago, user: user)
      end

      it "returns correct data" do
        expect(report.data[0][:y]).to eq 1
        expect(report.data[1][:y]).to eq 2
        expect(report.total).to eq 3
      end

      context "with some public posts" do
        before do
          Fabricate(:post, user: user, topic: topic)
          Fabricate(:post, user: user, topic: topic)
        end

        it "returns correct data" do
          expect(report.data[0][:y]).to eq 1
          expect(report.data[1][:y]).to eq 2
          expect(report.total).to eq 3
        end
      end
    end

    context "with private message from system user" do
      before do
        Fabricate(:private_message_post, created_at: 1.hour.ago, user: Discourse.system_user)
      end

      it "does not include system users" do
        expect(report.data).to be_blank
        expect(report.total).to eq 0
      end
    end
  end

  describe "user to user private messages" do
    let(:report) { Report.find("user_to_user_private_messages") }

    context "with private message from system user" do
      before do
        Fabricate(:private_message_post, created_at: 1.hour.ago, user: Discourse.system_user)
      end

      it "does not include system users" do
        expect(report.data).to be_blank
        expect(report.total).to eq 0
      end
    end
  end

  describe "users by trust level report" do
    let(:report) { Report.find("users_by_trust_level") }

    include_examples "no data"

    context "with users at different trust levels" do
      before do
        3.times { Fabricate(:user, trust_level: TrustLevel[0]) }
        2.times { Fabricate(:user, trust_level: TrustLevel[2]) }
        Fabricate(:user, trust_level: TrustLevel[4])
      end

      it "returns a report with data" do
        expect(report.data).to be_present
        expect(report.data.find { |d| d[:x] == TrustLevel[0] }[:y]).to eq 3
        expect(report.data.find { |d| d[:x] == TrustLevel[2] }[:y]).to eq 2
        expect(report.data.find { |d| d[:x] == TrustLevel[4] }[:y]).to eq 1

        expect(
          report.data.find { |d| d[:x] == TrustLevel[0] }[:url],
        ).to eq "/admin/users/list/newuser"
      end
    end
  end

  describe "new contributors report" do
    let(:report) { Report.find("new_contributors") }

    include_examples "no data"

    context "with contributors" do
      before do
        jeff = Fabricate(:user)
        jeff.user_stat = UserStat.new(new_since: 1.hour.ago, first_post_created_at: 1.day.ago)

        regis = Fabricate(:user)
        regis.user_stat = UserStat.new(new_since: 1.hour.ago, first_post_created_at: 2.days.ago)

        hawk = Fabricate(:user)
        hawk.user_stat = UserStat.new(new_since: 1.hour.ago, first_post_created_at: 2.days.ago)
      end

      it "returns a report with data" do
        expect(report.data).to be_present

        expect(report.data[0][:y]).to eq 2
        expect(report.data[1][:y]).to eq 1
      end
    end
  end

  describe "users by types level report" do
    let(:report) { Report.find("users_by_type") }

    include_examples "no data"

    context "with users at different trust levels" do
      before do
        3.times { Fabricate(:user, admin: true) }
        2.times { Fabricate(:user, moderator: true) }
        UserSilencer.silence(Fabricate(:user, refresh_auto_groups: true), Fabricate.build(:admin))
        Fabricate(:user, suspended_till: 1.week.from_now, suspended_at: 1.day.ago)
      end

      it "returns a report with data" do
        expect(report.data).to be_present

        label = Proc.new { |key| I18n.t("reports.users_by_type.xaxis_labels.#{key}") }
        expect(report.data.find { |d| d[:x] == label.call("admin") }[:y]).to eq 3
        expect(report.data.find { |d| d[:x] == label.call("moderator") }[:y]).to eq 2
        expect(report.data.find { |d| d[:x] == label.call("silenced") }[:y]).to eq 1
        expect(report.data.find { |d| d[:x] == label.call("suspended") }[:y]).to eq 1
      end
    end
  end

  describe "trending search report" do
    let(:report) { Report.find("trending_search") }

    include_examples "no data"

    context "with different searches" do
      before do
        SearchLog.log(term: "ruby", search_type: :header, ip_address: "127.0.0.1")

        SearchLog.create!(
          term: "ruby",
          search_result_id: 1,
          search_type: 1,
          ip_address: "127.0.0.1",
          user_id: Fabricate(:user).id,
        )

        SearchLog.log(term: "ruby", search_type: :header, ip_address: "127.0.0.2")
        SearchLog.log(term: "php", search_type: :header, ip_address: "127.0.0.1")
      end

      after { SearchLog.clear_debounce_cache! }

      it "returns a report with data" do
        expect(report.data[0][:term]).to eq("ruby")
        expect(report.data[0][:searches]).to eq(3)
        expect(report.data[0][:ctr]).to eq(33.4)

        expect(report.data[1][:term]).to eq("php")
        expect(report.data[1][:searches]).to eq(1)
      end
    end
  end

  describe "DAU/MAU report" do
    let(:report) { Report.find("dau_by_mau") }

    include_examples "no data"

    context "with different users/visits" do
      before do
        freeze_time_safe

        arpit = Fabricate(:user)
        arpit.user_visits.create(visited_at: 1.day.ago)

        sam = Fabricate(:user)
        sam.user_visits.create(visited_at: 2.days.ago)

        robin = Fabricate(:user)
        robin.user_visits.create(visited_at: 2.days.ago)

        michael = Fabricate(:user)
        michael.user_visits.create(visited_at: 35.days.ago)

        gerhard = Fabricate(:user)
        gerhard.user_visits.create(visited_at: 45.days.ago)
      end

      it "returns a report with data" do
        expect(report.data.first[:y]).to eq(100)
        expect(report.data.last[:y]).to eq(33.34)
        expect(report.prev30Days).to eq(75)
      end
    end
  end

  describe "Daily engaged users" do
    let(:report) { Report.find("daily_engaged_users") }

    include_examples "no data"

    context "with different activities" do
      before do
        freeze_time_safe

        UserActionManager.enable

        arpit = Fabricate(:user)
        sam = Fabricate(:user)

        jeff = Fabricate(:user, created_at: 1.day.ago, refresh_auto_groups: true)
        post = create_post(user: jeff, created_at: 1.day.ago)
        PostActionCreator.like(arpit, post)
        PostActionCreator.like(sam, post)
      end

      it "returns a report with data" do
        expect(report.data.first[:y]).to eq(1)
        expect(report.data.last[:y]).to eq(2)
      end
    end
  end

  describe "posts counts" do
    it "only counts regular posts" do
      post = Fabricate(:post)
      Fabricate(:moderator_post, topic: post.topic)
      Fabricate.build(:post, post_type: Post.types[:whisper], topic: post.topic)
      post.topic.add_small_action(Fabricate(:admin), "invited_group", "coolkids")
      r = Report.find("posts")
      expect(r.total).to eq(1)
      expect(r.data[0][:y]).to eq(1)
    end
  end

  describe "flags_status" do
    let(:report) { Report.find("flags_status") }

    include_examples "no data"

    context "with flags" do
      let(:flagger) { Fabricate(:user, refresh_auto_groups: true) }
      let(:post) { Fabricate(:post, user: flagger) }

      before { freeze_time }

      it "returns a report with data" do
        result =
          PostActionCreator.new(flagger, post, PostActionType.types[:spam], message: "bad").perform

        expect(result.success).to eq(true)
        expect(report.data).to be_present

        row = report.data[0]
        expect(row[:post_type]).to eq("spam")
        expect(row[:staff_username]).to eq(nil)
        expect(row[:staff_id]).to eq(nil)
        expect(row[:poster_username]).to eq(post.user.username)
        expect(row[:poster_id]).to eq(post.user.id)
        expect(row[:poster_avatar_template]).to be_present
        expect(row[:flagger_id]).to eq(flagger.id)
        expect(row[:flagger_username]).to eq(flagger.username)
        expect(row[:flagger_avatar_template]).to be_present
        expect(row[:resolution]).to eq("No action")
        expect(row[:response_time]).to eq(nil)
      end
    end
  end

  describe "post_edits" do
    let(:report) { Report.find("post_edits") }

    include_examples "no data"

    context "with edits" do
      let(:editor) { Fabricate(:user) }
      let(:post) { Fabricate(:post) }

      before do
        freeze_time

        post.revise(
          post.user,
          { raw: "updated body by author", edit_reason: "not cool" },
          force_new_version: true,
        )
        post.revise(editor, raw: "updated body", edit_reason: "not cool")
      end

      it "returns a report with data" do
        expect(report.data).to be_present
        expect(report.data.count).to be(1)

        row = report.data[0]
        expect(row[:editor_id]).to eq(editor.id)
        expect(row[:editor_username]).to eq(editor.username)
        expect(row[:editor_avatar_template]).to be_present
        expect(row[:author_id]).to eq(post.user.id)
        expect(row[:author_username]).to eq(post.user.username)
        expect(row[:author_avatar_template]).to be_present
        expect(row[:edit_reason]).to eq("not cool")
        expect(row[:post_raw]).to eq("updated body")
        expect(row[:post_number]).to eq(post.post_number)
        expect(row[:topic_id]).to eq(post.topic.id)
      end
    end

    context "with editor filter" do
      fab!(:posts) { Fabricate.times(3, :post) }

      fab!(:editor_with_two_edits) do
        Fabricate(:user).tap do |user|
          2.times { |i| posts[i].revise(user, { raw: "edit #{i + 1}" }) }
        end
      end

      fab!(:editor_with_one_edit) do
        Fabricate(:user).tap { |user| posts.last.revise(user, { raw: "edit 3" }) }
      end

      let(:report_with_one_edit) do
        Report.find("post_edits", { filters: { "editor" => editor_with_one_edit.username } })
      end

      let(:report_with_two_edits) do
        Report.find("post_edits", { filters: { "editor" => editor_with_two_edits.username } })
      end

      it "returns a report for a given editor" do
        expect(report_with_one_edit.data.count).to be(1)
        expect(report_with_two_edits.data.count).to be(2)
      end
    end
  end

  describe "moderator activity" do
    let(:report) { Report.find("moderators_activity") }

    let(:sam) { Fabricate(:user, moderator: true, username: "sam") }

    let(:jeff) { Fabricate(:user, moderator: true, username: "jeff", refresh_auto_groups: true) }

    include_examples "no data"

    context "with moderators" do
      before { freeze_time(Date.today) }

      context "with moderators order" do
        before do
          Fabricate(:post, user: sam)
          Fabricate(:post, user: jeff)
        end

        it "returns the moderators in alphabetical order" do
          expect(report.data[0][:username]).to eq("jeff")
          expect(report.data[1][:username]).to eq("sam")
        end
      end

      context "with time read" do
        before do
          sam.user_visits.create(visited_at: 2.days.ago, time_read: 200)
          sam.user_visits.create(visited_at: 1.day.ago, time_read: 100)

          jeff.user_visits.create(visited_at: 2.days.ago, time_read: 1000)
          jeff.user_visits.create(visited_at: 1.day.ago, time_read: 2000)

          Fabricate(:topic, created_at: 1.day.ago)
        end

        it "returns the correct read times" do
          expect(report.data[0][:username]).to eq("jeff")
          expect(report.data[0][:time_read]).to eq(3000)
          expect(report.data[1][:username]).to eq("sam")
          expect(report.data[1][:time_read]).to eq(300)
        end
      end

      context "with flags" do
        before do
          flagged_post = Fabricate(:post)
          result = PostActionCreator.off_topic(jeff, flagged_post)
          result.reviewable.perform(jeff, :agree_and_keep)
        end

        it "returns the correct flag counts" do
          expect(report.data.count).to eq(1)
          expect(report.data[0][:flag_count]).to eq(1)
          expect(report.data[0][:username]).to eq("jeff")
        end
      end

      context "with topics" do
        before do
          Fabricate(:topic, user: sam)
          Fabricate(:topic, user: sam)
          Fabricate(:topic, user: jeff)
        end

        it "returns the correct topic count" do
          expect(report.data[0][:topic_count]).to eq(1)
          expect(report.data[0][:username]).to eq("jeff")
          expect(report.data[1][:topic_count]).to eq(2)
          expect(report.data[1][:username]).to eq("sam")
        end

        context "with private messages" do
          before { Fabricate(:private_message_topic, user: jeff) }

          it "doesn’t count private topic" do
            expect(report.data[0][:topic_count]).to eq(1)
            expect(report.data[1][:topic_count]).to eq(2)
          end
        end
      end

      context "with posts" do
        before do
          Fabricate(:post, user: sam)
          Fabricate(:post, user: sam)
          Fabricate(:post, user: jeff)
        end

        it "returns the correct topic count" do
          expect(report.data[0][:topic_count]).to eq(1)
          expect(report.data[0][:username]).to eq("jeff")
          expect(report.data[1][:topic_count]).to eq(2)
          expect(report.data[1][:username]).to eq("sam")
        end

        context "with private messages" do
          before { Fabricate(:private_message_post, user: jeff) }

          it "doesn’t count private post" do
            expect(report.data[0][:post_count]).to eq(1)
            expect(report.data[1][:post_count]).to eq(2)
          end
        end
      end

      context "with private messages" do
        before do
          Fabricate(:post, user: sam)
          Fabricate(:post, user: jeff)
          Fabricate(:private_message_post, user: jeff)
        end

        it "returns the correct topic count" do
          expect(report.data[0][:pm_count]).to eq(1)
          expect(report.data[0][:username]).to eq("jeff")
          expect(report.data[1][:pm_count]).to be_blank
          expect(report.data[1][:username]).to eq("sam")
        end
      end

      context "with revisions" do
        before do
          post = Fabricate(:post)
          post.revise(sam, raw: "updated body", edit_reason: "not cool")
        end

        it "returns the correct revisions count" do
          expect(report.data[0][:revision_count]).to eq(1)
          expect(report.data[0][:username]).to eq("sam")
        end

        context "when revising own post" do
          before do
            post = Fabricate(:post, user: sam)
            post.revise(sam, raw: "updated body")
          end

          it "doesn't count a revision on your own post" do
            expect(report.data[0][:revision_count]).to eq(1)
            expect(report.data[0][:username]).to eq("sam")
          end
        end
      end

      context "with previous data" do
        before { Fabricate(:topic, user: sam, created_at: 1.year.ago) }

        it "doesn’t count old data" do
          expect(report.data[0][:topic_count]).to be_blank
          expect(report.data[0][:username]).to eq("sam")
        end
      end
    end
  end

  describe "flags" do
    let(:report) { Report.find("flags") }

    include_examples "no data"

    context "with data" do
      include_examples "with data x/y"

      before(:each) do
        user = Fabricate(:user, refresh_auto_groups: true)
        topic = Fabricate(:topic, user: user)
        post0 = Fabricate(:post, topic: topic, user: user)
        post1 =
          Fabricate(:post, topic: Fabricate(:topic, category: category_2, user: user), user: user)
        post2 = Fabricate(:post, topic: topic, user: user)
        post3 = Fabricate(:post, topic: topic, user: user)
        PostActionCreator.off_topic(user, post0)
        PostActionCreator.off_topic(user, post1)
        PostActionCreator.off_topic(user, post2)
        PostActionCreator.create(user, post3, :off_topic, created_at: 45.days.ago)
      end

      context "with category filtering" do
        let(:report) { Report.find("flags", filters: { category: category_2.id }) }

        include_examples "category filtering"

        context "with subcategories" do
          let(:report) do
            Report.find("flags", filters: { category: category_1.id, include_subcategories: true })
          end

          include_examples "category filtering on subcategories"
        end
      end
    end
  end

  describe "topics" do
    let(:report) { Report.find("topics") }

    include_examples "no data"

    context "with data" do
      include_examples "with data x/y"

      before(:each) do
        user = Fabricate(:user)
        Fabricate(:topic, user: user)
        Fabricate(:topic, category: category_2, user: user)
        Fabricate(:topic, user: user)
        Fabricate(:topic, created_at: 45.days.ago, user: user)
      end

      context "with category filtering" do
        let(:report) { Report.find("topics", filters: { category: category_2.id }) }

        include_examples "category filtering"

        context "with subcategories" do
          let(:report) do
            Report.find("topics", filters: { category: category_1.id, include_subcategories: true })
          end

          include_examples "category filtering on subcategories"
        end
      end
    end
  end

  describe "exception report" do
    before(:each) do
      class Report
        def self.report_exception_test(report)
          report.data = x
        end
      end
    end

    it "returns a report with an exception error" do
      report = Report.find("exception_test", wrap_exceptions_in_test: true)
      expect(report.error).to eq(:exception)
    end
  end

  describe "timeout report" do
    before(:each) do
      freeze_time

      class Report
        def self.report_timeout_test(report)
          report.error =
            wrap_slow_query(1) { ActiveRecord::Base.connection.execute("SELECT pg_sleep(5)") }
        end
      end
    end

    it "returns a report with a timeout error" do
      report = Report.find("timeout_test")
      expect(report.error).to eq(:timeout)
    end
  end

  describe "unexpected error on report initialization" do
    before do
      @orig_logger = Rails.logger
      Rails.logger = @fake_logger = FakeLogger.new
    end

    after { Rails.logger = @orig_logger }

    it "returns no report" do
      class ReportInitError < StandardError
      end

      Report.stubs(:new).raises(ReportInitError.new("x"))

      report = Report.find("signups", wrap_exceptions_in_test: true)

      expect(report).to be_nil

      expect(@fake_logger.errors).to eq(["Couldn’t create report `signups`: <ReportInitError x>"])
    end
  end

  describe "posts" do
    let(:report) { Report.find("posts") }

    include_examples "no data"

    context "with data" do
      include_examples "with data x/y"

      before(:each) do
        user = Fabricate(:user)
        topic = Fabricate(:topic, user: user)
        topic_with_category_id = Fabricate(:topic, category: category_2, user: user)
        Fabricate(:post, topic: topic, user: user)
        Fabricate(:post, topic: topic_with_category_id, user: user)
        Fabricate(:post, topic: topic, user: user)
        Fabricate(:post, created_at: 45.days.ago, topic: topic, user: user)
      end

      context "with category filtering" do
        let(:report) { Report.find("posts", filters: { category: category_2.id }) }

        include_examples "category filtering"

        context "with subcategories" do
          let(:report) do
            Report.find("posts", filters: { category: category_1.id, include_subcategories: true })
          end

          include_examples "category filtering on subcategories"
        end
      end
    end
  end

  # TODO: time_to_first_response

  describe "topics_with_no_response" do
    let(:report) { Report.find("topics_with_no_response") }

    include_examples "no data"

    context "with data" do
      include_examples "with data x/y"

      before(:each) do
        user = Fabricate(:user)
        Fabricate(:topic, category: category_2, user: user)
        Fabricate(:post, topic: Fabricate(:topic, user: user), user: user)
        Fabricate(:topic, user: user)
        Fabricate(:topic, created_at: 45.days.ago, user: user)
      end

      context "with category filtering" do
        let(:report) do
          Report.find("topics_with_no_response", filters: { category: category_2.id })
        end

        include_examples "category filtering"

        context "with subcategories" do
          let(:report) do
            Report.find(
              "topics_with_no_response",
              filters: {
                category: category_1.id,
                include_subcategories: true,
              },
            )
          end

          include_examples "category filtering on subcategories"
        end
      end
    end
  end

  describe "likes" do
    let(:report) { Report.find("likes") }

    include_examples "no data"

    context "with data" do
      include_examples "with data x/y"

      before(:each) do
        topic = Fabricate(:topic, category: category_2)
        post = Fabricate(:post, topic: topic)
        PostActionCreator.like(Fabricate(:user), post)

        topic = Fabricate(:topic, category: category_3)
        post = Fabricate(:post, topic: topic)
        PostActionCreator.like(Fabricate(:user), post)
        PostActionCreator.like(Fabricate(:user), post)
        PostActionCreator
          .like(Fabricate(:user), post)
          .post_action
          .tap { |pa| pa.created_at = 45.days.ago }
          .save!
      end

      context "with category filtering" do
        let(:report) { Report.find("likes", filters: { category: category_2.id }) }

        include_examples "category filtering"

        context "with subcategories" do
          let(:report) do
            Report.find("likes", filters: { category: category_1.id, include_subcategories: true })
          end

          include_examples "category filtering on subcategories"
        end
      end
    end
  end

  describe "user_flagging_ratio" do
    let(:joffrey) { Fabricate(:user, username: "joffrey", refresh_auto_groups: true) }
    let(:robin) { Fabricate(:user, username: "robin", refresh_auto_groups: true) }
    let(:moderator) { Fabricate(:moderator) }
    let(:user) { Fabricate(:user) }

    context "with data" do
      it "it works" do
        topic = Fabricate(:topic, user: user)
        2.times do
          post_disagreed = Fabricate(:post, topic: topic, user: user)
          result = PostActionCreator.spam(joffrey, post_disagreed)
          result.reviewable.perform(moderator, :disagree)
        end

        3.times do
          post_disagreed = Fabricate(:post, topic: topic, user: user)
          result = PostActionCreator.spam(robin, post_disagreed)
          result.reviewable.perform(moderator, :disagree)
        end
        post_agreed = Fabricate(:post, user: user, topic: topic)
        result = PostActionCreator.off_topic(robin, post_agreed)
        result.reviewable.perform(moderator, :agree_and_keep)

        report = Report.find("user_flagging_ratio")

        first = report.data[0]
        expect(first[:username]).to eq("joffrey")
        expect(first[:score]).to eq(2)
        expect(first[:agreed_flags]).to eq(0)
        expect(first[:disagreed_flags]).to eq(2)

        second = report.data[1]
        expect(second[:username]).to eq("robin")
        expect(second[:agreed_flags]).to eq(1)
        expect(second[:disagreed_flags]).to eq(3)
      end
    end
  end

  describe "report_suspicious_logins" do
    let(:joffrey) { Fabricate(:user, username: "joffrey") }
    let(:robin) { Fabricate(:user, username: "robin") }

    context "with data" do
      it "works" do
        SiteSetting.verbose_auth_token_logging = true

        UserAuthToken.log(action: "suspicious", user_id: joffrey.id, created_at: 2.hours.ago)
        UserAuthToken.log(action: "suspicious", user_id: joffrey.id, created_at: 3.hours.ago)
        UserAuthToken.log(action: "suspicious", user_id: robin.id, created_at: 1.hour.ago)

        report = Report.find("suspicious_logins")

        expect(report.data.length).to eq(3)
        expect(report.data[0][:username]).to eq("robin")
        expect(report.data[1][:username]).to eq("joffrey")
        expect(report.data[2][:username]).to eq("joffrey")
      end
    end
  end

  describe "report_staff_logins" do
    let(:joffrey) { Fabricate(:admin, username: "joffrey") }
    let(:robin) { Fabricate(:admin, username: "robin") }
    let(:james) { Fabricate(:user, username: "james") }

    context "with data" do
      it "works" do
        freeze_time_safe

        ip = [81, 2, 69, 142]

        DiscourseIpInfo.open_db(File.join(Rails.root, "spec", "fixtures", "mmdb"))
        Resolv::DNS
          .any_instance
          .stubs(:getname)
          .with(ip.join("."))
          .returns("ip-#{ip.join("-")}.example.com")

        UserAuthToken.log(
          action: "generate",
          user_id: robin.id,
          client_ip: ip.join("."),
          created_at: 1.hour.ago,
        )
        UserAuthToken.log(action: "generate", user_id: joffrey.id, client_ip: "1.2.3.4")
        UserAuthToken.log(
          action: "generate",
          user_id: joffrey.id,
          client_ip: ip.join("."),
          created_at: 2.hours.ago,
        )
        UserAuthToken.log(action: "generate", user_id: james.id)

        report = Report.find("staff_logins")

        expect(report.data.length).to eq(3)
        expect(report.data[0][:username]).to eq("joffrey")

        expect(report.data[1][:username]).to eq("robin")
        expect(report.data[1][:location]).to eq("London, England, United Kingdom")

        expect(report.data[2][:username]).to eq("joffrey")
      end
    end
  end

  describe "report_top_uploads" do
    let(:report) { Report.find("top_uploads") }
    let(:tarek) { Fabricate(:admin, username: "tarek") }
    let(:khalil) { Fabricate(:admin, username: "khalil") }

    context "with data" do
      let!(:tarek_upload) do
        Fabricate(
          :upload,
          user: tarek,
          url: "/uploads/default/original/1X/tarek.jpg",
          extension: "jpg",
          original_filename: "tarek.jpg",
          filesize: 1000,
        )
      end
      let!(:khalil_upload) do
        Fabricate(
          :upload,
          user: khalil,
          url: "/uploads/default/original/1X/khalil.png",
          extension: "png",
          original_filename: "khalil.png",
          filesize: 2000,
        )
      end

      it "works" do
        expect(report.data.length).to eq(2)
        expect_uploads_report_data_to_be_equal(report.data, khalil, khalil_upload)
        expect_uploads_report_data_to_be_equal(report.data, tarek, tarek_upload)
      end
    end

    def expect_uploads_report_data_to_be_equal(data, user, upload)
      row = data.find { |r| r[:author_id] == user.id }
      expect(row[:author_id]).to eq(user.id)
      expect(row[:author_username]).to eq(user.username)
      expect(row[:author_avatar_template]).to eq(
        User.avatar_template(user.username, user.uploaded_avatar_id),
      )
      expect(row[:filesize]).to eq(upload.filesize)
      expect(row[:extension]).to eq(upload.extension)
      expect(row[:file_url]).to eq(Discourse.store.cdn_url(upload.url))
      expect(row[:file_name]).to eq(upload.original_filename.truncate(25))
    end

    include_examples "no data"
  end

  describe "report_top_ignored_users" do
    let(:report) { Report.find("top_ignored_users") }
    let(:tarek) { Fabricate(:user, username: "tarek") }
    let(:john) { Fabricate(:user, username: "john") }
    let(:matt) { Fabricate(:user, username: "matt") }

    context "with data" do
      before do
        Fabricate(:ignored_user, user: tarek, ignored_user: john)
        Fabricate(:ignored_user, user: tarek, ignored_user: matt)
      end

      it "works" do
        expect(report.data.length).to eq(2)

        expect_ignored_users_report_data_to_be_equal(report.data, john, 1, 0)
        expect_ignored_users_report_data_to_be_equal(report.data, matt, 1, 0)
      end

      context "when muted users exist" do
        before do
          Fabricate(:muted_user, user: tarek, muted_user: john)
          Fabricate(:muted_user, user: tarek, muted_user: matt)
        end

        it "works" do
          expect(report.data.length).to eq(2)
          expect_ignored_users_report_data_to_be_equal(report.data, john, 1, 1)
          expect_ignored_users_report_data_to_be_equal(report.data, matt, 1, 1)
        end
      end
    end

    def expect_ignored_users_report_data_to_be_equal(data, user, ignores, mutes)
      row = data.find { |r| r[:ignored_user_id] == user.id }
      expect(row).to be_present
      expect(row[:ignored_user_id]).to eq(user.id)
      expect(row[:ignored_username]).to eq(user.username)
      expect(row[:ignored_user_avatar_template]).to eq(
        User.avatar_template(user.username, user.uploaded_avatar_id),
      )
      expect(row[:ignores_count]).to eq(ignores)
      expect(row[:mutes_count]).to eq(mutes)
    end

    include_examples "no data"
  end

  describe "consolidated_page_views_browser_detection" do
    before do
      freeze_time(Time.now.at_midnight)
      Theme.clear_default!
    end

    let(:reports) { Report.find("consolidated_page_views_browser_detection") }

    context "with no data" do
      it "works" do
        reports.data.each { |report| expect(report[:data]).to be_empty }
      end
    end

    context "with data" do
      before do
        CachedCounting.reset
        CachedCounting.enable
        ApplicationRequest.enable
      end

      after do
        CachedCounting.reset
        ApplicationRequest.disable
        CachedCounting.disable
      end

      it "works" do
        3.times { ApplicationRequest.increment!(:page_view_crawler) }
        8.times { ApplicationRequest.increment!(:page_view_logged_in) }
        6.times { ApplicationRequest.increment!(:page_view_logged_in_browser) }
        2.times { ApplicationRequest.increment!(:page_view_anon) }
        1.times { ApplicationRequest.increment!(:page_view_anon_browser) }

        CachedCounting.flush

        page_view_crawler_report = reports.data.find { |r| r[:req] == "page_view_crawler" }
        page_view_logged_in_browser_report =
          reports.data.find { |r| r[:req] == "page_view_logged_in_browser" }
        page_view_anon_browser_report =
          reports.data.find { |r| r[:req] == "page_view_anon_browser" }
        page_view_other_report = reports.data.find { |r| r[:req] == "page_view_other" }

        expect(page_view_crawler_report[:data][0][:y]).to eql(3)
        expect(page_view_logged_in_browser_report[:data][0][:y]).to eql(6)
        expect(page_view_anon_browser_report[:data][0][:y]).to eql(1)
        expect(page_view_other_report[:data][0][:y]).to eql(3)
      end

      it "gives the same total as page_view_total_reqs" do
        3.times { ApplicationRequest.increment!(:page_view_crawler) }
        8.times { ApplicationRequest.increment!(:page_view_logged_in) }
        6.times { ApplicationRequest.increment!(:page_view_logged_in_browser) }
        2.times { ApplicationRequest.increment!(:page_view_anon) }
        1.times { ApplicationRequest.increment!(:page_view_anon_browser) }

        CachedCounting.flush

        total_consolidated = reports.data.sum { |r| r[:data][0][:y] }
        total_page_views = Report.find("page_view_total_reqs").data[0][:y]

        expect(total_consolidated).to eq(total_page_views)
      end
    end
  end

  describe "consolidated_page_views" do
    before do
      freeze_time(Time.now.at_midnight)
      Theme.clear_default!
    end

    let(:reports) { Report.find("consolidated_page_views") }

    context "with no data" do
      it "works" do
        reports.data.each { |report| expect(report[:data]).to be_empty }
      end
    end

    context "with data" do
      before do
        CachedCounting.reset
        CachedCounting.enable
        ApplicationRequest.enable
      end

      after do
        CachedCounting.reset
        ApplicationRequest.disable
        CachedCounting.disable
      end

      it "works" do
        3.times { ApplicationRequest.increment!(:page_view_crawler) }
        2.times { ApplicationRequest.increment!(:page_view_logged_in) }
        ApplicationRequest.increment!(:page_view_anon)

        CachedCounting.flush

        page_view_crawler_report = reports.data.find { |r| r[:req] == "page_view_crawler" }
        page_view_logged_in_report = reports.data.find { |r| r[:req] == "page_view_logged_in" }
        page_view_anon_report = reports.data.find { |r| r[:req] == "page_view_anon" }

        expect(page_view_crawler_report[:color]).to eql("#721D8D")
        expect(page_view_crawler_report[:data][0][:y]).to eql(3)

        expect(page_view_logged_in_report[:color]).to eql("#1EB8D1")
        expect(page_view_logged_in_report[:data][0][:y]).to eql(2)

        expect(page_view_anon_report[:color]).to eql("#9BC53D")
        expect(page_view_anon_report[:data][0][:y]).to eql(1)
      end
    end
  end

  describe ".report_consolidated_api_requests" do
    before do
      freeze_time(Time.now.at_midnight)
      Theme.clear_default!
    end

    let(:reports) { Report.find("consolidated_api_requests") }

    context "with no data" do
      it "works" do
        reports.data.each { |report| expect(report[:data]).to be_empty }
      end
    end

    context "with data" do
      before do
        CachedCounting.reset
        CachedCounting.enable
        ApplicationRequest.enable
      end

      after do
        ApplicationRequest.disable
        CachedCounting.disable
      end

      it "works" do
        2.times { ApplicationRequest.increment!(:api) }
        ApplicationRequest.increment!(:user_api)

        CachedCounting.flush

        api_report = reports.data.find { |r| r[:req] == "api" }
        user_api_report = reports.data.find { |r| r[:req] == "user_api" }

        expect(api_report[:color]).to eql("#1EB8D1")
        expect(api_report[:data][0][:y]).to eql(2)

        expect(user_api_report[:color]).to eql("#9BC53D")
        expect(user_api_report[:data][0][:y]).to eql(1)
      end
    end
  end

  describe "trust_level_growth" do
    before do
      freeze_time(Time.now.at_midnight)
      Theme.clear_default!
    end

    let(:reports) { Report.find("trust_level_growth") }

    context "with no data" do
      it "works" do
        reports.data.each { |report| expect(report[:data]).to be_empty }
      end
    end

    context "with data" do
      fab!(:gwen) { Fabricate(:user) }
      fab!(:martin) { Fabricate(:user) }

      before do
        UserHistory.create(
          action: UserHistory.actions[:auto_trust_level_change],
          target_user_id: gwen.id,
          new_value: TrustLevel[2],
          previous_value: 1,
        )
        UserHistory.create(
          action: UserHistory.actions[:change_trust_level],
          target_user_id: martin.id,
          new_value: TrustLevel[4],
          previous_value: 0,
        )
      end

      it "works" do
        tl1_reached = reports.data.find { |r| r[:req] == "tl1_reached" }
        tl2_reached = reports.data.find { |r| r[:req] == "tl2_reached" }
        tl3_reached = reports.data.find { |r| r[:req] == "tl3_reached" }
        tl4_reached = reports.data.find { |r| r[:req] == "tl4_reached" }

        expect(tl1_reached[:data][0][:y]).to eql(0)
        expect(tl2_reached[:data][0][:y]).to eql(1)
        expect(tl3_reached[:data][0][:y]).to eql(0)
        expect(tl4_reached[:data][0][:y]).to eql(1)
      end
    end
  end

  describe ".cache" do
    let(:exception_report) { Report.find("exception_test", wrap_exceptions_in_test: true) }
    let(:valid_report) { Report.find("valid_test", wrap_exceptions_in_test: true) }

    before(:each) do
      class Report
        def self.report_exception_test(report)
          report.data = x
        end

        def self.report_valid_test(report)
          report.data = "success!"
        end
      end
    end

    it "caches exception reports for 1 minute" do
      Discourse
        .cache
        .expects(:write)
        .with(Report.cache_key(exception_report), exception_report.as_json, expires_in: 1.minute)
      Report.cache(exception_report)
    end

    it "caches valid reports for 35 minutes" do
      Discourse
        .cache
        .expects(:write)
        .with(Report.cache_key(valid_report), valid_report.as_json, expires_in: 35.minutes)
      Report.cache(valid_report)
    end
  end

  describe "top_uploads" do
    context "with no data" do
      it "works" do
        report = Report.find("top_uploads")

        expect(report.data).to be_empty
      end
    end

    context "with data" do
      fab!(:jpg_upload) { Fabricate(:upload, extension: :jpg) }
      fab!(:png_upload) { Fabricate(:upload, extension: :png) }

      it "works" do
        report = Report.find("top_uploads")

        expect(report.data.length).to eq(2)
        expect(report.data.map { |row| row[:extension] }).to contain_exactly("jpg", "png")
      end

      it "works with filters" do
        report = Report.find("top_uploads", filters: { file_extension: "jpg" })

        expect(report.data.length).to eq(1)
        expect(report.data[0][:extension]).to eq("jpg")
      end
    end
  end

  describe "top_users_by_likes_received" do
    let(:report) { Report.find("top_users_by_likes_received") }

    include_examples "no data"

    context "with data" do
      before do
        user_1 = Fabricate(:user, username: "jonah")
        user_2 = Fabricate(:user, username: "jake")
        user_3 = Fabricate(:user, username: "john")

        3.times { UserAction.create!(user_id: user_1.id, action_type: UserAction::WAS_LIKED) }
        9.times { UserAction.create!(user_id: user_2.id, action_type: UserAction::WAS_LIKED) }
        6.times { UserAction.create!(user_id: user_3.id, action_type: UserAction::WAS_LIKED) }
      end

      it "with category filtering" do
        report = Report.find("top_users_by_likes_received")

        expect(report.data.length).to eq(3)
        expect(report.data[0][:username]).to eq("jake")
        expect(report.data[1][:username]).to eq("john")
        expect(report.data[2][:username]).to eq("jonah")
      end
    end
  end

  describe "top_users_by_likes_received_from_a_variety_of_people" do
    let(:report) { Report.find("top_users_by_likes_received_from_a_variety_of_people") }

    include_examples "no data"

    context "with data" do
      before do
        user_1 = Fabricate(:user, username: "jonah")
        user_2 = Fabricate(:user, username: "jake")
        user_3 = Fabricate(:user, username: "john")
        user_4 = Fabricate(:user, username: "joseph")
        user_5 = Fabricate(:user, username: "joanne")
        user_6 = Fabricate(:user, username: "jerome")

        topic_1 = Fabricate(:topic, user: user_1)
        topic_2 = Fabricate(:topic, user: user_2)
        topic_3 = Fabricate(:topic, user: user_3)

        post_1 = Fabricate(:post, topic: topic_1, user: user_1)
        post_2 = Fabricate(:post, topic: topic_2, user: user_2)
        post_3 = Fabricate(:post, topic: topic_3, user: user_3)

        3.times do
          UserAction.create!(
            user_id: user_4.id,
            target_post_id: post_1.id,
            action_type: UserAction::LIKE,
          )
        end
        6.times do
          UserAction.create!(
            user_id: user_5.id,
            target_post_id: post_2.id,
            action_type: UserAction::LIKE,
          )
        end
        9.times do
          UserAction.create!(
            user_id: user_6.id,
            target_post_id: post_3.id,
            action_type: UserAction::LIKE,
          )
        end
      end

      it "with category filtering" do
        report = Report.find("top_users_by_likes_received_from_a_variety_of_people")

        expect(report.data.length).to eq(3)
        expect(report.data[0][:username]).to eq("jonah")
        expect(report.data[1][:username]).to eq("jake")
        expect(report.data[2][:username]).to eq("john")
      end
    end
  end

  describe "top_users_by_likes_received_from_inferior_trust_level" do
    let(:report) { Report.find("top_users_by_likes_received_from_inferior_trust_level") }

    include_examples "no data"

    context "with data" do
      before do
        user_1 = Fabricate(:user, username: "jonah", trust_level: 2)
        user_2 = Fabricate(:user, username: "jake", trust_level: 2)
        user_3 = Fabricate(:user, username: "john", trust_level: 2)
        user_4 = Fabricate(:user, username: "joseph", trust_level: 1)
        user_5 = Fabricate(:user, username: "joanne", trust_level: 1)
        user_6 = Fabricate(:user, username: "jerome", trust_level: 2)

        topic_1 = Fabricate(:topic, user: user_1)
        topic_2 = Fabricate(:topic, user: user_2)
        topic_3 = Fabricate(:topic, user: user_3)

        post_1 = Fabricate(:post, topic: topic_1, user: user_1)
        post_2 = Fabricate(:post, topic: topic_2, user: user_2)
        post_3 = Fabricate(:post, topic: topic_3, user: user_3)

        3.times do
          UserAction.create!(
            user_id: user_4.id,
            target_post_id: post_1.id,
            action_type: UserAction::LIKE,
          )
        end
        6.times do
          UserAction.create!(
            user_id: user_5.id,
            target_post_id: post_2.id,
            action_type: UserAction::LIKE,
          )
        end
        9.times do
          UserAction.create!(
            user_id: user_6.id,
            target_post_id: post_3.id,
            action_type: UserAction::LIKE,
          )
        end
      end

      it "with category filtering" do
        report = Report.find("top_users_by_likes_received_from_inferior_trust_level")

        expect(report.data.length).to eq(2)
        expect(report.data[0][:username]).to eq("jake")
        expect(report.data[1][:username]).to eq("jonah")
      end
    end
  end

  describe "topic_view_stats" do
    let(:report) { Report.find("topic_view_stats") }

    fab!(:topic_1) { Fabricate(:topic) }
    fab!(:topic_2) { Fabricate(:topic) }

    include_examples "no data"

    context "with data" do
      before do
        freeze_time_safe

        Fabricate(
          :topic_view_stat,
          topic: topic_1,
          anonymous_views: 4,
          logged_in_views: 2,
          viewed_at: Time.zone.now - 5.days,
        )
        Fabricate(
          :topic_view_stat,
          topic: topic_1,
          anonymous_views: 5,
          logged_in_views: 18,
          viewed_at: Time.zone.now - 3.days,
        )
        Fabricate(
          :topic_view_stat,
          topic: topic_2,
          anonymous_views: 14,
          logged_in_views: 21,
          viewed_at: Time.zone.now - 5.days,
        )
        Fabricate(
          :topic_view_stat,
          topic: topic_2,
          anonymous_views: 9,
          logged_in_views: 13,
          viewed_at: Time.zone.now - 1.days,
        )
        Fabricate(
          :topic_view_stat,
          topic: Fabricate(:topic),
          anonymous_views: 1,
          logged_in_views: 34,
          viewed_at: Time.zone.now - 40.days,
        )
      end

      it "works" do
        expect(report.data.length).to eq(2)
        expect(report.data[0]).to include(
          topic_id: topic_2.id,
          topic_title: topic_2.title,
          total_anonymous_views: 23,
          total_logged_in_views: 34,
          total_views: 57,
        )
        expect(report.data[1]).to include(
          topic_id: topic_1.id,
          topic_title: topic_1.title,
          total_anonymous_views: 9,
          total_logged_in_views: 20,
          total_views: 29,
        )
      end

      context "with category filtering" do
        let(:report) { Report.find("topic_view_stats", filters: { category: category_1.id }) }

        before { topic_1.update!(category: category_1) }

        it "filters topics to that category" do
          expect(report.data.length).to eq(1)
          expect(report.data[0]).to include(
            topic_id: topic_1.id,
            topic_title: topic_1.title,
            total_anonymous_views: 9,
            total_logged_in_views: 20,
            total_views: 29,
          )
        end
      end
    end
  end
end