integrity 0.1.8 → 0.1.9.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 (77) hide show
  1. data/README.markdown +7 -0
  2. data/Rakefile +77 -124
  3. data/config/config.ru +29 -0
  4. data/config/config.sample.ru +6 -16
  5. data/config/config.sample.yml +15 -12
  6. data/config/config.yml +34 -0
  7. data/lib/integrity.rb +13 -13
  8. data/lib/integrity/app.rb +138 -0
  9. data/lib/integrity/author.rb +39 -0
  10. data/lib/integrity/build.rb +54 -31
  11. data/lib/integrity/commit.rb +71 -0
  12. data/lib/integrity/helpers.rb +3 -3
  13. data/lib/integrity/helpers/authorization.rb +2 -2
  14. data/lib/integrity/helpers/forms.rb +3 -3
  15. data/lib/integrity/helpers/pretty_output.rb +1 -1
  16. data/lib/integrity/helpers/rendering.rb +6 -1
  17. data/lib/integrity/helpers/resources.rb +9 -3
  18. data/lib/integrity/helpers/urls.rb +15 -13
  19. data/lib/integrity/installer.rb +43 -60
  20. data/lib/integrity/migrations.rb +31 -48
  21. data/lib/integrity/notifier.rb +14 -16
  22. data/lib/integrity/notifier/base.rb +29 -19
  23. data/lib/integrity/notifier/test_helpers.rb +100 -0
  24. data/lib/integrity/project.rb +69 -33
  25. data/lib/integrity/project_builder.rb +23 -14
  26. data/lib/integrity/scm/git.rb +15 -14
  27. data/lib/integrity/scm/git/uri.rb +9 -9
  28. data/test/acceptance/api_test.rb +97 -0
  29. data/test/acceptance/browse_project_builds_test.rb +65 -0
  30. data/test/acceptance/browse_project_test.rb +95 -0
  31. data/test/acceptance/build_notifications_test.rb +42 -0
  32. data/test/acceptance/create_project_test.rb +97 -0
  33. data/test/acceptance/delete_project_test.rb +53 -0
  34. data/test/acceptance/edit_project_test.rb +117 -0
  35. data/test/acceptance/error_page_test.rb +18 -0
  36. data/test/acceptance/helpers.rb +2 -0
  37. data/test/acceptance/installer_test.rb +62 -0
  38. data/test/acceptance/manual_build_project_test.rb +82 -0
  39. data/test/acceptance/notifier_test.rb +109 -0
  40. data/test/acceptance/project_syndication_test.rb +30 -0
  41. data/test/acceptance/stylesheet_test.rb +18 -0
  42. data/test/helpers.rb +59 -26
  43. data/test/helpers/acceptance.rb +19 -65
  44. data/test/helpers/acceptance/email_notifier.rb +55 -0
  45. data/test/helpers/acceptance/git_helper.rb +15 -15
  46. data/test/helpers/acceptance/textfile_notifier.rb +3 -3
  47. data/test/helpers/expectations.rb +0 -1
  48. data/test/helpers/expectations/be_a.rb +4 -4
  49. data/test/helpers/expectations/change.rb +5 -5
  50. data/test/helpers/expectations/have.rb +4 -4
  51. data/test/helpers/expectations/predicates.rb +4 -4
  52. data/test/helpers/fixtures.rb +44 -18
  53. data/test/helpers/initial_migration_fixture.sql +44 -0
  54. data/test/unit/build_test.rb +51 -0
  55. data/test/unit/commit_test.rb +83 -0
  56. data/test/unit/helpers_test.rb +56 -0
  57. data/test/unit/integrity_test.rb +18 -0
  58. data/test/unit/migrations_test.rb +56 -0
  59. data/test/unit/notifier_test.rb +123 -0
  60. data/test/unit/project_builder_test.rb +108 -0
  61. data/test/unit/project_test.rb +282 -0
  62. data/test/unit/scm_test.rb +54 -0
  63. data/views/_commit_info.haml +24 -0
  64. data/views/build.haml +2 -2
  65. data/views/error.haml +4 -3
  66. data/views/home.haml +3 -5
  67. data/views/integrity.sass +19 -6
  68. data/views/new.haml +6 -6
  69. data/views/project.builder +9 -9
  70. data/views/project.haml +14 -12
  71. metadata +89 -122
  72. data/VERSION.yml +0 -4
  73. data/app.rb +0 -138
  74. data/integrity.gemspec +0 -76
  75. data/lib/integrity/core_ext/string.rb +0 -5
  76. data/test/helpers/expectations/have_tag.rb +0 -128
  77. data/views/_build_info.haml +0 -18
@@ -0,0 +1,83 @@
1
+ require File.dirname(__FILE__) + '/../helpers'
2
+
3
+ class CommitTest < Test::Unit::TestCase
4
+ specify "fixture is valid and can be saved" do
5
+ lambda do
6
+ commit = Commit.gen
7
+ commit.save
8
+
9
+ commit.should be_valid
10
+ end.should change(Commit, :count).by(1)
11
+ end
12
+
13
+ describe "Properties" do
14
+ before(:each) do
15
+ @commit = Commit.generate(:identifier => "658ba96cb0235e82ee720510c049883955200fa9",
16
+ :author => "Nicolás Sanguinetti <contacto@nicolassanguinetti.info>")
17
+ end
18
+
19
+ it "has a commit identifier" do
20
+ @commit.identifier.should be("658ba96cb0235e82ee720510c049883955200fa9")
21
+ end
22
+
23
+ it "has a short commit identifier" do
24
+ @commit.short_identifier.should == "658ba96"
25
+
26
+ @commit.identifier = "402"
27
+ @commit.short_identifier.should == "402"
28
+ end
29
+
30
+ it "has a commit author" do
31
+ commit = Commit.gen(:author => "Nicolás Sanguinetti <contacto@nicolassanguinetti.info>")
32
+ commit.author.name.should == "Nicolás Sanguinetti"
33
+ commit.author.email.should == "contacto@nicolassanguinetti.info"
34
+ commit.author.full.should == "Nicolás Sanguinetti <contacto@nicolassanguinetti.info>"
35
+ end
36
+
37
+ it "raises ArgumentError with invalid author" do
38
+ lambda { Commit.gen(:author => "foo") }.should raise_error(ArgumentError)
39
+ end
40
+
41
+ it "has a commit message" do
42
+ commit = Commit.gen(:message => "This commit rocks")
43
+ commit.message.should == "This commit rocks"
44
+ end
45
+
46
+ it "has a commit date" do
47
+ commit = Commit.gen(:committed_at => Time.utc(2008, 10, 12, 14, 18, 20))
48
+ commit.committed_at.to_s.should == "2008-10-12T14:18:20+00:00"
49
+ end
50
+
51
+ it "has a human readable status" do
52
+ commit = Commit.gen(:successful, :identifier => "658ba96cb0235e82ee720510c049883955200fa9")
53
+ commit.human_readable_status.should be("Built 658ba96 successfully")
54
+
55
+ commit = Commit.gen(:failed, :identifier => "658ba96cb0235e82ee720510c049883955200fa9")
56
+ commit.human_readable_status.should be("Built 658ba96 and failed")
57
+
58
+ commit = Commit.gen(:pending, :identifier => "658ba96cb0235e82ee720510c049883955200fa9")
59
+ commit.human_readable_status.should be("658ba96 hasn't been built yet")
60
+ end
61
+ end
62
+
63
+ describe "Queueing a build" do
64
+ before(:each) do
65
+ @commit = Commit.gen
66
+ stub.instance_of(ProjectBuilder).build(@commit)
67
+ end
68
+
69
+ it "creates an empty Build" do
70
+ @commit.build.should be_nil
71
+ @commit.queue_build
72
+ @commit.build.should_not be_nil
73
+ end
74
+
75
+ it "ensures the build is saved" do
76
+ @commit.build.should be_nil
77
+ @commit.queue_build
78
+
79
+ commit = Commit.first(:identifier => @commit.identifier)
80
+ commit.build.should_not be_nil
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,56 @@
1
+ require File.dirname(__FILE__) + "/../helpers"
2
+
3
+ class BrowsePublicProjectsTest < Test::Unit::TestCase
4
+ include ::Integrity::Helpers
5
+
6
+ test "#pretty_date" do
7
+ pretty_date(Time.now).should == "today"
8
+ pretty_date(Time.new - 86400).should == "yesterday"
9
+
10
+ pretty_date(Time.mktime(1995, 12, 01)).should == "on Dec 01st"
11
+ pretty_date(Time.mktime(1995, 12, 21)).should == "on Dec 21st"
12
+ pretty_date(Time.mktime(1995, 12, 31)).should == "on Dec 31st"
13
+
14
+ pretty_date(Time.mktime(1995, 12, 22)).should == "on Dec 22nd"
15
+ pretty_date(Time.mktime(1995, 12, 22)).should == "on Dec 22nd"
16
+
17
+ pretty_date(Time.mktime(1995, 12, 03)).should == "on Dec 03rd"
18
+ pretty_date(Time.mktime(1995, 12, 23)).should == "on Dec 23rd"
19
+
20
+ pretty_date(Time.mktime(1995, 12, 15)).should == "on Dec 15th"
21
+ pretty_date(Time.mktime(1995, 12, 15)).should == "on Dec 15th"
22
+ pretty_date(Time.mktime(1995, 12, 15)).should == "on Dec 15th"
23
+ end
24
+
25
+ describe "#push_url_for" do
26
+ before(:each) do
27
+ @project = Project.gen(:integrity)
28
+ Integrity.config[:admin_username] = "admin"
29
+ Integrity.config[:admin_password] = "test"
30
+
31
+ stub(self).request {
32
+ OpenStruct.new(:url => "http://integrity.example.org:1234")
33
+ }
34
+ end
35
+
36
+ test "with auth disabled" do
37
+ Integrity.config[:use_basic_auth] = false
38
+
39
+ push_url_for(@project).should == "http://integrity.example.org:1234/integrity/push"
40
+ end
41
+
42
+ test "with auth and hashing enabled" do
43
+ Integrity.config[:use_basic_auth] = true
44
+ Integrity.config[:hash_admin_password] = true
45
+
46
+ push_url_for(@project).should == "http://admin:<password>@integrity.example.org:1234/integrity/push"
47
+ end
48
+
49
+ test "with auth enabled and hashing disabled" do
50
+ Integrity.config[:use_basic_auth] = true
51
+ Integrity.config[:hash_admin_password] = false
52
+
53
+ push_url_for(@project).should == "http://admin:test@integrity.example.org:1234/integrity/push"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + "/../helpers"
2
+
3
+ class IntegrityTest < Test::Unit::TestCase
4
+ test "Integrity.new loads configuration from a file" do
5
+ stub(DataMapper).setup { nil }
6
+
7
+ file = File.dirname(__FILE__) + "/../../config/config.sample.yml"
8
+ Integrity.new(file)
9
+
10
+ Integrity.config[:base_uri].should == "http://integrity.domain.tld"
11
+ Integrity.config[:export_directory].should == "/path/to/scm/exports"
12
+ end
13
+
14
+ specify "config is just a hash" do
15
+ Integrity.config[:foo] = "bar"
16
+ Integrity.config[:foo].should == "bar"
17
+ end
18
+ end
@@ -0,0 +1,56 @@
1
+ require File.dirname(__FILE__) + "/../helpers"
2
+
3
+ class MigrationsTest < Test::Unit::TestCase
4
+ def database_adapter
5
+ DataMapper.repository(:default).adapter
6
+ end
7
+
8
+ def table_exists?(table_name)
9
+ database_adapter.storage_exists?(table_name)
10
+ end
11
+
12
+ def current_migrations
13
+ database_adapter.query("SELECT * from migration_info")
14
+ end
15
+
16
+ def load_initial_migration_fixture
17
+ database_adapter.execute(File.read(File.dirname(__FILE__) +
18
+ "/../helpers/initial_migration_fixture.sql"))
19
+ end
20
+
21
+ before(:all) do
22
+ require "integrity/migrations"
23
+ end
24
+
25
+ before(:each) do
26
+ [Project, Build, Commit, Notifier].each(&:auto_migrate_down!)
27
+ assert !table_exists?("migration_info") # just to be sure
28
+ end
29
+
30
+ test "upgrading a pre migration database" do
31
+ util_capture { Integrity.migrate_db }
32
+
33
+ current_migrations.should == ["initial", "add_commits"]
34
+ assert table_exists?("integrity_projects")
35
+ assert table_exists?("integrity_builds")
36
+ assert table_exists?("integrity_notifiers")
37
+ assert table_exists?("integrity_commits")
38
+ end
39
+
40
+ test "migrating data from initial to add_commits migration" do
41
+ load_initial_migration_fixture
42
+
43
+ util_capture { Integrity.migrate_db }
44
+ current_migrations.should == ["initial", "add_commits"]
45
+
46
+ sinatra = Project.first(:name => "Sinatra")
47
+ sinatra.should have(1).commits
48
+ sinatra.commits.first.should be_successful
49
+ sinatra.commits.first.output.should =~ /sinatra/
50
+
51
+ shout_bot = Project.first(:name => "Shout Bot")
52
+ shout_bot.should have(1).commits
53
+ shout_bot.commits.first.should be_failed
54
+ shout_bot.commits.first.output.should =~ /shout-bot/
55
+ end
56
+ end
@@ -0,0 +1,123 @@
1
+ require File.dirname(__FILE__) + "/../helpers"
2
+
3
+ class NotifierTest < Test::Unit::TestCase
4
+ specify "IRC fixture is valid and can be saved" do
5
+ lambda do
6
+ Notifier.generate(:irc).tap do |project|
7
+ project.should be_valid
8
+ project.save
9
+ end
10
+ end.should change(Project, :count).by(1)
11
+ end
12
+
13
+ specify "Twitter fixture is valid and can be saved" do
14
+ lambda do
15
+ Notifier.generate(:twitter).tap do |project|
16
+ project.should be_valid
17
+ project.save
18
+ end
19
+ end.should change(Project, :count).by(1)
20
+ end
21
+
22
+ describe "Properties" do
23
+ before(:each) do
24
+ @notifier = Notifier.generate(:irc)
25
+ end
26
+
27
+ it "has a name" do
28
+ @notifier.name.should == "IRC"
29
+ end
30
+
31
+ it "has a config" do
32
+ @notifier.config.should == {:uri => "irc://irc.freenode.net/integrity"}
33
+ end
34
+ end
35
+
36
+ describe "Validation" do
37
+ it "requires a name" do
38
+ lambda do
39
+ Notifier.generate(:irc, :name => nil)
40
+ end.should_not change(Notifier, :count)
41
+ end
42
+
43
+ it "requires a config" do
44
+ lambda do
45
+ Notifier.generate(:irc, :config => nil)
46
+ end.should_not change(Notifier, :count)
47
+ end
48
+
49
+ it "requires a project" do
50
+ lambda do
51
+ Notifier.generate(:irc, :project => nil)
52
+ end.should_not change(Notifier, :count)
53
+ end
54
+
55
+ it "requires an unique name in project scope" do
56
+ project = Project.generate
57
+ irc = Notifier.gen(:irc, :project => project)
58
+
59
+ project.tap { |project| project.notifiers << irc }.save
60
+
61
+ lambda do
62
+ project.tap { |project| project.notifiers << irc }.save
63
+ end.should_not change(project.notifiers, :count).from(1).to(2)
64
+
65
+ lambda { Notifier.gen(:irc) }.should change(Notifier, :count).to(2)
66
+ end
67
+ end
68
+
69
+ it "knows which notifiers are available" do
70
+ Notifier.gen(:irc)
71
+ Notifier.gen(:twitter)
72
+ Notifier.should have(2).available
73
+ Notifier.available.should include(Integrity::Notifier::IRC)
74
+ Notifier.available.should include(Integrity::Notifier::Twitter)
75
+ end
76
+
77
+ it "knows how to notify the world of a build" do
78
+ irc = Notifier.generate(:irc)
79
+ build = Integrity::Build.generate
80
+ Notifier::IRC.expects(:notify_of_build).with(build, irc.config)
81
+ irc.notify_of_build(build)
82
+ end
83
+
84
+ describe "Enabling a list of notifiers for a project" do
85
+ it "creates new notifiers for the project" do
86
+ project = Project.generate
87
+ lambda do
88
+ project.enable_notifiers(["IRC", "Twitter"],
89
+ {"IRC" => {"uri" => "irc://irc.freenode.net/integrity"},
90
+ "Twitter" => {"username" => "john"}})
91
+ end.should change(project.notifiers, :count).from(0).to(2)
92
+ end
93
+
94
+ it "deletes all of previous notifiers" do
95
+ project = Project.generate(:notifiers => [Notifier.gen(:irc), Notifier.gen(:twitter)])
96
+ lambda do
97
+ project.enable_notifiers("IRC", {"IRC" => {:foo => "bar"}})
98
+ project.reload
99
+ end.should change(project.notifiers, :count).from(2).to(1)
100
+ end
101
+
102
+ it "does nothing if given nil as the list of notifiers to enable" do
103
+ lambda { Project.gen.enable_notifiers(nil, {}) }.should_not change(Notifier, :count)
104
+ end
105
+
106
+ it "doesn't destroy any of the other notifiers that exist for other projects" do
107
+ irc = Notifier.generate(:irc)
108
+
109
+ project = Project.gen
110
+ project.enable_notifiers("IRC", {"IRC" => irc.config})
111
+
112
+ lambda do
113
+ Project.gen.enable_notifiers("IRC", {"IRC" => irc.config})
114
+ end.should_not change(project.notifiers, :count)
115
+ end
116
+ end
117
+
118
+ it "requires notifier classes to implement Notifier.to_haml and Notifier#deliver!" do
119
+ class Blah < Notifier::Base; end
120
+ lambda { Blah.to_haml }.should raise_error(NoMethodError)
121
+ lambda { Blah.new(Build.gen, {}).deliver! }.should raise_error(NoMethodError)
122
+ end
123
+ end
@@ -0,0 +1,108 @@
1
+ require File.dirname(__FILE__) + "/../helpers"
2
+
3
+ class ProjectBuilderTest < Test::Unit::TestCase
4
+ before(:all) do
5
+ Integrity.config[:export_directory] = File.dirname(__FILE__)
6
+ @directory = Integrity.config[:export_directory] + "/foca-integrity-master"
7
+ FileUtils.mkdir(@directory)
8
+ end
9
+
10
+ after(:all) do
11
+ FileUtils.rm_rf(@directory)
12
+ end
13
+
14
+ before(:each) do
15
+ @project = Integrity::Project.generate(:integrity, :command => "echo 'output!'")
16
+ ignore_logs!
17
+ end
18
+
19
+ it "creates a new SCM with given project's uri, branch and export_directory" do
20
+ SCM::Git.expects(:new).with(@project.uri, @project.branch, @directory)
21
+ ProjectBuilder.new(@project)
22
+ end
23
+
24
+ describe "When building" do
25
+ before(:each) do
26
+ @commit = @project.commits.gen(:pending)
27
+ end
28
+
29
+ it "sets the started and completed timestamps" do
30
+ SCM::Git.any_instance.expects(:with_revision).with(@commit.identifier).yields
31
+ SCM::Git.any_instance.expects(:info).returns({})
32
+
33
+ build = ProjectBuilder.new(@project).build(@commit)
34
+ build.output.should == "output!\n"
35
+ build.started_at.should_not be_nil
36
+ build.completed_at.should_not be_nil
37
+ build.should be_successful
38
+ end
39
+
40
+ it "ensures completed_at is set, even if something horrible happens" do
41
+ lambda {
42
+ SCM::Git.any_instance.expects(:with_revision).with(@commit.identifier).raises
43
+ SCM::Git.any_instance.expects(:info).returns({})
44
+
45
+ build = ProjectBuilder.new(@project).build(@commit)
46
+ build.started_at.should_not be_nil
47
+ build.completed_at.should_not be_nil
48
+ build.should be_failed
49
+ }.should raise_error
50
+ end
51
+
52
+
53
+ it "sets the build status to failure when the build command exits with a non-zero status" do
54
+ @project.update_attributes(:command => "exit 1")
55
+ SCM::Git.any_instance.expects(:with_revision).with(@commit.identifier).yields
56
+ SCM::Git.any_instance.expects(:info).returns({})
57
+
58
+ build = ProjectBuilder.new(@project).build(@commit)
59
+ build.should be_failed
60
+ end
61
+
62
+ it "sets the build status to success when the build command exits with a zero status" do
63
+ @project.update_attributes(:command => "exit 0")
64
+ SCM::Git.any_instance.expects(:with_revision).with(@commit.identifier).yields
65
+ SCM::Git.any_instance.expects(:info).returns({})
66
+
67
+ build = ProjectBuilder.new(@project).build(@commit)
68
+ build.should be_successful
69
+ end
70
+
71
+ it "runs the command in the export directory" do
72
+ @project.update_attributes(:command => "cat foo.txt")
73
+ File.open(@directory + "/foo.txt", "w") { |file| file << "bar!" }
74
+ SCM::Git.any_instance.expects(:with_revision).with(@commit.identifier).yields
75
+ SCM::Git.any_instance.expects(:info).returns({})
76
+
77
+ build = ProjectBuilder.new(@project).build(@commit)
78
+ build.output.should == "bar!"
79
+ end
80
+
81
+ it "captures both stdout and stderr" do
82
+ @project.update_attributes(:command => "echo foo through out && echo bar through err 1>&2")
83
+ SCM::Git.any_instance.expects(:with_revision).with(@commit.identifier).yields
84
+ SCM::Git.any_instance.expects(:info).returns({})
85
+
86
+ build = ProjectBuilder.new(@project).build(@commit)
87
+ build.output.should == "foo through out\nbar through err\n"
88
+ end
89
+
90
+ it "raises SCMUnknownError if it can't figure the scm from the uri" do
91
+ @project.update_attributes(:uri => "scm://example.org")
92
+ lambda { ProjectBuilder.new(@project) }.should raise_error(SCM::SCMUnknownError)
93
+ end
94
+ end
95
+
96
+ describe "When deleting the code from disk" do
97
+ it "destroys the directory" do
98
+ lambda do
99
+ ProjectBuilder.new(@project).delete_code
100
+ end.should change(Pathname.new(@directory), :directory?).from(true).to(false)
101
+ end
102
+
103
+ it "don't complains if the directory doesn't exists" do
104
+ Pathname.new(@directory).should_not be_directory
105
+ lambda { ProjectBuilder.new(@project).delete_code }.should_not raise_error
106
+ end
107
+ end
108
+ end