gitdocs 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +6 -14
  2. data/.codeclimate.yml +26 -0
  3. data/.rubocop.yml +8 -2
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG +13 -0
  6. data/Gemfile +1 -1
  7. data/README.md +7 -6
  8. data/Rakefile +31 -5
  9. data/bin/gitdocs +1 -0
  10. data/config.ru +6 -4
  11. data/gitdocs.gemspec +22 -19
  12. data/lib/gitdocs.rb +54 -16
  13. data/lib/gitdocs/browser_app.rb +34 -41
  14. data/lib/gitdocs/cli.rb +41 -32
  15. data/lib/gitdocs/configuration.rb +40 -101
  16. data/lib/gitdocs/git_notifier.rb +111 -0
  17. data/lib/gitdocs/initializer.rb +83 -0
  18. data/lib/gitdocs/manager.rb +90 -60
  19. data/lib/gitdocs/migration/004_add_index_for_path.rb +1 -1
  20. data/lib/gitdocs/notifier.rb +70 -104
  21. data/lib/gitdocs/rendering_helper.rb +3 -0
  22. data/lib/gitdocs/repository.rb +324 -307
  23. data/lib/gitdocs/repository/committer.rb +77 -0
  24. data/lib/gitdocs/repository/path.rb +157 -140
  25. data/lib/gitdocs/search.rb +40 -25
  26. data/lib/gitdocs/settings_app.rb +5 -3
  27. data/lib/gitdocs/share.rb +64 -0
  28. data/lib/gitdocs/synchronizer.rb +40 -0
  29. data/lib/gitdocs/version.rb +1 -1
  30. data/lib/gitdocs/views/_header.haml +2 -2
  31. data/lib/gitdocs/views/dir.haml +3 -3
  32. data/lib/gitdocs/views/edit.haml +1 -1
  33. data/lib/gitdocs/views/file.haml +1 -1
  34. data/lib/gitdocs/views/home.haml +3 -3
  35. data/lib/gitdocs/views/layout.haml +13 -13
  36. data/lib/gitdocs/views/revisions.haml +3 -3
  37. data/lib/gitdocs/views/search.haml +1 -1
  38. data/lib/gitdocs/views/settings.haml +6 -6
  39. data/test/integration/cli/full_sync_test.rb +83 -0
  40. data/test/integration/cli/share_management_test.rb +29 -0
  41. data/test/integration/cli/status_test.rb +14 -0
  42. data/test/integration/test_helper.rb +185 -151
  43. data/test/integration/{browse_test.rb → web/browse_test.rb} +11 -29
  44. data/test/integration/web/share_management_test.rb +46 -0
  45. data/test/support/git_factory.rb +276 -0
  46. data/test/unit/browser_app_test.rb +346 -0
  47. data/test/unit/configuration_test.rb +8 -70
  48. data/test/unit/git_notifier_test.rb +116 -0
  49. data/test/unit/gitdocs_test.rb +90 -0
  50. data/test/unit/manager_test.rb +36 -0
  51. data/test/unit/notifier_test.rb +60 -124
  52. data/test/unit/repository_committer_test.rb +111 -0
  53. data/test/unit/repository_path_test.rb +92 -76
  54. data/test/unit/repository_test.rb +243 -356
  55. data/test/unit/search_test.rb +15 -0
  56. data/test/unit/settings_app_test.rb +80 -0
  57. data/test/unit/share_test.rb +97 -0
  58. data/test/unit/test_helper.rb +17 -3
  59. metadata +114 -108
  60. data/lib/gitdocs/runner.rb +0 -108
  61. data/lib/gitdocs/server.rb +0 -62
  62. data/test/integration/full_sync_test.rb +0 -66
  63. data/test/integration/share_management_test.rb +0 -95
  64. data/test/integration/status_test.rb +0 -21
  65. data/test/unit/runner_test.rb +0 -122
@@ -2,37 +2,35 @@
2
2
  require File.expand_path('../test_helper', __FILE__)
3
3
 
4
4
  describe Gitdocs::Repository do
5
- let(:local_repo_path) { 'tmp/unit/local' }
6
- let(:author1) { 'Art T. Fish <afish@example.com>' }
7
- let(:author2) { 'A U Thor <author@example.com>' }
8
-
9
5
  before do
10
- FileUtils.rm_rf('tmp/unit')
11
- mkdir
12
- repo = Rugged::Repository.init_at(local_repo_path)
13
- repo.config['user.email'] = 'afish@example.com'
14
- repo.config['user.name'] = 'Art T. Fish'
6
+ FileUtils.rm_rf(GitFactory.working_directory)
7
+ GitFactory.init(:local)
8
+ GitFactory.init_bare(:remote)
15
9
  end
16
10
 
17
11
  let(:repository) { Gitdocs::Repository.new(path_or_share) }
18
- # Default path for the repository object, which can be overridden by the
12
+ # Default Share for the repository object, which can be overridden by the
19
13
  # tests when necessary.
20
- let(:path_or_share) { local_repo_path }
21
- let(:remote_repo) { Rugged::Repository.init_at('tmp/unit/remote', :bare) }
22
- let(:local_repo) { Rugged::Repository.new(local_repo_path) }
14
+ let(:path_or_share) do
15
+ stub(
16
+ path: expand_path(:local),
17
+ remote_name: 'origin',
18
+ branch_name: 'master'
19
+ )
20
+ end
23
21
 
24
22
  describe 'initialize' do
25
23
  subject { repository }
26
24
 
27
25
  describe 'with a missing path' do
28
- let(:path_or_share) { 'tmp/unit/missing_path' }
26
+ let(:path_or_share) { expand_path(:missing) }
29
27
  it { subject.must_be_kind_of Gitdocs::Repository }
30
28
  it { subject.valid?.must_equal false }
31
29
  it { subject.invalid_reason.must_equal :directory_missing }
32
30
  end
33
31
 
34
32
  describe 'with a path that is not a repository' do
35
- let(:path_or_share) { 'tmp/unit/not_a_repo' }
33
+ let(:path_or_share) { expand_path(:not_a_repo) }
36
34
  before { FileUtils.mkdir_p(path_or_share) }
37
35
  it { subject.must_be_kind_of Gitdocs::Repository }
38
36
  it { subject.valid?.must_equal false }
@@ -40,6 +38,7 @@ describe Gitdocs::Repository do
40
38
  end
41
39
 
42
40
  describe 'with a string path that is a repository' do
41
+ let(:path_or_share) { expand_path(:local) }
43
42
  it { subject.must_be_kind_of Gitdocs::Repository }
44
43
  it { subject.valid?.must_equal true }
45
44
  it { subject.invalid_reason.must_be_nil }
@@ -48,35 +47,26 @@ describe Gitdocs::Repository do
48
47
  end
49
48
 
50
49
  describe 'with a share that is a repository' do
51
- let(:path_or_share) do
52
- stub(
53
- path: local_repo_path,
54
- remote_name: 'remote',
55
- branch_name: 'branch'
56
- )
57
- end
58
50
  it { subject.must_be_kind_of Gitdocs::Repository }
59
51
  it { subject.valid?.must_equal true }
60
52
  it { subject.invalid_reason.must_be_nil }
61
53
  it { subject.instance_variable_get(:@rugged).wont_be_nil }
62
54
  it { subject.instance_variable_get(:@grit).wont_be_nil }
63
- it { subject.instance_variable_get(:@branch_name).must_equal 'branch' }
64
- it { subject.instance_variable_get(:@remote_name).must_equal 'remote' }
55
+ it { subject.instance_variable_get(:@remote_name).must_equal 'origin' }
56
+ it { subject.instance_variable_get(:@branch_name).must_equal 'master' }
65
57
  end
66
58
  end
67
59
 
68
60
  describe '.clone' do
69
- subject { Gitdocs::Repository.clone(path, remote) }
70
-
71
- let(:path) { 'tmp/unit/clone' }
72
- let(:remote) { 'tmp/unit/remote' }
61
+ subject { Gitdocs::Repository.clone('tmp/unit/clone', remote) }
73
62
 
74
63
  describe 'with invalid remote' do
64
+ let(:remote) { expand_path(:invalid) }
75
65
  it { assert_raises(RuntimeError) { subject } }
76
66
  end
77
67
 
78
68
  describe 'with valid remote' do
79
- before { Rugged::Repository.init_at(remote, :bare) }
69
+ let(:remote) { expand_path(:remote) }
80
70
  it { subject.must_be_kind_of Gitdocs::Repository }
81
71
  it { subject.valid?.must_equal true }
82
72
  end
@@ -91,7 +81,7 @@ describe Gitdocs::Repository do
91
81
  end
92
82
 
93
83
  describe 'when valid' do
94
- it { subject.must_equal File.expand_path(local_repo_path) }
84
+ it { subject.must_equal expand_path(:local) }
95
85
  end
96
86
  end
97
87
 
@@ -129,7 +119,7 @@ describe Gitdocs::Repository do
129
119
  end
130
120
 
131
121
  describe 'has commits' do
132
- before { @head_oid = write_and_commit('touch_me', '', 'commit', author1) }
122
+ before { @head_oid = commit('touch_me', '') }
133
123
  it { subject.must_equal @head_oid }
134
124
  end
135
125
  end
@@ -137,14 +127,6 @@ describe Gitdocs::Repository do
137
127
  describe '#dirty?' do
138
128
  subject { repository.dirty? }
139
129
 
140
- let(:path_or_share) do
141
- stub(
142
- path: local_repo_path,
143
- remote_name: 'origin',
144
- branch_name: 'master'
145
- )
146
- end
147
-
148
130
  describe 'when invalid' do
149
131
  let(:path_or_share) { 'tmp/unit/missing' }
150
132
  it { subject.must_equal false }
@@ -161,20 +143,20 @@ describe Gitdocs::Repository do
161
143
  end
162
144
 
163
145
  describe 'and new empty directory' do
164
- before { mkdir('directory') }
146
+ before { GitFactory.mkdir(:local, 'directory') }
165
147
  it { subject.must_equal true }
166
148
  end
167
149
  end
168
150
 
169
151
  describe 'when commits exist' do
170
- before { write_and_commit('file1', 'foobar', 'initial commit', author1) }
152
+ before { commit('file1', 'foobar') }
171
153
 
172
154
  describe 'and no changes' do
173
155
  it { subject.must_equal false }
174
156
  end
175
157
 
176
158
  describe 'add empty directory' do
177
- before { mkdir('directory') }
159
+ before { GitFactory.mkdir(:local, 'directory') }
178
160
  it { subject.must_equal false }
179
161
  end
180
162
 
@@ -189,7 +171,7 @@ describe Gitdocs::Repository do
189
171
  end
190
172
 
191
173
  describe 'delete file' do
192
- before { rm_rf('file1') }
174
+ before { GitFactory.rm(:local, 'file1') }
193
175
  it { subject.must_equal true }
194
176
  end
195
177
  end
@@ -198,14 +180,6 @@ describe Gitdocs::Repository do
198
180
  describe '#need_sync' do
199
181
  subject { repository.need_sync? }
200
182
 
201
- let(:path_or_share) do
202
- stub(
203
- path: local_repo_path,
204
- remote_name: 'origin',
205
- branch_name: 'master'
206
- )
207
- end
208
-
209
183
  describe 'when invalid' do
210
184
  let(:path_or_share) { 'tmp/unit/missing' }
211
185
  it { subject.must_equal false }
@@ -216,38 +190,33 @@ describe Gitdocs::Repository do
216
190
  end
217
191
 
218
192
  describe 'when no remote commits' do
219
- before { create_local_repo_with_remote }
193
+ before { clone_remote }
220
194
 
221
195
  describe 'no local commits' do
222
196
  it { subject.must_equal false }
223
197
  end
224
198
 
225
199
  describe 'local commits' do
226
- before { write_and_commit('file1', 'beef', 'conflict commit', author1) }
200
+ before { commit('file1', 'beef') }
227
201
  it { subject.must_equal true }
228
202
  end
229
203
  end
230
204
 
231
205
  describe 'when remote commits' do
232
- before { create_local_repo_with_remote_with_commit }
206
+ before { clone_remote_with_commit }
233
207
 
234
208
  describe 'no local commits' do
235
209
  it { subject.must_equal false }
236
210
  end
237
211
 
238
212
  describe 'new local commit' do
239
- before { write_and_commit('file2', 'beef', 'conflict commit', author1) }
213
+ before { commit('file2', 'beef') }
240
214
  it { subject.must_equal true }
241
215
  end
242
216
 
243
217
  describe 'new remote commit' do
244
218
  before do
245
- bare_commit(
246
- remote_repo,
247
- 'file3', 'dead',
248
- 'second commit',
249
- 'author@example.com', 'A U Thor'
250
- )
219
+ bare_commit('file3', 'dead')
251
220
  repository.fetch
252
221
  end
253
222
 
@@ -256,14 +225,9 @@ describe Gitdocs::Repository do
256
225
 
257
226
  describe 'new local and remote commit' do
258
227
  before do
259
- bare_commit(
260
- remote_repo,
261
- 'file3', 'dead',
262
- 'second commit',
263
- 'author@example.com', 'A U Thor'
264
- )
228
+ bare_commit('file3', 'dead')
265
229
  repository.fetch
266
- write_and_commit('file4', 'beef', 'conflict commit', author1)
230
+ commit('file4', 'beef')
267
231
  end
268
232
 
269
233
  it { subject.must_equal true }
@@ -278,7 +242,9 @@ describe Gitdocs::Repository do
278
242
 
279
243
  describe 'timeout' do
280
244
  before do
281
- Grit::Repo.any_instance.stubs(:remote_fetch)
245
+ Grit::Repo
246
+ .any_instance
247
+ .stubs(:remote_fetch)
282
248
  .raises(Grit::Git::GitTimeout.new)
283
249
  end
284
250
  it { subject ; @grep_result.must_equal([]) }
@@ -287,7 +253,9 @@ describe Gitdocs::Repository do
287
253
 
288
254
  describe 'command failure' do
289
255
  before do
290
- Grit::Repo.any_instance.stubs(:remote_fetch)
256
+ Grit::Repo
257
+ .any_instance
258
+ .stubs(:remote_fetch)
291
259
  .raises(Grit::Git::CommandFailed.new('', 1, 'grep error output'))
292
260
  end
293
261
  it { subject ; @grep_result.must_equal([]) }
@@ -296,10 +264,10 @@ describe Gitdocs::Repository do
296
264
 
297
265
  describe 'success' do
298
266
  before do
299
- write_and_commit('file1', 'foo', 'commit', author1)
300
- write_and_commit('file2', 'beef', 'commit', author1)
301
- write_and_commit('file3', 'foobar', 'commit', author1)
302
- write_and_commit('file4', "foo\ndead\nbeef\nfoobar", 'commit', author1)
267
+ commit('file1', 'foo')
268
+ commit('file2', 'beef')
269
+ commit('file3', 'foobar')
270
+ commit('file4', "foo\ndead\nbeef\nfoobar")
303
271
  end
304
272
  it { subject ; @grep_result.must_equal(['file1 foo', 'file3 foobar', 'file4 foo', 'file4 foobar']) }
305
273
  it { subject.must_equal("file1:foo\nfile3:foobar\nfile4:foo\nfile4:foobar\n") }
@@ -319,37 +287,45 @@ describe Gitdocs::Repository do
319
287
  end
320
288
 
321
289
  describe 'with remote' do
322
- before { create_local_repo_with_remote }
290
+ before { clone_remote }
323
291
 
324
292
  describe 'and times out' do
325
293
  before do
326
- Grit::Repo.any_instance.stubs(:remote_fetch)
294
+ Grit::Repo
295
+ .any_instance
296
+ .stubs(:remote_fetch)
327
297
  .raises(Grit::Git::GitTimeout.new)
328
298
  end
329
- it { subject.must_equal "Fetch timed out for #{File.absolute_path(local_repo_path)}" }
299
+ it do
300
+ assert_raises(
301
+ Gitdocs::Repository::FetchError,
302
+ "Fetch timed out for #{expand_path(:local)}"
303
+ ) { subject }
304
+ end
330
305
  end
331
306
 
332
307
  describe 'and command fails' do
333
308
  before do
334
- Grit::Repo.any_instance.stubs(:remote_fetch)
309
+ Grit::Repo
310
+ .any_instance
311
+ .stubs(:remote_fetch)
335
312
  .raises(Grit::Git::CommandFailed.new('', 1, 'fetch error output'))
336
313
  end
337
- it { subject.must_equal 'fetch error output' }
314
+ it do
315
+ assert_raises(
316
+ Gitdocs::Repository::FetchError,
317
+ 'fetch error output'
318
+ ) { subject }
319
+ end
338
320
  end
339
321
 
340
322
  describe 'and success' do
341
- before do
342
- bare_commit(
343
- remote_repo,
344
- 'file1', 'deadbeef',
345
- 'commit', 'author@example.com', 'A U Thor'
346
- )
347
- end
323
+ before { bare_commit('file1', 'deadbeef') }
348
324
  it { subject.must_equal :ok }
349
325
 
350
326
  describe 'side effects' do
351
327
  before { subject }
352
- it { local_repo_remote_branch.tip.oid.wont_be_nil }
328
+ it { GitInspector.remote_oid(:local).wont_be_nil }
353
329
  end
354
330
  end
355
331
  end
@@ -358,13 +334,7 @@ describe Gitdocs::Repository do
358
334
  describe '#merge' do
359
335
  subject { repository.merge }
360
336
 
361
- let(:path_or_share) do
362
- stub(
363
- path: local_repo_path,
364
- remote_name: 'origin',
365
- branch_name: 'master'
366
- )
367
- end
337
+ before { repository.stubs(:author_count).returns(:author_counts) }
368
338
 
369
339
  describe 'when invalid' do
370
340
  let(:path_or_share) { 'tmp/unit/missing' }
@@ -376,52 +346,53 @@ describe Gitdocs::Repository do
376
346
  end
377
347
 
378
348
  describe 'has remote but nothing to merge' do
379
- before { create_local_repo_with_remote }
349
+ before { clone_remote }
380
350
  it { subject.must_equal :ok }
381
351
  end
382
352
 
383
353
  describe 'has remote and times out' do
384
354
  before do
385
- create_local_repo_with_remote
386
- bare_commit(
387
- remote_repo,
388
- 'file1', 'deadbeef',
389
- 'commit', 'author@example.com', 'A U Thor'
390
- )
355
+ clone_remote
356
+ bare_commit('file1', 'deadbeef')
391
357
  repository.fetch
392
358
 
393
- Grit::Git.any_instance.stubs(:merge)
359
+ Grit::Git
360
+ .any_instance
361
+ .stubs(:merge)
394
362
  .raises(Grit::Git::GitTimeout.new)
395
363
  end
396
- it { subject.must_equal "Merge timed out for #{File.absolute_path(local_repo_path)}" }
364
+ it do
365
+ assert_raises(
366
+ Gitdocs::Repository::MergeError,
367
+ "Merge timed out for #{expand_path(:local)}"
368
+ ) { subject }
369
+ end
397
370
  end
398
371
 
399
372
  describe 'and fails, but does not conflict' do
400
373
  before do
401
- create_local_repo_with_remote
402
- bare_commit(
403
- remote_repo,
404
- 'file1', 'deadbeef',
405
- 'commit', 'author@example.com', 'A U Thor'
406
- )
374
+ clone_remote
375
+ bare_commit('file1', 'deadbeef')
407
376
  repository.fetch
408
377
 
409
- Grit::Git.any_instance.stubs(:merge)
378
+ Grit::Git
379
+ .any_instance
380
+ .stubs(:merge)
410
381
  .raises(Grit::Git::CommandFailed.new('', 1, 'merge error output'))
411
382
  end
412
- it { subject.must_equal 'merge error output' }
383
+ it do
384
+ assert_raises(
385
+ Gitdocs::Repository::MergeError,
386
+ 'merge error output'
387
+ ) { subject }
388
+ end
413
389
  end
414
390
 
415
391
  describe 'and there is a conflict' do
416
392
  before do
417
- create_local_repo_with_remote_with_commit
418
- bare_commit(
419
- remote_repo,
420
- 'file1', 'dead',
421
- 'second commit',
422
- 'author@example.com', 'A U Thor'
423
- )
424
- write_and_commit('file1', 'beef', 'conflict commit', author1)
393
+ clone_remote_with_commit
394
+ bare_commit('file1', 'dead')
395
+ commit('file1', 'beef')
425
396
  repository.fetch
426
397
  end
427
398
 
@@ -429,30 +400,20 @@ describe Gitdocs::Repository do
429
400
 
430
401
  describe 'side effects' do
431
402
  before { subject }
432
- it { commit_count(local_repo).must_equal 2 }
433
- it { local_file_count.must_equal 3 }
434
- it { local_file_content('file1 (f6ea049 original)').must_equal 'foobar' }
435
- it { local_file_content('file1 (18ed963)').must_equal 'beef' }
436
- it { local_file_content('file1 (7bfce5c)').must_equal 'dead' }
403
+ it { GitInspector.commit_count(:local).must_equal 2 }
404
+ it { GitInspector.file_count(:local).must_equal 3 }
405
+ it { GitInspector.file_content(:local, 'file1 (f6ea049 original)').must_equal 'foobar' }
406
+ it { GitInspector.file_content(:local, 'file1 (18ed963)').must_equal 'beef' }
407
+ it { GitInspector.file_content(:local, 'file1 (7bfce5c)').must_equal 'dead' }
437
408
  end
438
409
  end
439
410
 
440
411
  describe 'and there is a conflict, with additional files' do
441
412
  before do
442
- create_local_repo_with_remote_with_commit
443
- bare_commit(
444
- remote_repo,
445
- 'file1', 'dead',
446
- 'second commit',
447
- 'author@example.com', 'A U Thor'
448
- )
449
- bare_commit(
450
- remote_repo,
451
- 'file2', 'foo',
452
- 'second commit',
453
- 'author@example.com', 'A U Thor'
454
- )
455
- write_and_commit('file1', 'beef', 'conflict commit', author1)
413
+ clone_remote_with_commit
414
+ bare_commit('file1', 'dead')
415
+ bare_commit('file2', 'foo')
416
+ commit('file1', 'beef')
456
417
  repository.fetch
457
418
  end
458
419
 
@@ -460,46 +421,41 @@ describe Gitdocs::Repository do
460
421
 
461
422
  describe 'side effects' do
462
423
  before { subject }
463
- it { commit_count(local_repo).must_equal 2 }
464
- it { local_file_count.must_equal 3 }
465
- it { local_file_content('file1 (f6ea049 original)').must_equal 'foobar' }
466
- it { local_file_content('file1 (18ed963)').must_equal 'beef' }
467
- it { local_file_content('file2').must_equal 'foo' }
424
+ it { GitInspector.commit_count(:local).must_equal 2 }
425
+ it { GitInspector.file_count(:local).must_equal 3 }
426
+ it { GitInspector.file_content(:local, 'file1 (f6ea049 original)').must_equal 'foobar' }
427
+ it { GitInspector.file_content(:local, 'file1 (18ed963)').must_equal 'beef' }
428
+ it { GitInspector.file_content(:local, 'file2').must_equal 'foo' }
468
429
  end
469
430
  end
470
431
 
471
432
  describe 'and there are non-conflicted local commits' do
472
433
  before do
473
- create_local_repo_with_remote_with_commit
474
- write_and_commit('file1', 'beef', 'conflict commit', author1)
434
+ clone_remote_with_commit
435
+ commit('file1', 'beef')
475
436
  repository.fetch
476
437
  end
477
- it { subject.must_equal :ok }
438
+ it { subject.must_equal :author_counts }
478
439
 
479
440
  describe 'side effects' do
480
441
  before { subject }
481
- it { local_file_count.must_equal 1 }
482
- it { commit_count(local_repo).must_equal 2 }
442
+ it { GitInspector.file_count(:local).must_equal 1 }
443
+ it { GitInspector.commit_count(:local).must_equal 2 }
483
444
  end
484
445
  end
485
446
 
486
447
  describe 'when new remote commits are merged' do
487
448
  before do
488
- create_local_repo_with_remote_with_commit
489
- bare_commit(
490
- remote_repo,
491
- 'file2', 'deadbeef',
492
- 'second commit',
493
- 'author@example.com', 'A U Thor'
494
- )
449
+ clone_remote_with_commit
450
+ bare_commit('file2', 'deadbeef')
495
451
  repository.fetch
496
452
  end
497
- it { subject.must_equal :ok }
453
+ it { subject.must_equal :author_counts }
498
454
 
499
455
  describe 'side effects' do
500
456
  before { subject }
501
- it { local_file_exist?('file2').must_equal true }
502
- it { commit_count(local_repo).must_equal 2 }
457
+ it { GitInspector.file_exist?(:local, 'file2').must_equal true }
458
+ it { GitInspector.commit_count(:local).must_equal 2 }
503
459
  end
504
460
  end
505
461
  end
@@ -507,83 +463,24 @@ describe Gitdocs::Repository do
507
463
  describe '#commit' do
508
464
  subject { repository.commit }
509
465
 
510
- let(:path_or_share) do
511
- stub(
512
- path: local_repo_path,
513
- remote_name: 'origin',
514
- branch_name: 'master'
515
- )
516
- end
517
-
518
466
  describe 'when invalid' do
519
467
  let(:path_or_share) { 'tmp/unit/missing' }
520
468
  it { subject.must_be_nil }
521
469
  end
522
470
 
523
- # TODO: should test the paths which use the message file
524
-
525
- describe 'no previous commits' do
526
- describe 'nothing to commit' do
527
- it { subject.must_equal false }
528
- end
529
-
530
- describe 'changes to commit' do
531
- before do
532
- write('file1', 'foobar')
533
- mkdir('directory')
534
- end
535
- it { subject.must_equal true }
536
-
537
- describe 'side effects' do
538
- before { subject }
539
- it { local_file_exist?('directory/.gitignore').must_equal true }
540
- it { commit_count(local_repo).must_equal 1 }
541
- it { head_commit(local_repo).message.must_equal "Auto-commit from gitdocs\n" }
542
- it { local_repo_clean?.must_equal true }
543
- end
544
- end
545
- end
546
-
547
- describe 'previous commits' do
471
+ describe 'when valid' do
548
472
  before do
549
- write_and_commit('file1', 'foobar', 'initial commit', author1)
550
- write_and_commit('file2', 'deadbeef', 'second commit', author1)
551
- end
552
-
553
- describe 'nothing to commit' do
554
- it { subject.must_equal false }
555
- end
556
-
557
- describe 'changes to commit' do
558
- before do
559
- write('file1', 'foobar')
560
- rm_rf('file2')
561
- write('file3', 'foobar')
562
- mkdir('directory')
563
- end
564
- it { subject.must_equal true }
565
-
566
- describe 'side effects' do
567
- before { subject }
568
- it { local_file_exist?('directory/.gitignore').must_equal true }
569
- it { commit_count(local_repo).must_equal 3 }
570
- it { head_commit(local_repo).message.must_equal "Auto-commit from gitdocs\n" }
571
- it { local_repo_clean?.must_equal true }
572
- end
473
+ Gitdocs::Repository::Committer.expects(:new).returns(committer = mock)
474
+ committer.expects(:commit).returns(:result)
573
475
  end
476
+ it { subject.must_equal(:result) }
574
477
  end
575
478
  end
576
479
 
577
480
  describe '#push' do
578
481
  subject { repository.push }
579
482
 
580
- let(:path_or_share) do
581
- stub(
582
- path: local_repo_path,
583
- remote_name: 'origin',
584
- branch_name: 'master'
585
- )
586
- end
483
+ before { repository.stubs(:author_count).returns(:author_counts) }
587
484
 
588
485
  describe 'when invalid' do
589
486
  let(:path_or_share) { 'tmp/unit/missing' }
@@ -595,81 +492,85 @@ describe Gitdocs::Repository do
595
492
  end
596
493
 
597
494
  describe 'remote exists with no commits' do
598
- before { create_local_repo_with_remote }
495
+ before { clone_remote }
599
496
 
600
497
  describe 'and no local commits' do
601
498
  it { subject.must_equal :nothing }
602
499
 
603
500
  describe 'side effects' do
604
501
  before { subject }
605
- it { commit_count(remote_repo).must_equal 0 }
502
+ it { GitInspector.commit_count(:remote).must_equal 0 }
606
503
  end
607
504
  end
608
505
 
609
506
  describe 'and a local commit' do
610
- before { write_and_commit('file2', 'foobar', 'commit', author1) }
507
+ before { commit('file2', 'foobar') }
611
508
 
612
509
  describe 'and the push fails' do
613
510
  # Simulate an error occurring during the push
614
511
  before do
615
- Grit::Git.any_instance.stubs(:push)
512
+ Grit::Git
513
+ .any_instance
514
+ .stubs(:push)
616
515
  .raises(Grit::Git::CommandFailed.new('', 1, 'error message'))
617
516
  end
618
517
  it { subject.must_equal 'error message' }
619
518
  end
620
519
 
621
520
  describe 'and the push succeeds' do
622
- it { subject.must_equal :ok }
521
+ it { subject.must_equal :author_counts }
623
522
 
624
523
  describe 'side effects' do
625
524
  before { subject }
626
- it { commit_count(remote_repo).must_equal 1 }
525
+ it { GitInspector.commit_count(:remote).must_equal 1 }
627
526
  end
628
527
  end
629
528
  end
630
529
  end
631
530
 
632
531
  describe 'remote exists with commits' do
633
- before { create_local_repo_with_remote_with_commit }
532
+ before { clone_remote_with_commit }
634
533
 
635
534
  describe 'and no local commits' do
636
535
  it { subject.must_equal :nothing }
637
536
 
638
537
  describe 'side effects' do
639
538
  before { subject }
640
- it { commit_count(remote_repo).must_equal 1 }
539
+ it { GitInspector.commit_count(:remote).must_equal 1 }
641
540
  end
642
541
  end
643
542
 
644
543
  describe 'and a local commit' do
645
- before { write_and_commit('file2', 'foobar', 'commit', author1) }
544
+ before { commit('file2', 'foobar') }
646
545
 
647
546
  describe 'and the push fails' do
648
547
  # Simulate an error occurring during the push
649
548
  before do
650
- Grit::Git.any_instance.stubs(:push)
549
+ Grit::Git
550
+ .any_instance
551
+ .stubs(:push)
651
552
  .raises(Grit::Git::CommandFailed.new('', 1, 'error message'))
652
553
  end
653
554
  it { subject.must_equal 'error message' }
654
555
  end
655
556
 
656
557
  describe 'and the push conflicts' do
657
- before { bare_commit(remote_repo, 'file2', 'dead', 'commit', 'A U Thor', 'author@example.com') }
558
+ before { bare_commit('file2', 'dead') }
658
559
 
659
560
  it { subject.must_equal :conflict }
660
561
 
661
562
  describe 'side effects' do
662
563
  before { subject }
663
- it { commit_count(remote_repo).must_equal 2 }
564
+ it { GitInspector.commit_count(:remote).must_equal 2 }
664
565
  end
665
566
  end
666
567
 
667
568
  describe 'and the push succeeds' do
668
- it { subject.must_equal :ok }
569
+ it { subject.must_equal :author_counts }
669
570
 
670
571
  describe 'side effects' do
671
572
  before { subject }
672
- it { commit_count(remote_repo).must_equal 2 }
573
+ it { GitInspector.commit_count(:remote).must_equal 2 }
673
574
  end
674
575
  end
675
576
  end
@@ -686,46 +587,99 @@ describe Gitdocs::Repository do
686
587
 
687
588
  describe 'commits' do
688
589
  before do
689
- @intermediate_oid = write_and_commit('touch_me', 'first', 'initial commit', author1)
690
- write_and_commit('touch_me', 'second', 'commit', author1)
691
- write_and_commit('touch_me', 'third', 'commit', author2)
692
- write_and_commit('touch_me', 'fourth', 'commit', author1)
590
+ @intermediate_oid = commit('touch_me', 'first', 0)
591
+ commit('touch_me', 'second', 0)
592
+ commit('touch_me', 'third', 1)
593
+ commit('touch_me', 'fourth', 0)
693
594
  end
694
595
 
695
596
  describe 'all' do
696
597
  let(:last_oid) { nil }
697
- it { subject.must_equal(author1 => 3, author2 => 1) }
598
+ it { subject.must_equal(GitFactory.authors[0] => 3, GitFactory.authors[1] => 1) }
698
599
  end
699
600
 
700
601
  describe 'some' do
701
602
  let(:last_oid) { @intermediate_oid }
702
- it { subject.must_equal(author1 => 2, author2 => 1) }
603
+ it { subject.must_equal(GitFactory.authors[0] => 2, GitFactory.authors[1] => 1) }
703
604
  end
704
605
 
705
606
  describe 'missing oid' do
706
- let(:last_oid) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }
607
+ let(:last_oid) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }
707
608
  it { subject.must_equal({}) }
708
609
  end
709
610
  end
710
611
  end
711
612
 
712
- describe '#write_commit_message' do
713
- subject { repository.write_commit_message(commit_message) }
714
- before { subject }
613
+ describe '#synchronize' do
614
+ subject { repository.synchronize(type) }
615
+
616
+ describe 'invalid repository' do
617
+ let(:type) { :noop }
618
+ it { subject.must_equal(merge: nil, push: nil) }
619
+ end
715
620
 
716
- describe 'with missing message' do
717
- let(:commit_message) { nil }
718
- it { local_file_exist?('.gitmessage~').must_equal(false) }
621
+ describe 'fetch only' do
622
+ let(:type) { 'fetch' }
623
+
624
+ describe 'fetch failure' do
625
+ before do
626
+ repository.expects(:fetch).raises(Gitdocs::Repository::FetchError)
627
+ end
628
+ it { subject.must_equal(merge: nil, push: nil) }
629
+ end
630
+
631
+ describe 'success' do
632
+ before { repository.expects(:fetch) }
633
+ it { subject.must_equal(merge: nil, push: nil) }
634
+ end
719
635
  end
720
636
 
721
- describe 'with empty message' do
722
- let(:commit_message) { '' }
723
- it { local_file_exist?('.gitmessage~').must_equal(false) }
637
+ describe 'full' do
638
+ let(:type) { 'full' }
639
+
640
+ describe 'fetch failure' do
641
+ before do
642
+ repository.expects(:commit)
643
+ repository.expects(:fetch).raises(Gitdocs::Repository::FetchError)
644
+ end
645
+ it { subject.must_equal(merge: nil, push: nil) }
646
+ end
647
+
648
+ describe 'merge failure' do
649
+ before do
650
+ repository.expects(:commit)
651
+ repository.expects(:fetch)
652
+ repository.expects(:merge).raises(Gitdocs::Repository::MergeError, 'error')
653
+ end
654
+ it { subject.must_equal(merge: 'error', push: nil) }
655
+ end
656
+
657
+ describe 'success' do
658
+ before do
659
+ repository.expects(:commit)
660
+ repository.expects(:fetch)
661
+ repository.expects(:merge).returns('merge')
662
+ repository.expects(:push).returns('push')
663
+ end
664
+ it { subject.must_equal(merge: 'merge', push: 'push') }
665
+ end
724
666
  end
667
+ end
725
668
 
726
- describe 'with valid message' do
727
- let(:commit_message) { 'foobar' }
728
- it { local_file_content('.gitmessage~').must_equal('foobar') }
669
+ describe '#write_commit_message' do
670
+ subject { repository.write_commit_message(:message) }
671
+
672
+ describe 'when invalid' do
673
+ let(:path_or_share) { 'tmp/unit/missing' }
674
+ it { subject.must_be_nil }
675
+ end
676
+
677
+ describe 'when valid' do
678
+ before do
679
+ Gitdocs::Repository::Committer.expects(:new).returns(committer = mock)
680
+ committer.expects(:write_commit_message).with(:message).returns(:result)
681
+ end
682
+ it { subject.must_equal(:result) }
729
683
  end
730
684
  end
731
685
 
@@ -733,10 +687,10 @@ describe Gitdocs::Repository do
733
687
  subject { repository.commits_for('directory/file', 2) }
734
688
 
735
689
  before do
736
- write_and_commit('directory0/file0', '', 'initial commit', author1)
737
- write_and_commit('directory/file', 'foo', 'commit1', author1)
738
- @commit2 = write_and_commit('directory/file', 'bar', 'commit2', author2)
739
- @commit3 = write_and_commit('directory/file', 'beef', 'commit3', author2)
690
+ commit('directory0/file0', '', 0)
691
+ commit('directory/file', 'foo', 0)
692
+ @commit2 = commit('directory/file', 'bar', 1)
693
+ @commit3 = commit('directory/file', 'beef', 1)
740
694
  end
741
695
 
742
696
  it { subject.map(&:oid).must_equal([@commit3, @commit2]) }
@@ -746,9 +700,9 @@ describe Gitdocs::Repository do
746
700
  subject { repository.last_commit_for('directory/file') }
747
701
 
748
702
  before do
749
- write_and_commit('directory/file', 'foo', 'commit1', author1)
750
- write_and_commit('directory/file', 'bar', 'commit2', author2)
751
- @commit3 = write_and_commit('directory/file', 'beef', 'commit3', author2)
703
+ commit('directory/file', 'foo', 0)
704
+ commit('directory/file', 'bar', 1)
705
+ @commit3 = commit('directory/file', 'beef', 1)
752
706
  end
753
707
 
754
708
  it { subject.oid.must_equal(@commit3) }
@@ -758,9 +712,9 @@ describe Gitdocs::Repository do
758
712
  subject { repository.blob_at('directory/file', @commit) }
759
713
 
760
714
  before do
761
- write_and_commit('directory/file', 'foo', 'commit1', author1)
762
- @commit = write_and_commit('directory/file', 'bar', 'commit2', author2)
763
- write_and_commit('directory/file', 'beef', 'commit3', author2)
715
+ commit('directory/file', 'foo')
716
+ @commit = commit('directory/file', 'bar', 1)
717
+ commit('directory/file', 'beef', 1)
764
718
  end
765
719
 
766
720
  it { subject.text.must_equal('bar') }
@@ -770,107 +724,40 @@ describe Gitdocs::Repository do
770
724
 
771
725
  private
772
726
 
773
- def create_local_repo_with_remote
774
- FileUtils.rm_rf(local_repo_path)
775
- repo = Rugged::Repository.clone_at(remote_repo.path, local_repo_path)
776
- repo.config['user.email'] = 'afish@example.com'
777
- repo.config['user.name'] = 'Art T. Fish'
727
+ # @param (see GitFactory.expand_path)
728
+ # @return [String]
729
+ def expand_path(*args)
730
+ GitFactory.expand_path(*args)
778
731
  end
779
732
 
780
- def create_local_repo_with_remote_with_commit
781
- bare_commit(
782
- remote_repo,
783
- 'file1', 'foobar',
784
- 'initial commit',
785
- 'author@example.com', 'A U Thor'
786
- )
787
- FileUtils.rm_rf(local_repo_path)
788
- repo = Rugged::Repository.clone_at(remote_repo.path, local_repo_path)
789
- repo.config['user.email'] = 'afish@example.com'
790
- repo.config['user.name'] = 'Art T. Fish'
733
+ # @return (see GitFactory.clone)
734
+ def clone_remote
735
+ GitFactory.clone(:remote, 'local')
791
736
  end
792
737
 
793
- def mkdir(*path)
794
- FileUtils.mkdir_p(File.join(local_repo_path, *path))
738
+ # @return (see GitFactory.clone)
739
+ def clone_remote_with_commit
740
+ bare_commit('file1', 'foobar')
741
+ clone_remote
795
742
  end
796
743
 
744
+ # @param [String] filename
745
+ # @param [String] content
746
+ #
747
+ # @return [void]
797
748
  def write(filename, content)
798
- mkdir(File.dirname(filename))
799
- File.write(File.join(local_repo_path, filename), content)
800
- end
801
-
802
- def rm_rf(filename)
803
- FileUtils.rm_rf(File.join(local_repo_path, filename))
804
- end
805
-
806
- def write_and_commit(filename, content, commit_msg, author)
807
- mkdir(File.dirname(filename))
808
- File.write(File.join(local_repo_path, filename), content)
809
- `cd #{local_repo_path} ; git add #{filename}; git commit -m '#{commit_msg}' --author='#{author}'`
810
- `cd #{local_repo_path} ; git rev-parse HEAD`.strip
811
- end
812
-
813
- def bare_commit(repo, filename, content, message, email, name) # rubocop:disable ParameterLists
814
- index = Rugged::Index.new
815
- index.add(
816
- path: filename,
817
- oid: repo.write(content, :blob),
818
- mode: 0100644
819
- )
820
-
821
- Rugged::Commit.create(
822
- remote_repo,
823
- tree: index.write_tree(repo),
824
- author: { email: email, name: name, time: Time.now },
825
- committer: { email: email, name: name, time: Time.now },
826
- message: message,
827
- parents: repo.empty? ? [] : [repo.head.target].compact,
828
- update_ref: 'HEAD'
829
- )
830
- end
831
-
832
- def commit_count(repo)
833
- walker = Rugged::Walker.new(repo)
834
- walker.push(repo.head.target)
835
- walker.count
836
- rescue Rugged::ReferenceError
837
- # The repo does not have a head => no commits.
838
- 0
839
- end
840
-
841
- def head_commit(repo)
842
- walker = Rugged::Walker.new(repo)
843
- walker.push(repo.head.target)
844
- walker.first
845
- rescue Rugged::ReferenceError
846
- # The repo does not have a head => no commits => no head commit.
847
- nil
848
- end
849
-
850
- def head_tree_files(repo)
851
- head_commit(repo).tree.map { |x| x[:name] }
852
- end
853
-
854
- # NOTE: This method is ignoring hidden files.
855
- def local_file_count
856
- files = Dir.chdir(local_repo_path) { Dir.glob('*') }
857
- files.count
858
- end
859
-
860
- def local_repo_remote_branch
861
- Rugged::Branch.lookup(local_repo, 'origin/master', :remote)
862
- end
863
-
864
- def local_repo_clean?
865
- local_repo.diff_workdir(local_repo.head.target, include_untracked: true).deltas.empty?
749
+ GitFactory.write(:local, filename, content)
866
750
  end
867
751
 
868
- def local_file_exist?(*path_elements)
869
- File.exist?(File.join(local_repo_path, *path_elements))
752
+ # @param (see GitFactory.commit)
753
+ # @return (see GitFactory.commit)
754
+ def commit(*args)
755
+ GitFactory.commit(:local, *args)
870
756
  end
871
757
 
872
- def local_file_content(*path_elements)
873
- return nil unless local_file_exist?
874
- File.read(File.join(local_repo_path, *path_elements))
758
+ # @param (see GitFactory.bare_commit)
759
+ # @return (see GitFactory.bare_commit)
760
+ def bare_commit(*args)
761
+ GitFactory.bare_commit(:remote, *args)
875
762
  end
876
763
  end