integrity-integrity 0.1.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/.gitignore +12 -0
  2. data/CHANGES +48 -0
  3. data/README.md +82 -0
  4. data/Rakefile +58 -0
  5. data/bin/integrity +4 -0
  6. data/config/config.sample.ru +21 -0
  7. data/config/config.sample.yml +45 -0
  8. data/config/heroku/.gems +1 -0
  9. data/config/heroku/Rakefile +6 -0
  10. data/config/heroku/config.ru +7 -0
  11. data/config/heroku/integrity-config.rb +15 -0
  12. data/config/thin.sample.yml +13 -0
  13. data/integrity.gemspec +136 -0
  14. data/lib/integrity.rb +77 -0
  15. data/lib/integrity/app.rb +138 -0
  16. data/lib/integrity/author.rb +39 -0
  17. data/lib/integrity/build.rb +52 -0
  18. data/lib/integrity/commit.rb +60 -0
  19. data/lib/integrity/core_ext/object.rb +6 -0
  20. data/lib/integrity/helpers.rb +15 -0
  21. data/lib/integrity/helpers/authorization.rb +33 -0
  22. data/lib/integrity/helpers/breadcrumbs.rb +20 -0
  23. data/lib/integrity/helpers/pretty_output.rb +45 -0
  24. data/lib/integrity/helpers/rendering.rb +49 -0
  25. data/lib/integrity/helpers/resources.rb +19 -0
  26. data/lib/integrity/helpers/urls.rb +59 -0
  27. data/lib/integrity/installer.rb +145 -0
  28. data/lib/integrity/migrations.rb +151 -0
  29. data/lib/integrity/notifier.rb +44 -0
  30. data/lib/integrity/notifier/base.rb +74 -0
  31. data/lib/integrity/notifier/test.rb +52 -0
  32. data/lib/integrity/notifier/test/fixtures.rb +108 -0
  33. data/lib/integrity/notifier/test/hpricot_matcher.rb +38 -0
  34. data/lib/integrity/project.rb +93 -0
  35. data/lib/integrity/project/notifiers.rb +31 -0
  36. data/lib/integrity/project/push.rb +43 -0
  37. data/lib/integrity/project_builder.rb +56 -0
  38. data/lib/integrity/scm.rb +19 -0
  39. data/lib/integrity/scm/git.rb +84 -0
  40. data/lib/integrity/scm/git/uri.rb +57 -0
  41. data/public/buttons.css +82 -0
  42. data/public/reset.css +7 -0
  43. data/public/spinner.gif +0 -0
  44. data/test/acceptance/api_test.rb +97 -0
  45. data/test/acceptance/browse_project_builds_test.rb +65 -0
  46. data/test/acceptance/browse_project_test.rb +99 -0
  47. data/test/acceptance/build_notifications_test.rb +114 -0
  48. data/test/acceptance/create_project_test.rb +97 -0
  49. data/test/acceptance/delete_project_test.rb +53 -0
  50. data/test/acceptance/edit_project_test.rb +117 -0
  51. data/test/acceptance/error_page_test.rb +21 -0
  52. data/test/acceptance/installer_test.rb +81 -0
  53. data/test/acceptance/manual_build_project_test.rb +82 -0
  54. data/test/acceptance/not_found_page_test.rb +29 -0
  55. data/test/acceptance/project_syndication_test.rb +30 -0
  56. data/test/acceptance/stylesheet_test.rb +26 -0
  57. data/test/acceptance/unauthorized_page_test.rb +20 -0
  58. data/test/helpers.rb +75 -0
  59. data/test/helpers/acceptance.rb +82 -0
  60. data/test/helpers/acceptance/email_notifier.rb +52 -0
  61. data/test/helpers/acceptance/git_helper.rb +99 -0
  62. data/test/helpers/acceptance/notifier_helper.rb +47 -0
  63. data/test/helpers/acceptance/textfile_notifier.rb +26 -0
  64. data/test/helpers/expectations.rb +4 -0
  65. data/test/helpers/expectations/be_a.rb +23 -0
  66. data/test/helpers/expectations/change.rb +90 -0
  67. data/test/helpers/expectations/have.rb +105 -0
  68. data/test/helpers/expectations/predicates.rb +37 -0
  69. data/test/helpers/initial_migration_fixture.sql +44 -0
  70. data/test/unit/build_test.rb +72 -0
  71. data/test/unit/commit_test.rb +66 -0
  72. data/test/unit/helpers_test.rb +103 -0
  73. data/test/unit/integrity_test.rb +35 -0
  74. data/test/unit/migrations_test.rb +57 -0
  75. data/test/unit/notifier/base_test.rb +43 -0
  76. data/test/unit/notifier_test.rb +96 -0
  77. data/test/unit/project_builder_test.rb +118 -0
  78. data/test/unit/project_test.rb +344 -0
  79. data/test/unit/scm_test.rb +54 -0
  80. data/views/_commit_info.haml +24 -0
  81. data/views/build.haml +2 -0
  82. data/views/error.haml +37 -0
  83. data/views/home.haml +21 -0
  84. data/views/integrity.sass +400 -0
  85. data/views/layout.haml +29 -0
  86. data/views/new.haml +50 -0
  87. data/views/not_found.haml +31 -0
  88. data/views/notifier.haml +7 -0
  89. data/views/project.builder +21 -0
  90. data/views/project.haml +30 -0
  91. data/views/unauthorized.haml +38 -0
  92. metadata +323 -0
@@ -0,0 +1,52 @@
1
+ module Integrity
2
+ class Notifier
3
+ class Email < Notifier::Base
4
+ attr_reader :to, :from
5
+
6
+ def self.to_haml
7
+ <<-EOF
8
+ %p.normal
9
+ %label{ :for => "email_notifier_to" } Send to
10
+ %input.text#email_notifier_to{ :name => "notifiers[Email][to]", :type => "text", :value => config["to"] }
11
+
12
+ %p.normal
13
+ %label{ :for => "email_notifier_from" } Send from
14
+ %input.text#email_notifier_from{ :name => "notifiers[Email][from]", :type => "text", :value => config["from"] }
15
+
16
+ %h3 SMTP Server Configuration
17
+
18
+ %p.normal
19
+ %label{ :for => "email_notifier_host" } Host : Port
20
+ = succeed " : " do
21
+ %input.text#email_notifier_host{ :name => "notifiers[Email][host]", :value => config["host"], :style => "width: 24.5em;", :type => "text" }
22
+ %input.text#email_notifier_port{ :name => "notifiers[Email][port]", :value => config["port"], :style => "width: 3.5em;", :type => "text" }
23
+
24
+ %p.normal
25
+ %label{ :for => "email_notifier_user" } User
26
+ %input.text#email_notifier_user{ :name => "notifiers[Email][user]", :value => config["user"], :type => "text" }
27
+
28
+ %p.normal
29
+ %label{ :for => "email_notifier_pass" } Password
30
+ %input.text#email_notifier_pass{ :name => "notifiers[Email][pass]", :value => config["pass"], :type => "text" }
31
+
32
+ %p.normal
33
+ %label{ :for => "email_notifier_auth" } Auth type
34
+ %input.text#email_notifier_auth{ :name => "notifiers[Email][auth]", :value => (config["auth"] || "plain"), :type => "text" }
35
+
36
+ %p.normal
37
+ %label{ :for => "email_notifier_domain" } Domain
38
+ %input.text#email_notifier_domain{ :name => "notifiers[Email][domain]", :value => config["domain"], :type => "text" }
39
+ EOF
40
+ end
41
+
42
+ def initialize(build, config={})
43
+ @to = config.delete("to")
44
+ @from = config.delete("from")
45
+ super
46
+ end
47
+
48
+ def deliver!
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,99 @@
1
+ module GitHelper
2
+ @@_git_repositories = Hash.new {|h,k| h[k] = Repo.new(k) }
3
+
4
+ def git_repo(name)
5
+ @@_git_repositories[name]
6
+ end
7
+
8
+ def destroy_all_git_repos
9
+ @@_git_repositories.each {|n,r| r.destroy }
10
+ @@_git_repositories.clear
11
+ end
12
+
13
+ class Repo
14
+ attr_reader :path
15
+
16
+ def initialize(name)
17
+ @name = name
18
+ @path = "/tmp" / @name.to_s
19
+ create
20
+ end
21
+
22
+ def path
23
+ @path / ".git"
24
+ end
25
+
26
+ def create
27
+ destroy
28
+ FileUtils.mkdir_p @path
29
+
30
+ Dir.chdir(@path) do
31
+ system 'git init &>/dev/null'
32
+ system 'git config user.name "John Doe"'
33
+ system 'git config user.email "johndoe@example.org"'
34
+ system 'echo "just a test repo" >> README'
35
+ system 'git add README &>/dev/null'
36
+ system 'git commit -m "First commit" &>/dev/null'
37
+ end
38
+
39
+ add_successful_commit
40
+ end
41
+
42
+ def commits
43
+ Dir.chdir(@path) do
44
+ commits = `git log --pretty=oneline`.collect { |line| line.split(" ").first }
45
+ commits.inject([]) do |commits, sha1|
46
+ format = %Q(---%n:message: >-%n %s%n:timestamp: %ci%n:id: %H%n:author: %n :name: %an%n :email: %ae%n)
47
+ commits << YAML.load(`git show -s --pretty=format:"#{format}" #{sha1}`)
48
+ end
49
+ end
50
+ end
51
+
52
+ def add_commit(message, &action)
53
+ Dir.chdir(@path) do
54
+ yield action
55
+ system %Q(git commit -m "#{message}" &>/dev/null)
56
+ end
57
+ end
58
+
59
+ def add_failing_commit
60
+ add_commit "This commit will fail" do
61
+ system %Q(echo '#{build_script(false)}' > test)
62
+ system %Q(chmod +x test)
63
+ system %Q(git add test &>/dev/null)
64
+ end
65
+ end
66
+
67
+ def add_successful_commit
68
+ add_commit "This commit will work" do
69
+ system %Q(echo '#{build_script(true)}' > test)
70
+ system %Q(chmod +x test)
71
+ system %Q(git add test &>/dev/null)
72
+ end
73
+ end
74
+
75
+ def head
76
+ Dir.chdir(@path) do
77
+ `git log --pretty=format:%H | head -1`.chomp
78
+ end
79
+ end
80
+
81
+ def short_head
82
+ head[0..6]
83
+ end
84
+
85
+ def destroy
86
+ FileUtils.rm_rf @path if File.directory?(@path)
87
+ end
88
+
89
+ protected
90
+
91
+ def build_script(successful=true)
92
+ <<-script
93
+ #!/bin/sh
94
+ echo "Running tests..."
95
+ exit #{successful ? 0 : 1}
96
+ script
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,47 @@
1
+ module NotifierHelper
2
+ def fill_in_email_notifier
3
+ fill_in "notifiers[Email][to]", :with => "quentin@example.com"
4
+ fill_in "notifiers[Email][from]", :with => "ci@example.com"
5
+ fill_in "notifiers[Email][user]", :with => "inspector"
6
+ fill_in "notifiers[Email][pass]", :with => "gadget"
7
+ fill_in "notifiers[Email][auth]", :with => "simple"
8
+ fill_in "notifiers[Email][domain]", :with => "example.com"
9
+ end
10
+
11
+ def fill_in_project_info(name, repo)
12
+ fill_in "Name", :with => name
13
+ fill_in "Git repository", :with => repo
14
+ fill_in "Branch to track", :with => "master"
15
+ fill_in "Build script", :with => "rake"
16
+ check "Public project"
17
+
18
+ fill_in_email_notifier
19
+ end
20
+
21
+ def assert_have_email_notifier
22
+ assert_have_tag "input#email_notifier_to[@value='quentin@example.com']"
23
+ assert_have_tag "input#email_notifier_from[@value='ci@example.com']"
24
+ assert_have_tag "input#email_notifier_user[@value='inspector']"
25
+ assert_have_tag "input#email_notifier_pass[@value='gadget']"
26
+ assert_have_tag "input#email_notifier_auth[@value='simple']"
27
+ assert_have_tag "input#email_notifier_domain[@value='example.com']"
28
+ end
29
+
30
+ def add_project(name, repo)
31
+ visit "/new"
32
+ fill_in_project_info(name, repo)
33
+ click_button "Create Project"
34
+
35
+ assert_have_tag("h1", :content => name)
36
+ click_link 'Edit Project'
37
+ assert_have_email_notifier
38
+ end
39
+
40
+ def edit_project(name)
41
+ visit "/#{name}"
42
+ click_link "Edit Project"
43
+ assert_have_email_notifier
44
+ fill_in :branch, :with => "testing"
45
+ click_button "Update Project"
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ module Integrity
2
+ class Notifier
3
+ class Textfile < Notifier::Base
4
+ def self.to_haml
5
+ <<-haml
6
+ %p.normal
7
+ %label{ :for => "textfile_notifier_file" } File
8
+ %input.text#textfile_notifier_file{ :name => "notifiers[Textfile][file]", :type => "text", :value => config["file"] }
9
+ haml
10
+ end
11
+
12
+ def initialize(build, config={})
13
+ super
14
+ @file = @config["file"]
15
+ end
16
+
17
+ def deliver!
18
+ File.open(@file, "a") do |f|
19
+ f.puts "=== #{short_message} ==="
20
+ f.puts
21
+ f.puts full_message
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,4 @@
1
+ require File.dirname(__FILE__) + "/expectations/be_a"
2
+ require File.dirname(__FILE__) + "/expectations/change"
3
+ require File.dirname(__FILE__) + "/expectations/have"
4
+ require File.dirname(__FILE__) + "/expectations/predicates"
@@ -0,0 +1,23 @@
1
+ module Matchy::Expectations
2
+ class BeAExpectation < Base
3
+ def matches?(receiver)
4
+ @receiver = receiver
5
+ @receiver.is_a?(@expected)
6
+ end
7
+
8
+ def failure_message
9
+ "Expected #{@receiver.inspect} to be a #{@expected.inspect}."
10
+ end
11
+
12
+ def negative_failure_message
13
+ "Expected #{@receiver.inspect} to not be a #{@expected.inspect}."
14
+ end
15
+ end
16
+
17
+ module TestCaseExtensions
18
+ def be_a(obj)
19
+ Matchy::Expectations::BeAExpectation.new(obj, self)
20
+ end
21
+ alias :be_an :be_a
22
+ end
23
+ end
@@ -0,0 +1,90 @@
1
+ module Matchy::Expectations
2
+ class ChangeExpectation < Base
3
+ def initialize(receiver=nil, message=nil, test_case=nil, &block)
4
+ @message = message || "result"
5
+ @value_proc = block || lambda {
6
+ receiver.__send__(message)
7
+ }
8
+ @test_case = test_case
9
+ end
10
+
11
+ def matches?(event_proc)
12
+ raise_block_syntax_error if block_given?
13
+
14
+ @before = evaluate_value_proc
15
+ event_proc.call
16
+ @after = evaluate_value_proc
17
+
18
+ return false if @from unless @from == @before
19
+ return false if @to unless @to == @after
20
+ return (@before + @amount == @after) if @amount
21
+ return ((@after - @before) >= @minimum) if @minimum
22
+ return ((@after - @before) <= @maximum) if @maximum
23
+ return @before != @after
24
+ end
25
+
26
+ def raise_block_syntax_error
27
+ raise ArgumentError, "block passed to should or should_not change must use {} instead of do/end"
28
+ end
29
+
30
+ def evaluate_value_proc
31
+ @value_proc.call
32
+ end
33
+
34
+ def failure_message
35
+ if @to
36
+ "#{@message} should have been changed to #{@to.inspect}, but is now #{@after.inspect}"
37
+ elsif @from
38
+ "#{@message} should have initially been #{@from.inspect}, but was #{@before.inspect}"
39
+ elsif @amount
40
+ "#{@message} should have been changed by #{@amount.inspect}, but was changed by #{actual_delta.inspect}"
41
+ elsif @minimum
42
+ "#{@message} should have been changed by at least #{@minimum.inspect}, but was changed by #{actual_delta.inspect}"
43
+ elsif @maximum
44
+ "#{@message} should have been changed by at most #{@maximum.inspect}, but was changed by #{actual_delta.inspect}"
45
+ else
46
+ "#{@message} should have changed, but is still #{@before.inspect}"
47
+ end
48
+ end
49
+
50
+ def actual_delta
51
+ @after - @before
52
+ end
53
+
54
+ def negative_failure_message
55
+ "#{@message} should not have changed, but did change from #{@before.inspect} to #{@after.inspect}"
56
+ end
57
+
58
+ def by(amount)
59
+ @amount = amount
60
+ self
61
+ end
62
+
63
+ def by_at_least(minimum)
64
+ @minimum = minimum
65
+ self
66
+ end
67
+
68
+ def by_at_most(maximum)
69
+ @maximum = maximum
70
+ self
71
+ end
72
+
73
+ def to(to)
74
+ @to = to
75
+ self
76
+ end
77
+
78
+ def from (from)
79
+ @from = from
80
+ self
81
+ end
82
+ end
83
+
84
+
85
+ module TestCaseExtensions
86
+ def change(receiver=nil, message=nil, &block)
87
+ Matchy::Expectations::ChangeExpectation.new(receiver, message, self, &block)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,105 @@
1
+ module Matchy::Expectations
2
+ class HaveExpectation < Base
3
+ def initialize(expected, relativity=:exactly, test_case = nil)
4
+ @expected = (expected == :no ? 0 : expected)
5
+ @relativity = relativity
6
+ @test_case = test_case
7
+ end
8
+
9
+ def relativities
10
+ @relativities ||= {
11
+ :exactly => "",
12
+ :at_least => "at least ",
13
+ :at_most => "at most "
14
+ }
15
+ end
16
+
17
+ def matches?(collection_owner)
18
+ if collection_owner.respond_to?(@collection_name)
19
+ collection = collection_owner.__send__(@collection_name, *@args, &@block)
20
+ elsif (@plural_collection_name && collection_owner.respond_to?(@plural_collection_name))
21
+ collection = collection_owner.__send__(@plural_collection_name, *@args, &@block)
22
+ elsif (collection_owner.respond_to?(:length) || collection_owner.respond_to?(:size))
23
+ collection = collection_owner
24
+ else
25
+ collection_owner.__send__(@collection_name, *@args, &@block)
26
+ end
27
+ @given = collection.size if collection.respond_to?(:size)
28
+ @given = collection.length if collection.respond_to?(:length)
29
+ raise not_a_collection if @given.nil?
30
+ return @given >= @expected if @relativity == :at_least
31
+ return @given <= @expected if @relativity == :at_most
32
+ return @given == @expected
33
+ end
34
+
35
+ def not_a_collection
36
+ "expected #{@collection_name} to be a collection but it does not respond to #length or #size"
37
+ end
38
+
39
+ def failure_message
40
+ "expected #{relative_expectation} #{@collection_name}, got #{@given}"
41
+ end
42
+
43
+ def negative_failure_message
44
+ if @relativity == :exactly
45
+ return "expected target not to have #{@expected} #{@collection_name}, got #{@given}"
46
+ elsif @relativity == :at_most
47
+ return <<-EOF
48
+ Isn't life confusing enough?
49
+ Instead of having to figure out the meaning of this:
50
+ should_not have_at_most(#{@expected}).#{@collection_name}
51
+ We recommend that you use this instead:
52
+ should have_at_least(#{@expected + 1}).#{@collection_name}
53
+ EOF
54
+ elsif @relativity == :at_least
55
+ return <<-EOF
56
+ Isn't life confusing enough?
57
+ Instead of having to figure out the meaning of this:
58
+ should_not have_at_least(#{@expected}).#{@collection_name}
59
+ We recommend that you use this instead:
60
+ should have_at_most(#{@expected - 1}).#{@collection_name}
61
+ EOF
62
+ end
63
+ end
64
+
65
+ def description
66
+ "have #{relative_expectation} #{@collection_name}"
67
+ end
68
+
69
+ def respond_to?(sym)
70
+ @expected.respond_to?(sym) || super
71
+ end
72
+
73
+ private
74
+
75
+ def method_missing(sym, *args, &block)
76
+ @collection_name = sym
77
+ if inflector = (defined?(ActiveSupport::Inflector) ? ActiveSupport::Inflector : (defined?(Inflector) ? Inflector : nil))
78
+ @plural_collection_name = inflector.pluralize(sym.to_s)
79
+ end
80
+ @args = args
81
+ @block = block
82
+ self
83
+ end
84
+
85
+ def relative_expectation
86
+ "#{relativities[@relativity]}#{@expected}"
87
+ end
88
+ end
89
+
90
+
91
+ module TestCaseExtensions
92
+ def have(n)
93
+ HaveExpectation.new(n, :exactly, self)
94
+ end
95
+ alias :have_exactly :have
96
+
97
+ def have_at_least(n)
98
+ HaveExpectation.new(n, :at_least, self)
99
+ end
100
+
101
+ def have_at_most(n)
102
+ HaveExpectation.new(n, :at_most, self)
103
+ end
104
+ end
105
+ end