require 'rails_helper'

RSpec.describe ExternalSync do

  context '#changed?' do

    it 'keeps ActiveRecord instance unchanged on local but no remote changes' do
      object           = create(:group)
      previous_changes = { name: 'Changed' }
      current_changes  = previous_changes.dup

      result = described_class.changed?(
        object:           object,
        previous_changes: previous_changes,
        current_changes:  current_changes,
      )

      expect(result).to          be false
      expect(object.changed?).to be false
    end

    it 'keeps ActiveRecord instance unchanged on local and remote changes' do
      object           = create(:group)
      previous_changes = { name: 'Initial' }
      current_changes  = { name: 'Changed' }

      result = described_class.changed?(
        object:           object,
        previous_changes: previous_changes,
        current_changes:  current_changes,
      )

      expect(result).to          be false
      expect(object.changed?).to be false
    end

    it 'changes ActiveRecord instance attribute(s) for remote changes' do
      object           = create(:group)
      previous_changes = { name: object.name }
      current_changes  = { name: 'Changed' }

      result = described_class.changed?(
        object:           object,
        previous_changes: previous_changes,
        current_changes:  current_changes,
      )

      expect(result).to          be true
      expect(object.changed?).to be true
    end

    it 'prevents ActiveRecord method calls' do

      object           = create(:group)
      previous_changes = { name: object.name }
      current_changes  = { destroy: 'Changed' }

      result = described_class.changed?(
        object:           object,
        previous_changes: previous_changes,
        current_changes:  current_changes,
      )

      expect(result).to          be false
      expect(object.changed?).to be false
      expect(object.destroyed?).to be false
    end

  end

  context '#map' do
    it 'maps to symbol keys' do
      mapping = {
        'key' => 'key'
      }

      source = {
        'key' => 'value'
      }

      result = {
        key: 'value'
      }

      expect(described_class.map(mapping: mapping, source: source)).to eq(result)
    end

    it 'resolves deep structures' do
      mapping = {
        'sub.structure.key' => 'key',
      }

      source = {
        'sub' => {
          'structure' => {
            'key' => 'value'
          }
        }
      }

      result = {
        key: 'value'
      }

      expect(described_class.map(mapping: mapping, source: source)).to eq(result)

      # check if sub structure is untouched
      expect(source['sub'].key?('structure')).to be true
    end

    it 'skips irrelevant keys' do
      mapping = {
        'key' => 'key'
      }

      source = {
        'key'     => 'value',
        'skipped' => 'skipped'
      }

      result = {
        key: 'value'
      }

      expect(described_class.map(mapping: mapping, source: source)).to eq(result)
    end

    it 'can handle object instances' do

      mapping = {
        'name' => 'key'
      }

      source = double(name: 'value')

      result = {
        key: 'value'
      }

      expect(described_class.map(mapping: mapping, source: source)).to eq(result)
    end

    it 'can handle ActiveRecord instances' do

      mapping = {
        'name' => 'key'
      }

      source = create(:group, name: 'value')

      result = {
        key: 'value'
      }

      expect(described_class.map(mapping: mapping, source: source)).to eq(result)
    end

    it 'prevents ActiveRecord method calls' do

      mapping = {
        'name'    => 'key',
        'destroy' => 'evil'
      }

      source = create(:group, name: 'value')

      result = {
        key: 'value'
      }

      expect(described_class.map(mapping: mapping, source: source)).to eq(result)
      expect(source.destroyed?).to be false
    end
  end
end
