# frozen_string_literal: true

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

    #     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

    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' => 10_107, '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' => 10_107, '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' => 10_107, 'codeName' => 'NotMaster' }
      end

      it 'returns the code' do
        expect(parser.code).to eq(10_107)
      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' => 10_107, 'codeName' => 'NotMaster',
          'writeConcernError' => {
            'code' => 100, 'errmsg' => 'Not enough data-bearing nodes'
          } }
      end

      it 'returns top level code' do
        expect(parser.code).to eq(10_107)
      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' => 10_107, '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' => 10_107, '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' => 10_107, 'codeName' => 'NotMaster' }
      end

      it 'returns the code name' do
        expect(parser.code_name).to eq('NotMaster')
      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' => 10_107, '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' => 10_107, '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' => 10_107, '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' => 10_107, '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' => 10_107, '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' => 10_107, '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' => 10_107, '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' => 10_107, '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' => 10_107, '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
