summaryrefslogtreecommitdiff
path: root/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb
blob: a248f60d23e1aeda20f3bd4dd7d4fd85c2bca28a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
require 'spec_helper'

shared_examples_for 'AtomicInternalId' do |validate_presence: true|
  describe '.has_internal_id' do
    describe 'Module inclusion' do
      subject { described_class }

      it { is_expected.to include_module(AtomicInternalId) }
    end

    describe 'Validation' do
      before do
        allow_any_instance_of(described_class).to receive(:"ensure_#{scope}_#{internal_id_attribute}!")

        instance.valid?
      end

      context 'when presence validation is required' do
        before do
          skip unless validate_presence
        end

        it 'validates presence' do
          expect(instance.errors[internal_id_attribute]).to include("can't be blank")
        end
      end

      context 'when presence validation is not required' do
        before do
          skip if validate_presence
        end

        it 'does not validate presence' do
          expect(instance.errors[internal_id_attribute]).to be_empty
        end
      end
    end

    describe 'Creating an instance' do
      subject { instance.save! }

      it 'saves a new instance properly' do
        expect { subject }.not_to raise_error
      end
    end

    describe 'internal id generation' do
      subject { instance.save! }

      it 'calls InternalId.generate_next and sets internal id attribute' do
        iid = rand(1..1000)

        expect(InternalId).to receive(:generate_next).with(instance, scope_attrs, usage, any_args).and_return(iid)
        subject
        expect(read_internal_id).to eq(iid)
      end

      it 'does not overwrite an existing internal id' do
        write_internal_id(4711)

        expect { subject }.not_to change { read_internal_id }
      end

      context 'when the instance has an internal ID set' do
        let(:internal_id) { 9001 }

        it 'calls InternalId.update_last_value and sets the `last_value` to that of the instance' do
          write_internal_id(internal_id)

          expect(InternalId)
            .to receive(:track_greatest)
            .with(instance, scope_attrs, usage, internal_id, any_args)
            .and_return(internal_id)
          subject
        end
      end
    end

    describe "#reset_scope_internal_id_attribute" do
      it 'rewinds the allocated IID' do
        expect { ensure_scope_attribute! }.not_to raise_error
        expect(read_internal_id).not_to be_nil

        expect(reset_scope_attribute).to be_nil
        expect(read_internal_id).to be_nil
      end

      it 'allocates the same IID' do
        internal_id = ensure_scope_attribute!
        reset_scope_attribute
        expect(read_internal_id).to be_nil

        expect(ensure_scope_attribute!).to eq(internal_id)
      end
    end

    def ensure_scope_attribute!
      instance.public_send(:"ensure_#{scope}_#{internal_id_attribute}!")
    end

    def reset_scope_attribute
      instance.public_send(:"reset_#{scope}_#{internal_id_attribute}")
    end

    def read_internal_id
      instance.public_send(internal_id_attribute)
    end

    def write_internal_id(value)
      instance.public_send(:"#{internal_id_attribute}=", value)
    end
  end
end