grudge 0.1.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.
- data/HISTORY.txt +2 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +108 -0
- data/Rakefile +18 -0
- data/TODO.markdown +27 -0
- data/bin/grudge +55 -0
- data/config.ru +13 -0
- data/config/grudge.yml +31 -0
- data/grudge.gemspec +64 -0
- data/lib/boot.rb +8 -0
- data/lib/grudge.rb +73 -0
- data/lib/grudge/base.rb +16 -0
- data/lib/grudge/config.rb +23 -0
- data/lib/grudge/database.rb +15 -0
- data/lib/grudge/extensions.rb +18 -0
- data/lib/grudge/helpers.rb +28 -0
- data/lib/grudge/repository.rb +47 -0
- data/lib/models/commit.rb +55 -0
- data/lib/models/vote.rb +16 -0
- data/lib/views/application.haml +26 -0
- data/lib/views/application.sass +110 -0
- data/lib/views/index.haml +28 -0
- data/lib/views/not_found.haml +1 -0
- data/lib/views/show.haml +25 -0
- data/test/commit_test.rb +226 -0
- data/test/config_test.rb +18 -0
- data/test/grudge_test.rb +193 -0
- data/test/model_factory.rb +30 -0
- data/test/repository_test.rb +118 -0
- data/test/test_helper.rb +46 -0
- data/test/vote_test.rb +65 -0
- metadata +165 -0
data/test/config_test.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class ConfigTest < Test::Unit::TestCase
|
4
|
+
context "config file" do
|
5
|
+
setup do
|
6
|
+
config_file = StringIO.new({"foo" => {"bar" => "baz"}}.to_yaml)
|
7
|
+
@config = Grudge::Config::ConfigFile.new(config_file, :foo)
|
8
|
+
end
|
9
|
+
|
10
|
+
should "scope to specified environment" do
|
11
|
+
assert_equal "baz", @config.bar
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
should "return hash as value of repository" do
|
16
|
+
assert_kind_of Hash, Grudge::Config.repo
|
17
|
+
end
|
18
|
+
end
|
data/test/grudge_test.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class GrudgeTest < Test::Unit::TestCase
|
4
|
+
context "grudge" do
|
5
|
+
setup do
|
6
|
+
Grudge::Repository.stubs(:object).returns(repository_commit)
|
7
|
+
end
|
8
|
+
|
9
|
+
context "/" do
|
10
|
+
should "redirect to /recent" do
|
11
|
+
assert_redirected_to('/recent') { get_it '/' }
|
12
|
+
end
|
13
|
+
end # /
|
14
|
+
|
15
|
+
context "list view" do
|
16
|
+
setup do
|
17
|
+
@commits = (1..20).map {Factory.build(:commit)}
|
18
|
+
end
|
19
|
+
|
20
|
+
context "/recent" do
|
21
|
+
setup do
|
22
|
+
Commit.expects(:recent).returns(@commits)
|
23
|
+
get_it '/recent'
|
24
|
+
@h = Hpricot(@response.body)
|
25
|
+
end
|
26
|
+
|
27
|
+
should "render 20 most recent commits" do
|
28
|
+
assert_equal 20, (@h/'.commit').length
|
29
|
+
end
|
30
|
+
|
31
|
+
should "set a title" do
|
32
|
+
assert_match %r[Recent commits$], (@h/'title').text
|
33
|
+
end
|
34
|
+
end # /recent
|
35
|
+
|
36
|
+
context "/popular" do
|
37
|
+
setup do
|
38
|
+
Commit.expects(:popular).returns(@commits)
|
39
|
+
get_it '/popular'
|
40
|
+
@h = Hpricot(@response.body)
|
41
|
+
end
|
42
|
+
|
43
|
+
should "render 20 most recent commits" do
|
44
|
+
assert_equal 20, (@h/'.commit').length
|
45
|
+
end
|
46
|
+
|
47
|
+
should "set a title" do
|
48
|
+
assert_match %r[Popular commits$], (@h/'title').text
|
49
|
+
end
|
50
|
+
end # /popular
|
51
|
+
|
52
|
+
context "/unpopular" do
|
53
|
+
setup do
|
54
|
+
Commit.expects(:unpopular).returns(@commits)
|
55
|
+
get_it '/unpopular'
|
56
|
+
@h = Hpricot(@response.body)
|
57
|
+
end
|
58
|
+
|
59
|
+
should "render 20 most recent commits" do
|
60
|
+
assert_equal 20, (@h/'.commit').length
|
61
|
+
end
|
62
|
+
|
63
|
+
should "set a title" do
|
64
|
+
assert_match %r[Unpopular commits$], (@h/'title').text
|
65
|
+
end
|
66
|
+
end # /unpopular
|
67
|
+
end # list view
|
68
|
+
|
69
|
+
context "/:sha" do
|
70
|
+
context "found an entry" do
|
71
|
+
setup do
|
72
|
+
@commit = Factory.build(:commit, :sha => 'abc')
|
73
|
+
Commit.expects(:first).with(:sha => 'abc').returns(@commit)
|
74
|
+
get_it "/#{@commit.sha}"
|
75
|
+
@h = Hpricot(@response.body)
|
76
|
+
end
|
77
|
+
|
78
|
+
should "render one commit entry" do
|
79
|
+
assert_equal 1, (@h/'.commit').length
|
80
|
+
end
|
81
|
+
|
82
|
+
should "set title to sha" do
|
83
|
+
assert_match %r[abc$], (@h/'title').text
|
84
|
+
end
|
85
|
+
end # found an entry
|
86
|
+
|
87
|
+
context "no entry found" do
|
88
|
+
setup do
|
89
|
+
Commit.expects(:first).with(:sha => 'abcdef').returns(nil)
|
90
|
+
get_it "/abcdef"
|
91
|
+
@h = Hpricot(@response.body)
|
92
|
+
end
|
93
|
+
|
94
|
+
should "render not found page" do
|
95
|
+
assert_match %r[I don't even know you anymore], (@h/'body').text
|
96
|
+
end
|
97
|
+
end # no entry found
|
98
|
+
end # /:sha
|
99
|
+
|
100
|
+
context "voting" do
|
101
|
+
context "record found" do
|
102
|
+
setup do
|
103
|
+
@commit = Factory.build(:commit, :sha => 'abc')
|
104
|
+
Commit.expects(:first).with(:sha => 'abc').returns(@commit)
|
105
|
+
end
|
106
|
+
|
107
|
+
context "for /:sha/awesome" do
|
108
|
+
setup do
|
109
|
+
@commit.expects(:awesome!)
|
110
|
+
get_it "/abc/awesome"
|
111
|
+
end
|
112
|
+
should "redirect to show page" do
|
113
|
+
assert_redirected_to '/abc'
|
114
|
+
end
|
115
|
+
end # for /:sha/awesome
|
116
|
+
|
117
|
+
context "for /:sha/suck" do
|
118
|
+
setup do
|
119
|
+
@commit.expects(:suck!)
|
120
|
+
get_it "/abc/suck"
|
121
|
+
end
|
122
|
+
should "redirect to show page" do
|
123
|
+
assert_redirected_to '/abc'
|
124
|
+
end
|
125
|
+
end # for /:sha/suck
|
126
|
+
end # record found
|
127
|
+
|
128
|
+
context "record not found" do
|
129
|
+
setup do
|
130
|
+
Commit.expects(:first).returns(nil)
|
131
|
+
end
|
132
|
+
|
133
|
+
context "for /:sha/awesome" do
|
134
|
+
setup do
|
135
|
+
get_it "/abc/awesome"
|
136
|
+
@h = Hpricot(@response.body)
|
137
|
+
end
|
138
|
+
should "render not found page" do
|
139
|
+
assert_match %r[I don't even know you anymore], (@h/'body').text
|
140
|
+
end
|
141
|
+
end # for /:sha/awesome
|
142
|
+
|
143
|
+
context "for /:sha/suck" do
|
144
|
+
setup do
|
145
|
+
get_it "/abc/suck"
|
146
|
+
@h = Hpricot(@response.body)
|
147
|
+
end
|
148
|
+
should "render not found page" do
|
149
|
+
assert_match %r[I don't even know you anymore], (@h/'body').text
|
150
|
+
end
|
151
|
+
end # for /:sha/suck
|
152
|
+
end # record not found
|
153
|
+
end # voting
|
154
|
+
|
155
|
+
context "/search" do
|
156
|
+
context "for existing commit" do
|
157
|
+
setup do
|
158
|
+
@commit = Factory.build(:commit, :sha => 'abc')
|
159
|
+
Commit.expects(:search).with('abc').returns(@commit)
|
160
|
+
post_it '/search', {'query' => 'abc'}
|
161
|
+
@h = Hpricot(@response.body)
|
162
|
+
end
|
163
|
+
|
164
|
+
should "render page with just one commit" do
|
165
|
+
assert_equal 1, (@h/'.commit').length
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "for nonexistant commit" do
|
170
|
+
setup do
|
171
|
+
Commit.expects(:search).with('abc').returns(nil)
|
172
|
+
post_it '/search', {'query' => 'abc'}
|
173
|
+
@h = Hpricot(@response.body)
|
174
|
+
end
|
175
|
+
should "render not found page" do
|
176
|
+
assert_match %r[I don't even know you anymore], (@h/'body').text
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end # /search
|
180
|
+
|
181
|
+
context "/repository/pull" do
|
182
|
+
setup do
|
183
|
+
@commits = (1..2).map {Factory(:commit)}
|
184
|
+
Commit.expects(:download!)
|
185
|
+
post_it '/repository/pull'
|
186
|
+
end
|
187
|
+
|
188
|
+
should "render blank page" do
|
189
|
+
assert_equal '', @response.body
|
190
|
+
end
|
191
|
+
end # /repository/pull
|
192
|
+
end # grudge
|
193
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'factory_girl'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
# Model stuff
|
5
|
+
|
6
|
+
Factory.sequence :sha do |n|
|
7
|
+
"a#{n}b#{n}c#{n}d#{n}e#{n}f#{n}"
|
8
|
+
end
|
9
|
+
|
10
|
+
Factory.define :commit do |commit|
|
11
|
+
commit.sha { Factory.next(:sha) }
|
12
|
+
commit.committed_at Time.now
|
13
|
+
commit.score 0
|
14
|
+
end
|
15
|
+
|
16
|
+
Factory.define :awesome, :class => Vote do |vote|
|
17
|
+
vote.score 1
|
18
|
+
end
|
19
|
+
|
20
|
+
Factory.define :suck, :class => Vote do |vote|
|
21
|
+
vote.score -1
|
22
|
+
end
|
23
|
+
|
24
|
+
# Repository stuff
|
25
|
+
|
26
|
+
def repository_commit(attributes={})
|
27
|
+
defaults = {:sha => Factory.next(:sha), :author => O(:name => "Barney"),
|
28
|
+
:committer_date => Time.now, :message => "This is the shiznit!"}
|
29
|
+
O(defaults.merge(attributes))
|
30
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class RepositoryTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "origin uri" do
|
6
|
+
should "return URI of repository/origin from config" do
|
7
|
+
assert_equal URI.parse('test/origin.git'), Grudge::Repository.origin_uri
|
8
|
+
end
|
9
|
+
end # origin uri
|
10
|
+
|
11
|
+
context "master path" do
|
12
|
+
should "return path from repository/master in config" do
|
13
|
+
assert_equal 'test/clone.git', Grudge::Repository.master_path
|
14
|
+
end
|
15
|
+
end # master path
|
16
|
+
|
17
|
+
context "open" do
|
18
|
+
context "logging" do
|
19
|
+
should "write to configured log" do
|
20
|
+
Git.expects(:open).with(any_parameters)
|
21
|
+
Grudge::Config.expects(:log)
|
22
|
+
Grudge::Repository.open
|
23
|
+
end
|
24
|
+
end # logging
|
25
|
+
|
26
|
+
context "without valid working directory" do
|
27
|
+
should "raise an error" do
|
28
|
+
assert_raise(ArgumentError) { Grudge::Repository.open }
|
29
|
+
end
|
30
|
+
end # without valid working directory
|
31
|
+
end # open
|
32
|
+
|
33
|
+
context "open or clone" do
|
34
|
+
should "clone if no master path exists" do
|
35
|
+
Git.expects(:clone).with(Grudge::Repository.origin_uri,
|
36
|
+
Grudge::Repository.master_path, any_parameters)
|
37
|
+
Grudge::Repository.open_or_clone
|
38
|
+
end
|
39
|
+
|
40
|
+
should "open if master_path exists" do
|
41
|
+
File.expects(:exists?).with(Grudge::Repository.master_path).returns(true)
|
42
|
+
Grudge::Repository.expects(:open)
|
43
|
+
Grudge::Repository.open_or_clone
|
44
|
+
end
|
45
|
+
end # open or clone
|
46
|
+
|
47
|
+
context "pull" do
|
48
|
+
setup do
|
49
|
+
git = mock('git repo') do
|
50
|
+
expects(:pull).with('origin', 'origin/master', 'grudge origin pull')
|
51
|
+
end
|
52
|
+
Grudge::Repository.stubs(:open).returns(git)
|
53
|
+
end
|
54
|
+
|
55
|
+
should "fetch from origin and merge from origin/master" do
|
56
|
+
Grudge::Repository.pull
|
57
|
+
end
|
58
|
+
end # pull
|
59
|
+
|
60
|
+
context "object" do
|
61
|
+
setup do
|
62
|
+
git = mock('git repo') { expects(:object).with('abc') }
|
63
|
+
Grudge::Repository.stubs(:open).returns(git)
|
64
|
+
end
|
65
|
+
|
66
|
+
should "return object for given sha" do
|
67
|
+
Grudge::Repository.object('abc')
|
68
|
+
end
|
69
|
+
end # object
|
70
|
+
|
71
|
+
context "latest commits" do
|
72
|
+
setup do
|
73
|
+
@repo_commits = (1..10).map { |i| Factory.next(:sha) }
|
74
|
+
Grudge::Repository.expects(:pull)
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when since is provided" do
|
78
|
+
should "also provide a between option" do
|
79
|
+
since = 'abcdef'
|
80
|
+
git = O(:lib => stub('lib'))
|
81
|
+
git.lib.expects(:log_commits).with(:between => [since, '']).returns([])
|
82
|
+
Grudge::Repository.expects(:open).returns(git)
|
83
|
+
Grudge::Repository.latest_commits_since(since)
|
84
|
+
end
|
85
|
+
end # when since provided
|
86
|
+
|
87
|
+
context "when since is not provided" do
|
88
|
+
should "not provide any options" do
|
89
|
+
git = O(:lib => stub('lib'))
|
90
|
+
git.lib.expects(:log_commits).with({}).returns([])
|
91
|
+
Grudge::Repository.expects(:open).returns(git)
|
92
|
+
Grudge::Repository.latest_commits_since(nil)
|
93
|
+
end
|
94
|
+
end # when since is not provided
|
95
|
+
|
96
|
+
context "yield" do
|
97
|
+
setup do
|
98
|
+
git = O(:lib => O(:log_commits => @repo_commits))
|
99
|
+
Grudge::Repository.expects(:open).returns(git)
|
100
|
+
Grudge::Repository.expects(:object).times(10).with(kind_of(String)).
|
101
|
+
returns(repository_commit(:sha => 'abc'))
|
102
|
+
end
|
103
|
+
|
104
|
+
should "for each commit" do
|
105
|
+
count = 0
|
106
|
+
Grudge::Repository.latest_commits_since { |o| count += 1 }
|
107
|
+
assert_equal 10, count
|
108
|
+
end
|
109
|
+
|
110
|
+
should "an object loaded from :sha" do
|
111
|
+
Grudge::Repository.latest_commits_since do |o|
|
112
|
+
assert_kind_of OpenStruct, o
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end # yield
|
116
|
+
end # latest commits since
|
117
|
+
|
118
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
require 'sinatra'
|
5
|
+
require 'sinatra/test/unit'
|
6
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'grudge')
|
7
|
+
|
8
|
+
require 'mocha'
|
9
|
+
require 'shoulda'
|
10
|
+
require 'ostruct'
|
11
|
+
require File.join(File.dirname(__FILE__), 'model_factory')
|
12
|
+
|
13
|
+
# TODO: Move to custom_assertions or make a new library for others to use.
|
14
|
+
# Gus would want to call it Chicago (as in "Chicago. It's my kind of town.")
|
15
|
+
|
16
|
+
class Test::Unit::TestCase
|
17
|
+
def teardown
|
18
|
+
Commit.all.destroy!
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generic test helpers
|
22
|
+
|
23
|
+
def deny(check, message=nil) assert(!check, message); end
|
24
|
+
|
25
|
+
def O(*args) OpenStruct.new(*args); end
|
26
|
+
|
27
|
+
# Controller test helpers
|
28
|
+
|
29
|
+
def assert_redirected_to(expected_path, &block)
|
30
|
+
yield if block_given?
|
31
|
+
assert_equal 302, @response.status
|
32
|
+
action = expected_path.kind_of?(Regexp) ? 'match' : 'equal'
|
33
|
+
send("assert_#{action}", expected_path, @response.headers["Location"])
|
34
|
+
end
|
35
|
+
|
36
|
+
def assert_ok(&block)
|
37
|
+
yield
|
38
|
+
assert_equal 200, @response.status
|
39
|
+
end
|
40
|
+
|
41
|
+
# Model test helpers
|
42
|
+
# Add some of these to a Shoulda::DataMapper::Macros thing
|
43
|
+
# def self.should_require_attributes(attributes=[])
|
44
|
+
# def self.should_expect_a_default_value :attribute, expected_value
|
45
|
+
# def self.should_be_unique :attribute
|
46
|
+
end
|
data/test/vote_test.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class VoteTest < Test::Unit::TestCase
|
4
|
+
context "required attributes" do
|
5
|
+
setup do
|
6
|
+
@vote = Vote.new
|
7
|
+
@vote.valid?
|
8
|
+
@errors = @vote.errors
|
9
|
+
end
|
10
|
+
|
11
|
+
should "include score" do
|
12
|
+
assert_contains @errors[:score], "Score must not be blank"
|
13
|
+
end
|
14
|
+
|
15
|
+
should "include commit id" do
|
16
|
+
assert_contains @errors[:commit_id], "Commit must not be blank"
|
17
|
+
end
|
18
|
+
end # required attributes
|
19
|
+
|
20
|
+
context "associations" do
|
21
|
+
should "belong to commit" do
|
22
|
+
assert_equal Hash.new, Vote.relationships[:commit].options
|
23
|
+
assert_equal Commit, Vote.relationships[:commit].parent_model
|
24
|
+
end
|
25
|
+
end # associations
|
26
|
+
|
27
|
+
context "building awesome votes" do
|
28
|
+
setup { @vote = Vote.awesome }
|
29
|
+
should "create new vote with positive score" do
|
30
|
+
assert_equal 1, @vote.score
|
31
|
+
end
|
32
|
+
|
33
|
+
should "create unsaved vote" do
|
34
|
+
assert @vote.new_record?
|
35
|
+
end
|
36
|
+
|
37
|
+
should "be awesome?" do
|
38
|
+
assert @vote.awesome?
|
39
|
+
end
|
40
|
+
|
41
|
+
should "not suck?" do
|
42
|
+
deny @vote.suck?
|
43
|
+
end
|
44
|
+
end # building awesome votes
|
45
|
+
|
46
|
+
context "building sucky votes" do
|
47
|
+
setup { @vote = Vote.suck }
|
48
|
+
should "create new vote with negative score" do
|
49
|
+
assert_equal -1, @vote.score
|
50
|
+
end
|
51
|
+
|
52
|
+
should "create unsaved vote" do
|
53
|
+
assert @vote.new_record?
|
54
|
+
end
|
55
|
+
|
56
|
+
should "not be awesome?" do
|
57
|
+
deny @vote.awesome?
|
58
|
+
end
|
59
|
+
|
60
|
+
should "suck?" do
|
61
|
+
assert @vote.suck?
|
62
|
+
end
|
63
|
+
end # building sucky votes
|
64
|
+
|
65
|
+
end
|