require 'spec_helper'

describe "regulate_all_broadcasts" do

  before(:each) do
    stub_const "TestModel1", Class.new
    TestModel1.class_eval do
      include ActiveModel::Model
      def react_serializer
        as_json # does not include type: xxx as per reactive-record
      end
      def saved_changes
        Hash[*as_json.keys.collect { |attr| [attr, send(attr)] }.flatten(1)]
      end
      def attribute_names
        [:attr1, :attr2, :attr3, :attr4, :attr5]
      end
      attr_accessor :attr1, :attr2, :attr3, :attr4, :attr5
    end
    stub_const "TestModel2", Class.new
    TestModel2.class_eval do
      include ActiveModel::Model
      def react_serializer
        as_json # does not include type: xxx as per reactive-record
      end
      def saved_changes
        Hash[*as_json.keys.collect { |attr| [attr, send(attr)] }.flatten(1)]
      end
      def attribute_names
        [:attrA, :attrB, :attrC, :attrD, :attrE]
      end
      attr_accessor :attrA, :attrB, :attrC, :attrD, :attrE
    end
    allow_any_instance_of(Hyperstack::InternalPolicy).to receive(:id).and_return(:unique_broadcast_id)
    allow(Hyperstack::Connection).to receive(:active).and_return(['Application', 'AnotherApplication', 'Class1', 'Class2'])
  end

  it "will broadcast to a single channel" do
    stub_const "ApplicationPolicy", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts do |policy|
        policy.send_all
      end
    end
    model = TestModel1.new(attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['Application'],
        klass: 'TestModel1',
        record: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      }
    )
  end

  it "will broadcast to a multiple channels" do
    stub_const "ApplicationPolicy", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_all
      end
    end
    stub_const "AnotherApplicationPolicy", Class.new
    AnotherApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_all
      end
    end
    model = TestModel1.new(attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['Application', 'AnotherApplication'],
        klass: 'TestModel1',
        record: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      },
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'AnotherApplication',
        channels: ['Application', 'AnotherApplication'],
        klass: 'TestModel1',
        record: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      }
    )
  end

  it "will broadcast to a multiple channels with different attributes" do
    stub_const "ApplicationPolicy", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_only(:attr1)
      end
    end
    stub_const "AnotherApplicationPolicy", Class.new
    AnotherApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_all_but(:attr1)
      end
    end
    model = TestModel1.new(attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['Application', 'AnotherApplication'],
        klass: 'TestModel1',
        record: {attr1: 1},
        previous_changes: {attr1: 1}
      },
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'AnotherApplication',
        channels: ['Application', 'AnotherApplication'],
        klass: 'TestModel1',
        record: {attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      }
    )
  end

  it "can override the default channel using the 'to' method" do
    stub_const "ApplicationPolicy", Class.new
    stub_const "AnotherApplicationPolicy", Class.new
    stub_const "Application", Class.new
    stub_const "AnotherApplication", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_only(:attr1).to(AnotherApplication)
      end
    end
    AnotherApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_all_but(:attr1).to(Application)
      end
    end
    model = TestModel1.new(attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'AnotherApplication',
        channels: ['AnotherApplication', 'Application'],
        klass: 'TestModel1',
        record: {attr1: 1},
        previous_changes: {attr1: 1}
      },
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['AnotherApplication', 'Application'],
        klass: 'TestModel1',
        record: {attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      }
    )
  end

  it "can have multiple polcies" do
    stub_const "ApplicationPolicy", Class.new
    stub_const "AnotherApplicationPolicy", Class.new
    stub_const "Application", Class.new
    stub_const "AnotherApplication", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_only(:attr1, :attr2, :attr3).to(AnotherApplication)
      end
    end
    AnotherApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_all_but(:attr1).to(AnotherApplication, Application)
      end
    end
    model = TestModel1.new(attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'AnotherApplication',
        channels: ['AnotherApplication', 'Application'],
        klass: 'TestModel1',
        record: {attr2: 2, attr3: 3},
        previous_changes: {attr2: 2, attr3: 3}
      },
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['AnotherApplication', 'Application'],
        klass: 'TestModel1',
        record: {attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      }
    )
  end

  it "can have multiple polcies defined by separate send policies" do
    stub_const "ApplicationPolicy", Class.new
    stub_const "AnotherApplicationPolicy", Class.new
    stub_const "Application", Class.new
    stub_const "AnotherApplication", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_only(:attr1, :attr2, :attr3).to(AnotherApplication)
      end
    end
    AnotherApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_all_but(:attr1).to(Application)
        policy.send_all_but(:attr1)
      end
    end
    model = TestModel1.new(attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'AnotherApplication',
        channels: ['AnotherApplication', 'Application'],
        klass: 'TestModel1',
        record: {attr2: 2, attr3: 3},
        previous_changes: {attr2: 2, attr3: 3}
      },
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['AnotherApplication', 'Application'],
        klass: 'TestModel1',
        record: {attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      }
    )
  end

  it "can have multiple policies without any to method and will intersect the results" do
    stub_const "ApplicationPolicy", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_only(:attr1, :attr2)
        policy.send_only(:attr2, :attr3)
      end
    end
    model = TestModel1.new(attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['Application'],
        klass: 'TestModel1',
        record: {attr2: 2},
        previous_changes: {attr2: 2}
      }
    )
  end

  it "can have apply the same policies to different models" do
    stub_const "ApplicationPolicy", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts do | policy |
        policy.send_only(:attr1, :attrA)
      end
    end
    model = TestModel1.new(attr1: 1)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['Application'],
        klass: 'TestModel1',
        record: {attr1: 1},
        previous_changes: {attr1: 1}
      }
    )
    model = TestModel2.new(attrA: "A")
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Application',
        channels: ['Application'],
        klass: 'TestModel2',
        record: {attrA: "A"},
        previous_changes: {attrA: "A"}
      }
    )
  end
  it "will accept a list of classes to apply the policy to" do
    stub_const "ApplicationPolicy", Class.new
    stub_const "Class1", Class.new
    stub_const "Class2", Class.new
    ApplicationPolicy.class_eval do
      regulate_all_broadcasts(Class1, Class2) do | policy |
        policy.send_all
      end
    end
    model = TestModel1.new(attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5)
    expect { |b| Hyperstack::InternalPolicy.regulate_broadcast(model, &b) }.to yield_successive_args(
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Class1',
        channels: ['Class1', 'Class2'],
        klass: 'TestModel1',
        record: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      },
      {
        broadcast_id: :unique_broadcast_id,
        channel: 'Class2',
        channels: ['Class1', 'Class2'],
        klass: 'TestModel1',
        record: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5},
        previous_changes: {attr1: 1, attr2: 2, attr3: 3, attr4: 4, attr5: 5}
      }
    )
  end
end
