require "spec_helper"
require 'spec_mock_server'
require 'debci'
require 'debci/frontend'
require 'debci/package'
require 'debci/job'
require 'rack/test'

describe Debci::Frontend do
  include WebAppHelper

  let(:theuser) { Debci::User.create!(username: 'debci') }

  class Frontend < Debci::Frontend
    set :raise_errors, true
    set :show_exceptions, false
  end

  def app
    mock_server('/packages', Frontend)
  end

  let(:suite) { Debci.config.suite }
  let(:arch) { Debci.config.arch }

  it 'redirects /packages/ to /' do
    get '/packages/'
    expect(last_response.status).to eq(302)
    expect(URI.parse(last_response.location).path).to eq("/")
  end

  before do
    @rake = Debci::Package.create!(name: "rake")
    @ruby_defaults = Debci::Package.create!(name: "ruby-defaults")
    fake_login("foobar")
  end

  it 'does not list package publicly' do
    logout
    get "/packages/r/"
    expect(last_response.body).to_not match(/rake/)
    expect(last_response.body).to_not match(/ruby-defaults/)
    expect(last_response.body).to match(/Authentication required/)
  end

  it 'lists packages by prefix' do
    fake_login("someone")
    get "/packages/r/"
    expect(last_response.body).to match(/rake/)
    expect(last_response.body).to match(/ruby-defaults/)
  end

  context 'showing package page' do
    it 'works' do
      get "/packages/r/rake/"
      expect(last_response.body).to match(/<h2>\s*rake/)
    end

    it 'responds with 404 to non-existing package' do
      get "/packages/x/x-missing/"
      expect(last_response.status).to eq(404)
    end

    it 'validates prefix vs package name' do
      get "/packages/f/rake/"
      expect(last_response.status).to eq(404)
    end
  end

  context 'showing package history' do
    it 'works' do
      Debci::Job.create(
        package: @rake,
        suite: suite,
        arch: arch,
        status: "pass",
        created_at: Time.now,
        updated_at: Time.now,
        requestor: theuser
      )

      get "/packages/r/rake/#{suite}/#{arch}/"
      expect(last_response.body).to match(/pass/)
    end

    it 'validates prefix vs package name' do
      get "/packages/f/rake/#{suite}/#{arch}/"
      expect(last_response.status).to eq(404)
    end

    it 'responds with 404 to non-existing package' do
      get "/packages/x/x-missing/#{suite}/#{arch}/"
      expect(last_response.status).to eq(404)
    end

    it 'lists jobs in reverse date order, with pending jobs first' do
      # first
      Debci::Job.create(
        package: @rake,
        suite: suite,
        arch: arch,
        status: "pass",
        created_at: Time.now - 1.day,
        date: Time.now - 23.hours,
        updated_at: Time.now - 23.hours,
        requestor: theuser,
        trigger: 'first',
      )
      # second
      Debci::Job.create(
        package: @rake,
        suite: suite,
        arch: arch,
        status: "pass",
        created_at: Time.now - 12.hours,
        date: Time.now - 11.hours,
        updated_at: Time.now - 11.hours,
        requestor: theuser,
        trigger: 'second',
      )
      # pending
      Debci::Job.create(
        package: @rake,
        suite: suite,
        arch: arch,
        status: nil,
        created_at: Time.now,
        updated_at: Time.now,
        date: nil,
        requestor: theuser,
        trigger: 'pending'
      )
      get "/packages/r/rake/#{suite}/#{arch}/"
      expect(last_response.body).to match(/pending.*second.*first/m)
    end
  end

  context 'redirecting' do
    %w[/packages/f /packages/f/foobar /packages/f/foobar/unstable/amd64].each do |path|
      it "redirects #{path} to #{path}/" do
        get path
        expect(last_response.status).to eq(302)
        expect(URI.parse(last_response.location).path).to eq("#{path}/")
      end
    end
  end

  context "searching" do
    it 'finds packages by name only one result' do
      Debci::Package.create(name: "foobar")
      get '/packages/-/search', query: "foobar"
      expect(last_response.status).to eq(302)
    end

    it 'finds packages by name' do
      Debci::Package.create(name: "foobar")
      Debci::Package.create(name: "foobar2")
      get '/packages/-/search', query: "foo"
      expect(last_response.body).to match(/foobar/)
      expect(last_response.body).to match(/foobar2/)
    end

    it 'displays second page' do
      ('a'..'z').each { |i| Debci::Package.create(name: "foobar-#{i}") }
      get '/packages/-/search', query: "foo", page: "2"
      expect(last_response.body).to match(/foobar-m/)
    end

    it 'redirects directly to exact match' do
      Debci::Package.create!(name: "foobar")
      Debci::Package.create!(name: "foobar2")
      Debci::Package.create!(name: "oba")

      get '/packages/-/search', query: "oba"
      expect(last_response.status).to eq(302)
      expect(URI.parse(last_response.location).path).to eq("/packages/o/oba/")
    end

    it 'redirects to only match' do
      Debci::Package.create!(name: "foobar")
      Debci::Package.create!(name: "bazqux")
      get '/packages/-/search', query: "foo"
      expect(last_response.status).to eq(302)
      expect(URI.parse(last_response.location).path).to eq("/packages/f/foobar/")
    end
  end

  context "displaying pending jobs" do
    before do
      @rake.jobs.create!(trigger: 'current-package', requestor: theuser)
    end
    it 'list pending jobs for the current package only' do
      get '/packages/r/rake/'
      expect(last_response.body).to match('current-package')
    end
    it 'does not display jobs from other packages' do
      get '/packages/r/ruby-defaults/'
      expect(last_response.body).to_not match('current-package')
    end
  end

  context "displaying logs" do
    before do
      logout # this part should be public
    end
    it 'formats logs for viewing' do
      job = @rake.jobs.create!(requestor: theuser, suite: "unstable", arch: "arm64")
      job.autopkgtest_dir.mkpath
      Zlib::GzipWriter.open(job.autopkgtest_dir / "log.gz") do |f|
        f.write(File.read("#{__dir__}/log_spec/foobar.fake.log"))
      end

      get "/packages/r/rake/unstable/arm64/#{job.run_id}/"
      expect(last_response.status).to eq(200)
      expect(last_response.body).to match(/starting date and time/)
    end

    it 'renders log expired template for expired logs' do
      job = @rake.jobs.create!(requestor: theuser, suite: "unstable", arch: "arm64", files_purged: true)
      get "/packages/r/rake/unstable/arm64/#{job.run_id}/"
      expect(last_response.status).to eq(404)
      expect(last_response.body).to match(/expired/i)
    end

    it "doesn't try to display logs > 500KB'" do
      job = @rake.jobs.create!(requestor: theuser, suite: "unstable", arch: "arm64", log_size: 600.kilobytes)
      get "/packages/r/rake/unstable/arm64/#{job.run_id}/"
      expect(last_response.status).to eq(200)
      expect(last_response.body).to match(/Please download the raw log instead/i)
    end

    it "redirects to actual log when Accept: text/plain" do
      job = @rake.jobs.create!(requestor: theuser, suite: "unstable", arch: "arm64", log_size: 600.kilobytes)
      header "Accept", "text/plain"
      get "/packages/r/rake/unstable/arm64/#{job.run_id}/"
      expect(last_response.status).to eq(302)
      expect(last_response.headers["Location"]).to match(/log.gz$/)
    end

    it 'redirects url without trailing slash' do
      job = @rake.jobs.create!(requestor: theuser, suite: "unstable", arch: "arm64")
      get "/packages/r/rake/unstable/arm64/#{job.run_id}"
      expect(last_response.location).to match(%r{/#{job.run_id}/$})
    end

    it 'returns 404 on missing job' do
      get "/packages/r/rake/unstable/arm64/99999/"
      expect(last_response.status).to eq(404)
    end
  end
end
