janko 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,400 @@
1
+ require "spec_helper"
2
+ require "janko/merge"
3
+
4
+ RSpec.shared_examples_for "a merger" do
5
+ around :each do |example|
6
+ begin
7
+ connection.exec(<<-END)
8
+ BEGIN;
9
+ CREATE TEMP TABLE merge_test(id SERIAL NOT NULL,
10
+ user_id INTEGER, title TEXT, content TEXT,
11
+ vector TSVECTOR, votes INTEGER DEFAULT 0)
12
+ ON COMMIT DROP;
13
+ CREATE UNIQUE INDEX index_merge_test_id ON merge_test(id);
14
+ CREATE UNIQUE INDEX index_merge_test_unique_content
15
+ ON merge_test(user_id, content);
16
+ END
17
+ merge.connect(connection)
18
+ example.run
19
+ rescue
20
+ raise
21
+ ensure
22
+ connection.exec("ROLLBACK")
23
+ end
24
+ end
25
+
26
+ let(:connection) { Janko::Connection.default }
27
+
28
+ let(:merge) { subject.set_table("merge_test") }
29
+
30
+ def select_all
31
+ result = connection.exec(<<-END)
32
+ SELECT * FROM merge_test ORDER BY id asc
33
+ END
34
+ output = []
35
+ result.each { |tuple| output.push(tuple) }
36
+ output
37
+ end
38
+
39
+ let(:results) { select_all }
40
+
41
+ it "#start preserves state" do
42
+ merge.start
43
+ expect(lambda { merge.insert(:id) }).to raise_error(RuntimeError)
44
+ end
45
+
46
+ it "#stop allows state changes" do
47
+ merge.start.stop
48
+ expect(lambda { merge.insert(:id) }).to_not raise_error
49
+ end
50
+
51
+ it "#push" do
52
+ record = { title: "Hello, world!" }
53
+ merge.start.push(record).stop
54
+ expect(results.count).to eq(1)
55
+ expect(results.first["title"]).to eq(record[:title])
56
+ end
57
+
58
+ describe "#insert" do
59
+ let(:row) { { id: 42, user_id: 1, title: "foo", content: "bar" } }
60
+
61
+ it "defaults to everything but id" do
62
+ merge.start.push(row).stop
63
+ expect(results.count).to eq(1)
64
+ expect(results.first["id"]).to eq("1")
65
+ expect(results.first["title"]).to eq(row[:title])
66
+ expect(results.first["content"]).to eq(row[:content])
67
+ expect(results.first["user_id"]).to eq(row[:user_id].to_s)
68
+ end
69
+
70
+ it "specific fields only" do
71
+ merge.insert(:title).start.push(row).stop
72
+ expect(results.count).to eq(1)
73
+ expect(results.first["id"]).to eq("1")
74
+ expect(results.first["title"]).to eq(row[:title])
75
+ expect(results.first["content"]).to be_nil
76
+ expect(results.first["user_id"]).to be_nil
77
+ end
78
+
79
+ it "multiple rows" do
80
+ merge.start.push(title: "foo").push(title: "bar").stop
81
+ expect(results.count).to eq(2)
82
+ end
83
+
84
+ describe "#alter" do
85
+ it "#default from user" do
86
+ merge.insert(:title)
87
+ merge.alter(:title) { |f| f.default("upper('foo')") }
88
+ merge.start.push(title: nil).stop
89
+ expect(results.first["title"]).to eq("FOO")
90
+ end
91
+
92
+ it "#default from database" do
93
+ merge.insert(:id)
94
+ merge.alter(:id) { |f| f.default(Janko::DEFAULT) }
95
+ merge.start.push(id: nil).stop
96
+ expect(results.first["id"]).to eq("1")
97
+ end
98
+
99
+ it "#default keep existing ignored" do
100
+ merge.insert(:title)
101
+ merge.alter(:title) { |f| f.default(Janko::KEEP) }
102
+ merge.start.push(id: 1, title: nil).stop
103
+ expect(results.count).to eq(1)
104
+ expect(results.first["title"]).to be_nil
105
+ end
106
+
107
+ it "#default keep if nil, otherwise use database default" do
108
+ merge.insert(:title).alter(:title) { |f|
109
+ f.default(Janko::DEFAULT | Janko::KEEP) }
110
+ merge.start.push(id: 1, title: nil).stop
111
+ expect(results.count).to eq(1)
112
+ expect(results.first["title"]).to be_nil
113
+ end
114
+
115
+ it "#wrap function" do
116
+ merge.insert(:title)
117
+ merge.alter(:title) { |f| f.wrap("upper($NEW)") }
118
+ merge.start.push(title: "foo").stop
119
+ expect(results.first["title"]).to eq("FOO")
120
+ end
121
+
122
+ it "#wrap cast" do
123
+ merge.insert(:vector)
124
+ merge.alter(:vector) { |f| f.wrap("$NEW::tsvector") }
125
+ merge.start.push(vector: "hello:1A world:2A").stop
126
+ expect(results.first["vector"]).to eq("'hello':1A 'world':2A")
127
+ end
128
+
129
+ it "#wrap value" do
130
+ merge.insert(:votes)
131
+ merge.alter(:votes) { |f| f.wrap("3") }
132
+ merge.start.push(votes: "42").stop
133
+ expect(results.first["votes"]).to eq("3")
134
+ end
135
+ end
136
+ end
137
+
138
+ describe "#key" do
139
+ it "defaults to id" do
140
+ original = { title: "Hello, world!" }
141
+ update = { id: 1, title: "こんにちは" }
142
+ merge.start.push(original).stop
143
+ merge.start.push(update).stop
144
+ expect(results.count).to eq(1)
145
+ expect(results.first["id"]).to eq("1")
146
+ expect(results.first["title"]).to eq(update[:title])
147
+ end
148
+
149
+ it "one field" do
150
+ original = { title: "foo", content: "bar" }
151
+ update = { title: "quux", content: "bar" }
152
+ merge.key(:content)
153
+ merge.start.push(original).stop
154
+ merge.start.push(update).stop
155
+ expect(results.count).to eq(1)
156
+ expect(results.first["title"]).to eq(update[:title])
157
+ expect(results.first["content"]).to eq(update[:content])
158
+ end
159
+
160
+ it "multiple fields" do
161
+ original = { title: "foo", content: "bar", user_id: 1 }
162
+ keep = { title: "foo", content: "bar", user_id: 2 }
163
+ update = { title: "foo", content: "baz", user_id: 1 }
164
+ merge.key(%w(title user_id))
165
+ merge.start.push(original).push(keep).stop
166
+ merge.start.push(update).stop
167
+ expect(results.count).to eq(2)
168
+ expect(results.first["content"]).to eq(update[:content])
169
+ expect(results.last["content"]).to eq(keep[:content])
170
+ expect(results.last["user_id"]).to eq(keep[:user_id].to_s)
171
+ end
172
+ end
173
+
174
+ describe "#update" do
175
+ let(:original) { { title: "foo", content: "bar", votes: 1 } }
176
+
177
+ let(:update) { { id: 1, title: "baz", content: "bang" } }
178
+
179
+ before(:each) { merge.start.push(original).stop }
180
+
181
+ it "defaults to everything but id" do
182
+ merge.start.push(update).stop
183
+ expect(results.count).to eq(1)
184
+ expect(results.first["id"]).to eq("1")
185
+ expect(results.first["title"]).to eq(update[:title])
186
+ expect(results.first["content"]).to eq(update[:content])
187
+ expect(results.first["user_id"]).to be_nil
188
+ end
189
+
190
+ it "specific fields only" do
191
+ merge.update(:title).start.push(update).stop
192
+ expect(results.count).to eq(1)
193
+ expect(results.first["title"]).to eq(update[:title])
194
+ expect(results.first["content"]).to eq(original[:content])
195
+ end
196
+
197
+ describe "#alter" do
198
+ it "#default NULL" do
199
+ merge.update(:votes)
200
+ merge.alter(:votes) { |f| f.default(nil) }
201
+ merge.start.push(update).stop
202
+ expect(results.count).to eq(1)
203
+ expect(results.first["votes"]).to be_nil
204
+ end
205
+
206
+ it "#default from user" do
207
+ merge.update(:votes)
208
+ merge.alter(:votes) { |f| f.default("round(3.14)") }
209
+ merge.start.push(update).stop
210
+ expect(results.count).to eq(1)
211
+ expect(results.first["votes"]).to eq("3")
212
+ end
213
+
214
+ it "#default from database" do
215
+ merge.update(:votes)
216
+ merge.alter(:votes) { |f| f.default(Janko::DEFAULT) }
217
+ merge.start.push(update).stop
218
+ expect(results.count).to eq(1)
219
+ expect(results.first["votes"]).to eq("0")
220
+ end
221
+
222
+ it "#default keep existing" do
223
+ merge.update(:title)
224
+ merge.alter(:title) { |f| f.default(Janko::KEEP) }
225
+ merge.start.push(id: 1, title: nil).stop
226
+ expect(results.count).to eq(1)
227
+ expect(results.first["title"]).to eq(original[:title])
228
+ end
229
+
230
+ it "#default keep if nil, otherwise use database default" do
231
+ merge.update(:title).alter(:title) { |f|
232
+ f.default(Janko::DEFAULT | Janko::KEEP) }
233
+ merge.start.push(id: 1, title: nil).stop
234
+ expect(results.count).to eq(1)
235
+ expect(results.first["title"]).to eq(original[:title])
236
+ end
237
+
238
+ it "#wrap function" do
239
+ merge.update(:title)
240
+ merge.alter(:title) { |f| f.wrap("upper($NEW)") }
241
+ merge.start.push(update).stop
242
+ expect(results.first["title"]).to eq("BAZ")
243
+ end
244
+
245
+ it "#wrap cast" do
246
+ merge.update(:title)
247
+ merge.alter(:title) { |f| f.wrap("$NEW::text") }
248
+ merge.start.push(update).stop
249
+ expect(results.first["title"]).to eq("baz")
250
+ end
251
+
252
+ it "#on_update alter existing value" do
253
+ merge.update(:title, :votes)
254
+ merge.alter(:votes) { |f| f.on_update("$OLD + 1") }
255
+ merge.start.push(update).stop
256
+ expect(results.first["votes"]).to eq("2")
257
+ end
258
+ end
259
+
260
+ end
261
+
262
+ describe "#returning" do
263
+ def insert_and_update
264
+ merge.start.push(title: "foo").stop
265
+ merge.start
266
+ merge.push(id: 1, title: "bar")
267
+ merge.push(title: "baz")
268
+ merge.stop
269
+ end
270
+
271
+ it "inserted" do
272
+ merge.returning(:inserted)
273
+ insert_and_update
274
+ expect(merge.result.inserted.count).to eq(1)
275
+ expect(merge.result.updated.count).to eq(0)
276
+ end
277
+
278
+ it "updated" do
279
+ merge.returning(:updated)
280
+ insert_and_update
281
+ expect(merge.result.inserted.count).to eq(0)
282
+ expect(merge.result.updated.count).to eq(1)
283
+ end
284
+
285
+ it "all" do
286
+ merge.returning(:all)
287
+ insert_and_update
288
+ expect(merge.result.inserted.count).to eq(1)
289
+ expect(merge.result.updated.count).to eq(1)
290
+ end
291
+
292
+ it "none" do
293
+ merge.returning(:none)
294
+ insert_and_update
295
+ expect(merge.result.inserted.count).to eq(0)
296
+ expect(merge.result.updated.count).to eq(0)
297
+ end
298
+
299
+ it "invalid" do
300
+ expect(lambda { merge.returning(:ducks) })
301
+ .to raise_error(RuntimeError)
302
+ end
303
+
304
+ it "into object" do
305
+ collector = double
306
+ merge.returning(:all).set_collector(collector)
307
+ expect(collector).to receive(:push).at_least(3).times
308
+ expect(collector).to receive(:clear).at_least(:once)
309
+ insert_and_update
310
+ end
311
+
312
+ pending "into table"
313
+ end
314
+
315
+ describe "#select" do
316
+ let(:record) { { title: "foo", content: "bar", user_id: 1 } }
317
+
318
+ let(:inserted) do
319
+ merge.returning(:inserted).start.push(record).stop
320
+ merge.result.inserted.first
321
+ end
322
+
323
+ it "all columns by default" do
324
+ expect(inserted["id"]).to eq("1")
325
+ expect(inserted["title"]).to eq(record[:title])
326
+ expect(inserted["content"]).to eq(record[:content])
327
+ expect(inserted["user_id"]).to eq(record[:user_id].to_s)
328
+ end
329
+
330
+ it "select columns" do
331
+ merge.select(:id, :title)
332
+ expect(inserted.count).to eq(2)
333
+ expect(inserted["id"]).to eq("1")
334
+ expect(inserted["title"]).to eq(record[:title])
335
+ end
336
+ end
337
+
338
+ describe "#set_locking" do
339
+ let(:record) { { title: "foo", content: "bar", user_id: 1 } }
340
+
341
+ it "true" do
342
+ merge.set_locking(true).start.push(record).stop
343
+ expect(results.count).to eq(1)
344
+ expect(results.first["title"]).to eq(record[:title])
345
+ end
346
+
347
+ it "false" do
348
+ merge.set_locking(false).start.push(record).stop
349
+ expect(results.count).to eq(1)
350
+ expect(results.first["title"]).to eq(record[:title])
351
+ end
352
+ end
353
+
354
+ describe "#set_transaction" do
355
+ let(:record) { { title: "foo", content: "bar", user_id: 1 } }
356
+
357
+ it "true" do
358
+ merge.set_transaction(true).start.push(record).stop
359
+ expect(results.count).to eq(1)
360
+ expect(results.first["title"]).to eq(record[:title])
361
+ end
362
+
363
+ it "false" do
364
+ merge.set_transaction(false).start.push(record).stop
365
+ expect(results.count).to eq(1)
366
+ expect(results.first["title"]).to eq(record[:title])
367
+ end
368
+ end
369
+
370
+ it "inserts on null key" do
371
+ merge.start.push(title: "foo", content: "bar").stop
372
+ merge.start.push(title: nil, content: "baz").stop
373
+ expect(select_all.count).to eq(2)
374
+ merge.key(:title).start
375
+ merge.push(title: nil, content: "bang")
376
+ merge.push(title: "foo", content: "quux")
377
+ merge.stop
378
+ expect(results.count).to eq(3)
379
+ expect(results).to be_any { |r|
380
+ r["title"].nil? and r["content"] == "baz" }
381
+ expect(results).to be_any { |r|
382
+ r["title"].nil? and r["content"] == "bang" }
383
+ expect(results).to be_any { |r|
384
+ r["title"] == "foo" and r["content"] == "quux" }
385
+ end
386
+ end
387
+
388
+ RSpec.describe Janko::Merge do
389
+ describe "#strategy single" do
390
+ let(:subject) { Janko::Merge.new.use(Janko::SingleMerge) }
391
+
392
+ it_behaves_like "a merger"
393
+ end
394
+
395
+ describe "#strategy bulk" do
396
+ let(:subject) { Janko::Merge.new.use(Janko::BulkMerge) }
397
+
398
+ it_behaves_like "a merger"
399
+ end
400
+ end
@@ -0,0 +1,32 @@
1
+ root = File.join(File.dirname(File.expand_path(__FILE__)), "..")
2
+ $LOAD_PATH.unshift(root) unless $LOAD_PATH.include?(root)
3
+
4
+ require "simplecov"
5
+ require "config/environment"
6
+
7
+ SimpleCov.start do
8
+ add_filter "/spec/"
9
+ add_filter "/config/"
10
+ end
11
+
12
+ #
13
+ # http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
14
+ #
15
+ RSpec.configure do |config|
16
+ config.filter_run(:focus)
17
+ config.run_all_when_everything_filtered = true
18
+ config.order = :random
19
+ config.default_formatter = "doc" if config.files_to_run.one?
20
+ Kernel.srand(config.seed)
21
+
22
+ # config.profile_examples = 10
23
+
24
+ config.expect_with :rspec do |expectations|
25
+ expectations.syntax = :expect
26
+ end
27
+
28
+ config.mock_with :rspec do |mocks|
29
+ mocks.syntax = :expect
30
+ mocks.verify_partial_doubles = true
31
+ end
32
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+ require "janko/tagged_column"
3
+
4
+ RSpec.describe Janko::TaggedColumn do
5
+ describe "parent" do
6
+ let(:target) { double }
7
+
8
+ let(:subject) { Janko::TaggedColumn.new(parent: target) }
9
+
10
+ it "#connection" do
11
+ expect(target).to receive(:connection)
12
+ subject.connection
13
+ end
14
+
15
+ it "#table" do
16
+ expect(target).to receive(:table)
17
+ subject.table
18
+ end
19
+ end
20
+
21
+ it "#set" do
22
+ subject = Janko::TaggedColumn.new
23
+ expect(subject.name).to be_nil
24
+ subject.set(name: "field")
25
+ expect(subject.name).to eq("field")
26
+ end
27
+
28
+ describe "#default" do
29
+ let(:parent) { double }
30
+
31
+ let(:subject) { Janko::TaggedColumn.new(parent: parent,
32
+ name: "field") }
33
+
34
+ it "none by default" do
35
+ expect(subject.to_setter("left", "right")).to \
36
+ eq('"field" = "right"."field"')
37
+ end
38
+
39
+ it "user-specified" do
40
+ subject.default("current_time")
41
+ expect(subject.to_setter("left", "right")).to \
42
+ eq('"field" = COALESCE("right"."field", current_time)')
43
+ end
44
+
45
+ it "from the database" do
46
+ connection = double
47
+ expect(parent).to receive(:connection).and_return(connection)
48
+ expect(parent).to receive(:table)
49
+ expect(connection).to receive(:column_default).and_return("NEXT")
50
+ subject.default(Janko::Constants::DEFAULT)
51
+ expect(subject.to_setter("left", "right")).to \
52
+ eq('"field" = COALESCE("right"."field", NEXT)')
53
+ end
54
+ end
55
+ end
56
+