# frozen_string_literal: true

# Tests here are meant to run against the dev deployment of spamcheck
# see https://gitlab.com/gitlab-private/gl-security/engineering-and-research/automation-team/kubernetes/spamcheck/spamcheck-py/-/blob/main/dev/config/config.yaml
# for the filters that are applied.

require 'test/unit'
require 'grpc'
require 'spamcheck'

LOCAL_TEST = ENV['LOCAL_TESTING'].freeze

unless LOCAL_TEST
  abort 'SPAMCHECK_HOSTNAME not set' unless ENV['SPAMCHECK_HOSTNAME']
  abort 'SPAMCHECK_API_TOKEN not set' unless ENV['SPAMCHECK_API_TOKEN']
end

class TestSpamcheck < Test::Unit::TestCase
  def setup
    host = ENV['SPAMCHECK_HOSTNAME']
    jwt = ENV['SPAMCHECK_API_TOKEN']

    tls_creds = GRPC::Core::ChannelCredentials.new(File.read('/etc/ssl/certs/ca-certificates.crt'))
    auth_proc = proc { { 'authorization' => jwt } }
    jwt_creds = GRPC::Core::CallCredentials.new(auth_proc)
    credentials = tls_creds.compose(jwt_creds)

    if LOCAL_TEST
      testing_stub = Spamcheck::SpamcheckService::Stub.new('localhost:8001', :this_channel_is_insecure)
      @auth_stub = testing_stub
      @unauth_stub = testing_stub
    else
      @auth_stub = Spamcheck::SpamcheckService::Stub.new(host, credentials)
      @unauth_stub = Spamcheck::SpamcheckService::Stub.new(host, tls_creds)
    end
  end

  # Check that authentication is required
  def test_no_authentication
    return if LOCAL_TEST

    issue = Spamcheck::Issue.new
    assert_raise(GRPC::PermissionDenied) { @unauth_stub.check_for_spam_issue(issue) }
  end

  # Check that an invalid issue returns an error
  def test_invalid_issue
    issue = Spamcheck::Issue.new(description: 'test issue')
    exception = assert_raise(GRPC::InvalidArgument) { @auth_stub.check_for_spam_issue(issue) }
    assert_match(/Issue title is required/, exception.message)
  end

  # Check that project not allowed is NOOP
  def test_project_not_allowed
    issue = Spamcheck::Issue.new(
      title: 'test issue',
      description: 'test issue',
      project: { project_id: 1, project_path: 'test/project' }
    )
    resp = @auth_stub.check_for_spam_issue(issue)
    verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict)
    if LOCAL_TEST
      assert_equal(::Spamcheck::SpamVerdict::Verdict::ALLOW, verdict, 'Ham issue should be ALLOWED')
      assert_equal('ml inference score', resp.reason)
      assert_equal(true, resp.evaluated)
    else
      assert_equal(::Spamcheck::SpamVerdict::Verdict::NOOP, verdict,
                   'Issue with project not in allow list should be NOOP')
      assert_equal('project not allowed', resp.reason)
      assert_equal(false, resp.evaluated)
    end
  end

  # Check that ham is allowed
  def test_generic_ham
    issue = Spamcheck::Generic.new(
      text: 'Dependency update needed. The dependencies for this application are outdated and need to be updated.',
      project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' },
      type: 'ham_type',
      user: {
        id: 17,
        abuse_metadata: {
          account_age: 50,
          spam_score: 0.02
        }
      }
    )
    resp = @auth_stub.check_for_spam_issue(issue)
    verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict)
    assert_equal(::Spamcheck::SpamVerdict::Verdict::ALLOW, verdict, 'Ham issue verdict not "ALLOW"')
    assert_equal('ml inference score', resp.reason)
    assert_equal(Float, resp.score.class)
    assert_equal(true, resp.evaluated)
  end

  # Check that spam is blocked
  def test_generic_spam
    issue = Spamcheck::Generic.new(
      text: 'watch fifa live stream best live streaming [here](https://livestream.com)',
      project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' },
      type: 'spam_type',
      user: {
        id: 23,
        abuse_metadata: {
          account_age: 3,
          spam_score: 0.62
        }
      }
    )
    resp = @auth_stub.check_for_spam_issue(issue)
    verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict)
    assert_equal(::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW, verdict,
                 'Spam issue verdict not "CONDITIONAL_ALLOW"')
    assert_equal('ml inference score', resp.reason)
    assert_equal(Float, resp.score.class)
    assert_equal(true, resp.evaluated)
  end

  # Check that ham is allowed
  def test_issue_ham
    issue = Spamcheck::Issue.new(
      title: 'Dependency update needed',
      description: 'The dependencies for this application are outdated and need to be updated.',
      project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' }
    )
    resp = @auth_stub.check_for_spam_issue(issue)
    verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict)
    assert_equal(::Spamcheck::SpamVerdict::Verdict::ALLOW, verdict, 'Ham issue verdict not "ALLOW"')
    assert_equal('ml inference score', resp.reason)
    assert_equal(Float, resp.score.class)
    assert_equal(true, resp.evaluated)
  end

  # Check that spam is blocked
  def test_issue_spam
    issue = Spamcheck::Issue.new(
      title: 'watch fifa live stream',
      description: 'best live streaming [here](https://livestream.com)',
      project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' }
    )
    resp = @auth_stub.check_for_spam_issue(issue)
    verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict)
    assert_equal(::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW, verdict,
                 'Spam issue verdict not "CONDITIONAL_ALLOW"')
    assert_equal('ml inference score', resp.reason)
    assert_equal(Float, resp.score.class)
    assert_equal(true, resp.evaluated)
  end

  # Check that snippet ham is allowed
  def test_snippet_ham
    issue = Spamcheck::Snippet.new(
      title: 'Example SQL queries',
      description: '',
      project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' },
      files: [{ path: 'snippetfile1.txt' }]
    )
    resp = @auth_stub.check_for_spam_snippet(issue)
    verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict)
    assert_equal(::Spamcheck::SpamVerdict::Verdict::ALLOW, verdict, 'Ham snippet verdict not "ALLOW"')
    assert_equal('ml inference score', resp.reason)
    assert_equal(Float, resp.score.class)
    assert_equal(true, resp.evaluated)
  end

  # Check that snippet spam is blocked
  def test_snippet_spam
    issue = Spamcheck::Snippet.new(
      title: 'Slot Online',
      description: '',
      project: { project_id: 278_964, project_path: 'gitlab-org/gitlab' },
      files: [{ path: 'snippetfile1.txt' }]
    )
    issue.files << Spamcheck::File.new(path: 'snippetfile1.txt')
    resp = @auth_stub.check_for_spam_snippet(issue)
    verdict = ::Spamcheck::SpamVerdict::Verdict.resolve(resp.verdict)
    assert_equal(::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW, verdict,
                 'Spam snippet verdict not "CONDITIONAL_ALLOW"')
    assert_equal('ml inference score', resp.reason)
    assert_equal(Float, resp.score.class)
    assert_equal(true, resp.evaluated)
  end
end
