File: optimistic_locking_spec.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (127 lines) | stat: -rw-r--r-- 3,737 bytes parent folder | download | duplicates (4)
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
require_relative "spec_helper"

describe "optimistic_locking plugin" do
  before do
    @c = Class.new(Sequel::Model(:people)) do
    end
    h = {1=>{:id=>1, :name=>'John', :lock_version=>2}}
    lv = @lv = "lock_version".dup
    @c.dataset = @c.dataset.with_numrows(proc do |sql|
      case sql
      when /UPDATE people SET (name|#{lv}) = ('Jim'|'Bob'|\d+), (?:name|#{lv}) = ('Jim'|'Bob'|\d+) WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
        name, nlv = $1 == 'name' ? [$2, $3] : [$3, $2]
        m = h[$4.to_i]
        if m && m[:lock_version] == $5.to_i
          m.merge!(:name=>name.gsub("'", ''), :lock_version=>nlv.to_i)
          1
        else
          0
        end
      when /UPDATE people SET #{lv} = (\d+) WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
        m = h[$2.to_i]
        if m && m[:lock_version] == $3.to_i
          m.merge!(:lock_version=>$1.to_i)
          1
        else
          0
        end
      when /DELETE FROM people WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
        m = h[$1.to_i]
        if m && m[lv.to_sym] == $2.to_i
          h.delete[$1.to_i]
          1
        else
          0
        end
      else
        puts sql
      end
    end).with_fetch(proc do |sql|
      m = h[1].dup
      v = m.delete(:lock_version)
      m[lv.to_sym] = v
      m
    end)
    @c.columns :id, :name, :lock_version
    @c.plugin :optimistic_locking
  end

  it "should raise an error when updating a stale record" do
    p1 = @c[1]
    p2 = @c[1]
    p1.update(:name=>'Jim')
    proc{p2.update(:name=>'Bob')}.must_raise(Sequel::Plugins::OptimisticLocking::Error)
  end 

  it "should raise an error when destroying a stale record" do
    p1 = @c[1]
    p2 = @c[1]
    p1.update(:name=>'Jim')
    proc{p2.destroy}.must_raise(Sequel::Plugins::OptimisticLocking::Error)
  end 

  it "should not raise an error when updating the same record twice" do
    p1 = @c[1]
    p1.update(:name=>'Jim')
    p1.update(:name=>'Bob')
  end

  it "should allow changing the lock column via model.lock_column=" do
    @lv.replace('lv')
    @c.columns :id, :name, :lv
    @c.lock_column = :lv
    p1 = @c[1]
    p2 = @c[1]
    p1.update(:name=>'Jim')
    proc{p2.update(:name=>'Bob')}.must_raise(Sequel::Plugins::OptimisticLocking::Error)
  end

  it "should allow changing the lock column via plugin option" do
    @lv.replace('lv')
    @c.columns :id, :name, :lv
    @c.plugin :optimistic_locking, :lock_column=>:lv
    p1 = @c[1]
    p2 = @c[1]
    p1.update(:name=>'Jim')
    proc{p2.destroy}.must_raise(Sequel::Plugins::OptimisticLocking::Error)
  end

  it "should work when subclassing" do
    c = Class.new(@c)
    p1 = c[1]
    p2 = c[1]
    p1.update(:name=>'Jim')
    proc{p2.update(:name=>'Bob')}.must_raise(Sequel::Plugins::OptimisticLocking::Error)
  end

  it "should work correctly if attempting to refresh and save again after a failed save" do
    p1 = @c[1]
    p2 = @c[1]
    p1.update(:name=>'Jim')
    begin
      p2.update(:name=>'Bob')
    rescue Sequel::Plugins::OptimisticLocking::Error
      p2.refresh
      @c.db.sqls
      p2.update(:name=>'Bob')
    end
    @c.db.sqls.must_equal ["UPDATE people SET name = 'Bob', lock_version = 4 WHERE ((id = 1) AND (lock_version = 3))"]
  end

  it "should increment the lock column when #modified! even if no columns are changed" do
    p1 = @c[1]
    p1.modified!
    lv = p1.lock_version
    p1.save_changes
    p1.lock_version.must_equal lv + 1
  end

  it "should not increment the lock column when the update fails" do
    @c.dataset = @c.dataset.with_extend{def update(_) raise end}
    p1 = @c[1]
    p1.modified!
    lv = p1.lock_version
    proc{p1.save_changes}.must_raise(RuntimeError)
    p1.lock_version.must_equal lv
  end
end