# frozen_string_literal: true
# rubocop:todo all

require 'spec_helper'

describe Mongo::Error::Parser do
  let(:parser) do
    described_class.new(document)
  end

  describe '#message' do

    context 'when the document contains no error message' do

      let(:document) do
        { 'ok' => 1 }
      end

      it 'returns an empty string' do
        expect(parser.message).to be_empty
      end
    end

    context 'when the document contains an errmsg' do

      let(:document) do
        { 'errmsg' => 'no such command: notacommand', 'code' => 59 }
      end

      it 'returns the message' do
        expect(parser.message).to eq('[59]: no such command: notacommand')
      end
    end

    context 'when the document contains an errmsg and code name' do

      let(:document) do
        { 'errmsg' => 'no such command: notacommand', 'code' => 59, 'codeName' => 'foo' }
      end

      it 'returns the message' do
        expect(parser.message).to eq('[59:foo]: no such command: notacommand')
      end
    end

=begin
    context 'when the document contains writeErrors' do

      context 'when only a single error exists' do

        let(:document) do
          { 'writeErrors' => [{ 'code' => 9, 'errmsg' => 'Unknown modifier: $st' }]}
        end

        it 'returns the message' do
          expect(parser.message).to eq('[9]: Unknown modifier: $st')
        end
      end

      context 'when multiple errors exist' do

        let(:document) do
          {
            'writeErrors' => [
              { 'code' => 9, 'errmsg' => 'Unknown modifier: $st' },
              { 'code' => 9, 'errmsg' => 'Unknown modifier: $bl' }
            ]
          }
        end

        it 'returns the messages concatenated' do
          expect(parser.message).to eq(
            'Multiple errors: 9: Unknown modifier: $st; 9: Unknown modifier: $bl'
          )
        end
      end

      context 'when multiple errors with code names exist' do

        let(:document) do
          {
            'writeErrors' => [
              { 'code' => 9, 'codeName' => 'foo', 'errmsg' => 'Unknown modifier: $st' },
              { 'code' => 9, 'codeName' => 'foo', 'errmsg' => 'Unknown modifier: $bl' },
            ]
          }
        end

        it 'returns the messages concatenated' do
          expect(parser.message).to eq(
            'Multiple errors: [9:foo]: Unknown modifier: $st; [9:foo]: Unknown modifier: $bl'
          )
        end
      end
    end
=end

    context 'when the document contains $err' do

      let(:document) do
        { '$err' => 'not authorized for query', 'code' => 13 }
      end

      it 'returns the message' do
        expect(parser.message).to eq('[13]: not authorized for query')
      end
    end

    context 'when the document contains err' do

      let(:document) do
        { 'err' => 'not authorized for query', 'code' => 13 }
      end

      it 'returns the message' do
        expect(parser.message).to eq('[13]: not authorized for query')
      end
    end

    context 'when the document contains a writeConcernError' do

      let(:document) do
        { 'writeConcernError' => { 'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'returns the message' do
        expect(parser.message).to eq('[100]: Not enough data-bearing nodes')
      end
    end
  end

  describe '#code' do

    context 'when document contains code and ok: 1' do
      let(:document) do
        { 'ok' => 1, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
      end

      it 'returns nil' do
        expect(parser.code).to be nil
      end
    end

    context 'when document contains code and ok: 1.0' do
      let(:document) do
        { 'ok' => 1.0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
      end

      it 'returns nil' do
        expect(parser.code).to be nil
      end
    end

    context 'when document contains code' do
      let(:document) do
        { 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
      end

      it 'returns the code' do
        expect(parser.code).to eq(10107)
      end

      context 'with legacy option' do
        let(:parser) do
          described_class.new(document, nil, legacy: true)
        end

        it 'returns nil' do
          expect(parser.code).to be nil
        end
      end
    end

    context 'when document does not contain code' do
      let(:document) do
        { 'ok' => 0, 'errmsg' => 'not master' }
      end

      it 'returns nil' do
        expect(parser.code).to eq(nil)
      end
    end

    context 'when the document contains a writeConcernError with a code' do

      let(:document) do
        { 'writeConcernError' => { 'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'returns the code' do
        expect(parser.code).to eq(100)
      end
    end

    context 'when the document contains a writeConcernError without a code' do

      let(:document) do
        { 'writeConcernError' => { 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'returns nil' do
        expect(parser.code).to be nil
      end
    end

    context 'when both top level code and write concern code are present' do

      let(:document) do
        { 'ok' => 0,
          'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster',
          'writeConcernError' => {
            'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'returns top level code' do
        expect(parser.code).to eq(10107)
      end
    end
  end

  describe '#code_name' do

    context 'when document contains code name and ok: 1' do
      let(:document) do
        { 'ok' => 1, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
      end

      it 'returns nil' do
        expect(parser.code_name).to be nil
      end
    end

    context 'when document contains code name and ok: 1.0' do
      let(:document) do
        { 'ok' => 1.0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
      end

      it 'returns nil' do
        expect(parser.code_name).to be nil
      end
    end

    context 'when document contains code name' do
      let(:document) do
        { 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
      end

      it 'returns the code name' do
        expect(parser.code_name).to eq('NotMaster')
      end

      context 'with legacy option' do
        let(:parser) do
          described_class.new(document, nil, legacy: true)
        end

        it 'returns nil' do
          expect(parser.code_name).to be nil
        end
      end
    end

    context 'when document does not contain code name' do
      let(:document) do
        { 'ok' => 0, 'errmsg' => 'not master' }
      end

      it 'returns nil' do
        expect(parser.code_name).to eq(nil)
      end
    end

    context 'when the document contains a writeConcernError with a code' do

      let(:document) do
        { 'writeConcernError' => { 'code' => 100, 'codeName' => 'CannotSatisfyWriteConcern',
          'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'returns the code name' do
        expect(parser.code_name).to eq('CannotSatisfyWriteConcern')
      end
    end

    context 'when the document contains a writeConcernError without a code' do

      let(:document) do
        { 'writeConcernError' => { 'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'returns nil' do
        expect(parser.code_name).to be nil
      end
    end

    context 'when both top level code and write concern code are present' do

      let(:document) do
        { 'ok' => 0,
          'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster',
          'writeConcernError' => {
            'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'returns top level code' do
        expect(parser.code_name).to eq('NotMaster')
      end
    end
  end

  describe '#write_concern_error?' do
    context 'there is a write concern error' do

      let(:document) do
        { 'ok' => 1,
          'writeConcernError' => {
            'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'is true' do
        expect(parser.write_concern_error?).to be true
      end
    end

    context 'there is no write concern error' do

      let(:document) do
        { 'ok' => 0,
          'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster',
        }
      end

      it 'is false' do
        expect(parser.write_concern_error?).to be false
      end
    end

    context 'there is a top level error and write concern error' do

      let(:document) do
        { 'ok' => 0,
          'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster',
          'writeConcernError' => {
            'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'is true' do
        expect(parser.write_concern_error?).to be true
      end
    end
  end

  describe '#write_concern_error_code' do
    context 'there is a write concern error' do

      let(:document) do
        { 'ok' => 1,
          'writeConcernError' => {
            'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'is true' do
        expect(parser.write_concern_error_code).to eq(100)
      end
    end

    context 'there is no write concern error' do

      let(:document) do
        { 'ok' => 0,
          'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster',
        }
      end

      it 'is nil' do
        expect(parser.write_concern_error_code).to be nil
      end
    end

    context 'there is a top level error and write concern error' do

      let(:document) do
        { 'ok' => 0,
          'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster',
          'writeConcernError' => {
            'code' => 100, 'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'is true' do
        expect(parser.write_concern_error_code).to eq(100)
      end
    end
  end

  describe '#write_concern_error_code_name' do
    context 'there is a write concern error' do

      let(:document) do
        { 'ok' => 1,
          'writeConcernError' => {
            'code' => 100, 'codeName' => 'SomeCodeName',
              'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'is the code name' do
        expect(parser.write_concern_error_code_name).to eq('SomeCodeName')
      end
    end

    context 'there is no write concern error' do

      let(:document) do
        { 'ok' => 0,
          'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster',
        }
      end

      it 'is nil' do
        expect(parser.write_concern_error_code_name).to be nil
      end
    end

    context 'there is a top level error and write concern error' do

      let(:document) do
        { 'ok' => 0,
          'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster',
          'writeConcernError' => {
            'code' => 100, 'codeName' => 'SomeCodeName',
              'errmsg' => 'Not enough data-bearing nodes' } }
      end

      it 'is the code name' do
        expect(parser.write_concern_error_code_name).to eq('SomeCodeName')
      end
    end
  end

  describe '#document' do

    let(:document) do
      { 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
    end

    it 'returns the document' do
      expect(parser.document).to eq(document)
    end
  end

  describe '#replies' do

    context 'when there are no replies' do
      let(:document) do
        { 'ok' => 0, 'errmsg' => 'not master', 'code' => 10107, 'codeName' => 'NotMaster' }
      end

      it 'returns nil' do
        expect(parser.replies).to eq(nil)
      end
    end
  end

  describe '#labels' do

    let(:document) do
      {
        'code' => 251,
        'codeName' => 'NoSuchTransaction',
        'errorLabels' => labels,
      }
    end

    context 'when there are no labels' do
      let(:labels) do
        []
      end

      it 'has the correct labels' do
        expect(parser.labels).to eq(labels)
      end
    end

    context 'when there are labels' do
      let(:labels) do
        %w(TransientTransactionError)
      end

      it 'has the correct labels' do
        expect(parser.labels).to eq(labels)
      end
    end
  end

  describe '#wtimeout' do

    context 'when document contains wtimeout' do
      let(:document) do
        { 'ok' => 1, 'writeConcernError' => {
          'errmsg' => 'replication timed out', 'code' => 64,
          'errInfo' => {'wtimeout' => true}} }
      end

      it 'returns true' do
        expect(parser.wtimeout).to be true
      end
    end

    context 'when document does not contain wtimeout' do
      let(:document) do
        { 'ok' => 1, 'writeConcernError' => {
          'errmsg' => 'replication did not time out', 'code' => 55 }}
      end

      it 'returns nil' do
        expect(parser.wtimeout).to be nil
      end
    end
  end
end
