summaryrefslogtreecommitdiff
path: root/spec/models/awareness_session_spec.rb
blob: 854ce5957f7c97c22ac48cb761d439f40af5814d (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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AwarenessSession, :clean_gitlab_redis_shared_state do
  subject { AwarenessSession.for(session_id) }

  let!(:user) { create(:user) }
  let(:session_id) { 1 }

  describe "when initiating a session" do
    it "provides a string representation of the model instance" do
      expected = "awareness_session=6b86b273ff34fce"

      expect(subject.to_s).to eql(expected)
    end

    it "provides a parameterized version of the session identifier" do
      expected = "6b86b273ff34fce"

      expect(subject.to_param).to eql(expected)
    end
  end

  describe "when a user joins a session" do
    let(:user2) { create(:user) }

    let(:presence_ttl) { 15.minutes }

    it "changes number of session members" do
      expect { subject.join(user) }.to change(subject, :size).by(1)
    end

    it "returns user as member of session with last_activity timestamp" do
      freeze_time do
        subject.join(user)

        session_users = subject.users_with_last_activity
        session_user, last_activity = session_users.first

        expect(session_user.id).to be(user.id)
        expect(last_activity).to be_eql(Time.now.utc)
      end
    end

    it "maintains user ID and last_activity pairs" do
      now = Time.zone.now

      travel_to now - 1.minute do
        subject.join(user2)
      end

      travel_to now do
        subject.join(user)
      end

      session_users = subject.users_with_last_activity

      expect(session_users[0].first.id).to eql(user.id)
      expect(session_users[0].last.to_i).to eql(now.to_i)

      expect(session_users[1].first.id).to eql(user2.id)
      expect(session_users[1].last.to_i).to eql((now - 1.minute).to_i)
    end

    it "reports user as present" do
      freeze_time do
        subject.join(user)

        expect(subject.present?(user, threshold: presence_ttl)).to be true
      end
    end

    it "reports user as away after a certain time on inactivity" do
      subject.join(user)

      travel_to((presence_ttl + 1.minute).from_now) do
        expect(subject.away?(user, threshold: presence_ttl)).to be true
      end
    end

    it "reports user as present still when there was some activity" do
      subject.join(user)

      travel_to((presence_ttl - 1.minute).from_now) do
        subject.touch!(user)
      end

      travel_to((presence_ttl + 1.minute).from_now) do
        expect(subject.present?(user, threshold: presence_ttl)).to be true
      end
    end

    it "creates user and session awareness keys in store" do
      subject.join(user)

      Gitlab::Redis::SharedState.with do |redis|
        keys = redis.scan_each(match: "gitlab:awareness:*").to_a

        expect(keys.size).to be(2)
      end
    end

    it "sets a timeout for user and session key" do
      subject.join(user)
      subject_id = Digest::SHA256.hexdigest(session_id.to_s)[0, 15]

      Gitlab::Redis::SharedState.with do |redis|
        ttl_session = redis.ttl("gitlab:awareness:session:#{subject_id}:users")
        ttl_user = redis.ttl("gitlab:awareness:user:#{user.id}:sessions")

        expect(ttl_session).to be > 0
        expect(ttl_user).to be > 0
      end
    end

    it "fetches user(s) from database" do
      subject.join(user)

      expect(subject.users.first).to eql(user)
    end

    it "fetches and filters online user(s) from database" do
      subject.join(user)

      travel 2.hours do
        subject.join(user2)

        online_users = subject.online_users_with_last_activity
        online_user, _ = online_users.first

        expect(online_users.size).to be 1
        expect(online_user).to eql(user2)
      end
    end
  end

  describe "when a user leaves a session" do
    it "changes number of session members" do
      subject.join(user)

      expect { subject.leave(user) }.to change(subject, :size).by(-1)
    end

    it "destroys the session when it was the last user" do
      subject.join(user)

      expect { subject.leave(user) }.to change(subject, :id).to(nil)
    end
  end

  describe "when last user leaves a session" do
    it "session and user keys are removed" do
      subject.join(user)

      Gitlab::Redis::SharedState.with do |redis|
        expect { subject.leave(user) }
          .to change { redis.scan_each(match: "gitlab:awareness:*").to_a.size }
                .to(0)
      end
    end
  end
end