gitdocs 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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