forked from paper-trail-gem/paper_trail
/
version_spec.rb
278 lines (249 loc) · 10.8 KB
/
version_spec.rb
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# frozen_string_literal: true
require "spec_helper"
module PaperTrail
::RSpec.describe Version, type: :model do
describe "object_changes column", versioning: true do
let(:widget) { Widget.create!(name: "Dashboard") }
let(:value) { widget.versions.last.object_changes }
context "serializer is YAML" do
specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML }
it "store out as a plain hash" do
expect(value).not_to include("HashWithIndifferentAccess")
end
end
context "with object_changes_adapter" do
after do
PaperTrail.config.object_changes_adapter = nil
end
it "creates a version with custom changes" do
adapter = instance_spy("CustomObjectChangesAdapter")
PaperTrail.config.object_changes_adapter = adapter
allow(adapter).to(
receive(:diff).with(
hash_including("name" => [nil, "Dashboard"])
).and_return([["name", nil, "Dashboard"]])
)
expect(widget.versions.last.object_changes).to eq("---\n- - name\n - \n - Dashboard\n")
expect(adapter).to have_received(:diff)
end
it "defaults to the original behavior" do
adapter = Class.new.new
PaperTrail.config.object_changes_adapter = adapter
expect(widget.versions.last.object_changes).to start_with("---")
end
end
context "serializer is JSON" do
before do
PaperTrail.serializer = PaperTrail::Serializers::JSON
end
it "store out as a plain hash" do
expect(value).not_to include("HashWithIndifferentAccess")
end
after do
PaperTrail.serializer = PaperTrail::Serializers::YAML
end
end
end
describe "#paper_trail_originator" do
context "no previous versions" do
it "returns nil" do
expect(PaperTrail::Version.new.paper_trail_originator).to be_nil
end
end
context "has previous version", versioning: true do
it "returns name of whodunnit" do
name = FFaker::Name.name
widget = Widget.create!(name: FFaker::Name.name)
widget.versions.first.update!(whodunnit: name)
widget.update!(name: FFaker::Name.first_name)
expect(widget.versions.last.paper_trail_originator).to eq(name)
end
end
end
describe "#previous" do
context "no previous versions" do
it "returns nil" do
expect(PaperTrail::Version.new.previous).to be_nil
end
end
context "has previous version", versioning: true do
it "returns a PaperTrail::Version" do
name = FFaker::Name.name
widget = Widget.create!(name: FFaker::Name.name)
widget.versions.first.update!(whodunnit: name)
widget.update!(name: FFaker::Name.first_name)
expect(widget.versions.last.previous).to be_instance_of(PaperTrail::Version)
end
end
end
describe "#terminator" do
it "is an alias for the `whodunnit` attribute" do
attributes = { whodunnit: FFaker::Name.first_name }
version = PaperTrail::Version.new(attributes)
expect(version.terminator).to eq(attributes[:whodunnit])
end
end
describe "#version_author" do
it "is an alias for the `terminator` method" do
version = PaperTrail::Version.new
expect(version.method(:version_author)).to eq(version.method(:terminator))
end
end
context "changing the data type of database columns on the fly" do
# TODO: Changing the data type of these database columns in the middle
# of the test suite adds a fair amount of complication. Is there a better
# way? We already have a `json_versions` table in our tests, maybe we
# could use that and add a `jsonb_versions` table?
column_overrides = [false]
if ENV["DB"] == "postgres" && ::ActiveRecord::VERSION::MAJOR >= 4
column_overrides << "json"
# 'jsonb' column types are only supported for ActiveRecord 4.2+
column_overrides << "jsonb" if ::ActiveRecord::VERSION::STRING >= "4.2"
end
column_overrides.shuffle.each do |column_datatype_override|
context "with a #{column_datatype_override || 'text'} column" do
let(:widget) { Widget.new }
let(:name) { FFaker::Name.first_name }
let(:int) { column_datatype_override ? 1 : rand(2..6) }
before do
if column_datatype_override
ActiveRecord::Base.connection.execute("SAVEPOINT pgtest;")
%w[object object_changes].each do |column|
ActiveRecord::Base.connection.execute(
"ALTER TABLE versions DROP COLUMN #{column};"
)
ActiveRecord::Base.connection.execute(
"ALTER TABLE versions ADD COLUMN #{column} #{column_datatype_override};"
)
end
PaperTrail::Version.reset_column_information
end
end
after do
PaperTrail.serializer = PaperTrail::Serializers::YAML
if column_datatype_override
ActiveRecord::Base.connection.execute("ROLLBACK TO SAVEPOINT pgtest;")
PaperTrail::Version.reset_column_information
end
end
describe "#where_object", versioning: true do
it "requires its argument to be a Hash" do
widget.update!(name: name, an_integer: int)
widget.update!(name: "foobar", an_integer: 100)
widget.update!(name: FFaker::Name.last_name, an_integer: 15)
expect {
PaperTrail::Version.where_object(:foo)
}.to raise_error(ArgumentError)
expect {
PaperTrail::Version.where_object([])
}.to raise_error(ArgumentError)
end
context "YAML serializer" do
it "locates versions according to their `object` contents" do
expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML
widget.update!(name: name, an_integer: int)
widget.update!(name: "foobar", an_integer: 100)
widget.update!(name: FFaker::Name.last_name, an_integer: 15)
expect(
PaperTrail::Version.where_object(an_integer: int)
).to eq([widget.versions[1]])
expect(
PaperTrail::Version.where_object(name: name)
).to eq([widget.versions[1]])
expect(
PaperTrail::Version.where_object(an_integer: 100)
).to eq([widget.versions[2]])
end
end
context "JSON serializer" do
it "locates versions according to their `object` contents" do
PaperTrail.serializer = PaperTrail::Serializers::JSON
expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON
widget.update!(name: name, an_integer: int)
widget.update!(name: "foobar", an_integer: 100)
widget.update!(name: FFaker::Name.last_name, an_integer: 15)
expect(
PaperTrail::Version.where_object(an_integer: int)
).to eq([widget.versions[1]])
expect(
PaperTrail::Version.where_object(name: name)
).to eq([widget.versions[1]])
expect(
PaperTrail::Version.where_object(an_integer: 100)
).to eq([widget.versions[2]])
end
end
end
describe "#where_object_changes", versioning: true do
it "requires its argument to be a Hash" do
expect {
PaperTrail::Version.where_object_changes(:foo)
}.to raise_error(ArgumentError)
expect {
PaperTrail::Version.where_object_changes([])
}.to raise_error(ArgumentError)
end
context "with object_changes_adapter configured" do
after do
PaperTrail.config.object_changes_adapter = nil
end
it "calls the adapter's where_object_changes method" do
adapter = instance_spy("CustomObjectChangesAdapter")
bicycle = Bicycle.create!(name: "abc")
allow(adapter).to(
receive(:where_object_changes).with(Version, name: "abc")
).and_return(bicycle.versions[0..1])
PaperTrail.config.object_changes_adapter = adapter
expect(
bicycle.versions.where_object_changes(name: "abc")
).to match_array(bicycle.versions[0..1])
expect(adapter).to have_received(:where_object_changes)
end
it "defaults to the original behavior" do
adapter = Class.new.new
PaperTrail.config.object_changes_adapter = adapter
bicycle = Bicycle.create!(name: "abc")
if column_datatype_override
expect(
bicycle.versions.where_object_changes(name: "abc")
).to match_array(bicycle.versions[0..1])
else
expect do
bicycle.versions.where_object_changes(name: "abc")
end.to raise_error(/no longer supports reading YAML/)
end
end
end
# Only test json and jsonb columns. where_object_changes no longer
# supports text columns.
if column_datatype_override
it "locates versions according to their object_changes contents" do
widget.update!(name: name, an_integer: 0)
widget.update!(name: "foobar", an_integer: 100)
widget.update!(name: FFaker::Name.last_name, an_integer: int)
expect(
widget.versions.where_object_changes(name: name)
).to eq(widget.versions[0..1])
expect(
widget.versions.where_object_changes(an_integer: 100)
).to eq(widget.versions[1..2])
expect(
widget.versions.where_object_changes(an_integer: int)
).to eq([widget.versions.last])
expect(
widget.versions.where_object_changes(an_integer: 100, name: "foobar")
).to eq(widget.versions[1..2])
end
else
it "raises error" do
expect {
widget.versions.where_object_changes(name: "foo").to_a
}.to(raise_error(/no longer supports reading YAML from a text column/))
end
end
end
end
end
end
end
end