cutting_edge 0.0.1 → 0.1
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.
- checksums.yaml +4 -4
- data/Gemfile +7 -2
- data/Gemfile.lock +130 -0
- data/LICENSE +68 -81
- data/Procfile +1 -0
- data/README.md +272 -74
- data/Rakefile +0 -5
- data/bin/cutting_edge +60 -45
- data/config.rb +55 -0
- data/cutting_edge.gemspec +33 -4
- data/heroku.config.rb +20 -0
- data/lib/cutting_edge.rb +1 -1
- data/lib/cutting_edge/app.rb +95 -19
- data/lib/cutting_edge/langs.rb +7 -1
- data/lib/cutting_edge/langs/python.rb +5 -1
- data/lib/cutting_edge/langs/ruby.rb +5 -1
- data/lib/cutting_edge/langs/rust.rb +5 -1
- data/lib/cutting_edge/public/images/error.svg +24 -0
- data/lib/cutting_edge/public/images/languages/python.svg +1 -0
- data/lib/cutting_edge/public/images/languages/ruby.svg +1 -0
- data/lib/cutting_edge/public/images/languages/rust.svg +1 -0
- data/lib/cutting_edge/public/images/ok.svg +24 -0
- data/lib/cutting_edge/public/javascript/clipboard.min.js +7 -0
- data/lib/cutting_edge/public/javascript/cuttingedge.js +53 -0
- data/lib/cutting_edge/public/stylesheets/primer.css +22 -0
- data/lib/cutting_edge/repo.rb +124 -18
- data/lib/cutting_edge/templates/_footer.html.erb +3 -0
- data/lib/cutting_edge/templates/_header.html.erb +8 -0
- data/lib/cutting_edge/templates/_overview.html.erb +9 -0
- data/lib/cutting_edge/templates/badge.svg.erb +39 -0
- data/lib/cutting_edge/templates/index.html.erb +62 -0
- data/lib/cutting_edge/templates/info.html.erb +101 -0
- data/lib/cutting_edge/templates/mail.html.erb +163 -0
- data/lib/cutting_edge/workers/badge.rb +33 -11
- data/lib/cutting_edge/workers/dependency.rb +36 -16
- data/lib/cutting_edge/workers/helpers.rb +8 -0
- data/lib/cutting_edge/workers/mail.rb +38 -0
- data/projects.yml +25 -0
- data/spec/app_spec.rb +115 -0
- data/spec/badge_worker_spec.rb +77 -0
- data/spec/dependency_worker_spec.rb +132 -0
- data/spec/email_worker_spec.rb +43 -0
- data/spec/fixtures.rb +180 -0
- data/spec/fixtures/projects.yml +27 -0
- data/spec/langs/python_spec.rb +47 -5
- data/spec/langs/ruby_spec.rb +105 -0
- data/spec/langs/rust_spec.rb +31 -0
- data/spec/repo_spec.rb +52 -0
- data/spec/spec_helper.rb +9 -1
- metadata +43 -15
- data/lib/cutting_edge/badge.rb +0 -46
@@ -14,6 +14,14 @@ module WorkerHelpers
|
|
14
14
|
def get_from_store(identifier)
|
15
15
|
::CuttingEdge::App.store[identifier]
|
16
16
|
end
|
17
|
+
|
18
|
+
def badge_worker(identifier)
|
19
|
+
BadgeWorker.perform_async(identifier)
|
20
|
+
end
|
21
|
+
|
22
|
+
def mail_worker(identifier, to_address)
|
23
|
+
MailWorker.perform_async(identifier, to_address)
|
24
|
+
end
|
17
25
|
|
18
26
|
end
|
19
27
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path('../helpers.rb', __FILE__)
|
2
|
+
require 'erb'
|
3
|
+
require 'mail'
|
4
|
+
|
5
|
+
module CuttingEdge
|
6
|
+
MAIL_TEMPLATE = File.read(File.expand_path('../../templates/mail.html.erb', __FILE__)) unless defined?(MAIL_TEMPLATE)
|
7
|
+
end
|
8
|
+
|
9
|
+
class MailWorker < GenericWorker
|
10
|
+
|
11
|
+
def perform(identifier, to_addr)
|
12
|
+
log_info('Running Worker!')
|
13
|
+
dependencies = get_from_store(identifier)
|
14
|
+
unless to_addr && dependencies
|
15
|
+
log_info("Failed to execute email job for #{identifier}: #{dependencies ? dependencies : 'No dependencies found.'} #{'No e-mail address set.' if to_addr.nil?}")
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
|
19
|
+
Mail.deliver do
|
20
|
+
from "CuttingEdge <#{CuttingEdge::MAIL_FROM}>"
|
21
|
+
to to_addr
|
22
|
+
subject "Dependency Status Changed For #{identifier}"
|
23
|
+
|
24
|
+
text_part do
|
25
|
+
body "Dependency Status Update For #{identifier} By CuttingEdge"
|
26
|
+
end
|
27
|
+
|
28
|
+
html_part do
|
29
|
+
content_type 'text/html; charset=UTF-8'
|
30
|
+
body ERB.new(CuttingEdge::MAIL_TEMPLATE).result_with_hash(
|
31
|
+
project: identifier,
|
32
|
+
url: CuttingEdge::SERVER_URL,
|
33
|
+
specs: dependencies
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/projects.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Example projects.yml file for CuttingEdge
|
2
|
+
# For more information and options, see: https://github.com/repotag/cutting_edge
|
3
|
+
|
4
|
+
github:
|
5
|
+
repotag:
|
6
|
+
cutting_edge:
|
7
|
+
language: ruby
|
8
|
+
hide: mysecret
|
9
|
+
gollum:
|
10
|
+
gollum:
|
11
|
+
language: ruby
|
12
|
+
gollum-lib:
|
13
|
+
locations: [gemspec.rb]
|
14
|
+
dependency_types: [runtime, development]
|
15
|
+
singingwolfboy:
|
16
|
+
flask-dance:
|
17
|
+
language: python
|
18
|
+
email: false
|
19
|
+
rust-lang:
|
20
|
+
crates.io:
|
21
|
+
language: rust
|
22
|
+
gitlab:
|
23
|
+
cthowl01:
|
24
|
+
team-chess-ruby:
|
25
|
+
locations: [Gemfile]
|
data/spec/app_spec.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CuttingEdgeHelpers do
|
4
|
+
it 'fails to load invalid projects.yml' do
|
5
|
+
expect(load_repositories('/tmp/fail.yml')).to be_nil
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe CuttingEdge::App do
|
10
|
+
before(:all) {
|
11
|
+
CuttingEdge::App.set(:repositories, load_repositories(File.expand_path('../fixtures/projects.yml', __FILE__)))
|
12
|
+
CuttingEdge::App.set(:store, Moneta.new(:Memory))
|
13
|
+
}
|
14
|
+
let(:app) { CuttingEdge::App.new }
|
15
|
+
|
16
|
+
context 'landing page' do
|
17
|
+
let(:response) { get '/' }
|
18
|
+
it 'returns status 200 OK' do
|
19
|
+
expect(response.status).to eq 200
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'unknown project' do
|
24
|
+
let(:project) { 'github/doesnt/exist' }
|
25
|
+
['/info', '/info/json', '/svg', '/refresh'].each do |route|
|
26
|
+
it "returns status 404 not found for #{route}" do
|
27
|
+
response = get("/#{project}#{route}")
|
28
|
+
expect(response.status).to eq 404
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'known project' do
|
34
|
+
let(:project) { 'github/gollum/gollum' }
|
35
|
+
before {
|
36
|
+
add_to_store("svg-#{project}", 'test')
|
37
|
+
add_to_store(project, {'test' => true})
|
38
|
+
}
|
39
|
+
{'/info' => 'text/html;charset=utf-8', '/info/json' => 'application/json', '/svg' => 'image/svg+xml'}.each do |route, type|
|
40
|
+
it "returns status 200 OK for #{route} with mime #{type}" do
|
41
|
+
response = get("/#{project}#{route}")
|
42
|
+
expect(response.status).to eq 200
|
43
|
+
expect(response.content_type).to eq type
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns status 500 for a known project json without data' do
|
48
|
+
response = get("/github/gollum/gollum-lib/info/json")
|
49
|
+
# the store contains nothing for gollum-lib
|
50
|
+
expect(response.status).to eq 500
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'svgs have cache-control: no-cache' do
|
54
|
+
response = get("/#{project}/svg")
|
55
|
+
expect(response.headers['Cache-Control']).to eq 'no-cache'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'hidden repos' do
|
60
|
+
let(:project) { 'gitlab/gitlab-org/gitlab-foss' }
|
61
|
+
let(:repo) { CuttingEdge::App.repositories[project] }
|
62
|
+
|
63
|
+
it 'does not list hidden repos on the landing page' do
|
64
|
+
response = get('/')
|
65
|
+
expect(response.body).to include('team-chess-ruby')
|
66
|
+
expect(response.body).not_to include('gitlab-foss')
|
67
|
+
end
|
68
|
+
|
69
|
+
before {
|
70
|
+
::CuttingEdge::SECRET_TOKEN = 'secret'
|
71
|
+
add_to_store(project, {'test' => true})
|
72
|
+
}
|
73
|
+
after {
|
74
|
+
CuttingEdge.send(:remove_const, :SECRET_TOKEN)
|
75
|
+
}
|
76
|
+
|
77
|
+
it 'returns a JSON encoded HTML partial if the token is correct' do
|
78
|
+
response = post('/hidden_repos', "{\"token\":\"secret\"}")
|
79
|
+
expect(response.body).to include('gitlab-foss')
|
80
|
+
expect(JSON.parse(response.body)['partial']).to include "<div class=\"Box\">"
|
81
|
+
expect(JSON.parse(response.body)['partial']).to include "gitlab/gitlab-org/gitlab-foss"
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'only gives access to repo information with the repo hidden token' do
|
85
|
+
['/info', '/info/json', '/svg'].each do |route|
|
86
|
+
url = File.join('/', project, route)
|
87
|
+
unauthorized_response = get(url)
|
88
|
+
expect(unauthorized_response.status).to eq 404
|
89
|
+
authorized_response = get(url, token: repo.hidden_token)
|
90
|
+
expect(authorized_response.status).to eq 200
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'refreshing' do
|
97
|
+
let(:project) { 'github/gollum/gollum' }
|
98
|
+
before {
|
99
|
+
::CuttingEdge::SECRET_TOKEN = 'secret'
|
100
|
+
}
|
101
|
+
after {
|
102
|
+
CuttingEdge.send(:remove_const, :SECRET_TOKEN)
|
103
|
+
}
|
104
|
+
|
105
|
+
it 'fails with wrong token' do
|
106
|
+
response = post "/#{project}/refresh", :token => 'fail'
|
107
|
+
expect(response.status).to eq 401
|
108
|
+
end
|
109
|
+
it 'succeeds with right token' do
|
110
|
+
expect(DependencyWorker).to receive(:perform_async).exactly(:once)
|
111
|
+
response = post "/#{project}/refresh", token: 'secret'
|
112
|
+
expect(response.status).to eq 200
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
standard_svg = <<EOF
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="20" role="img" aria-label="CuttingEdge Dependency Status">
|
3
|
+
<title>CuttingEdge Dependency Status</title>
|
4
|
+
<linearGradient id="s" x2="0" y2="100%">
|
5
|
+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
6
|
+
<stop offset="1" stop-opacity=".1"/>
|
7
|
+
</linearGradient>
|
8
|
+
<clipPath id="r">
|
9
|
+
<rect width="100" height="20" rx="3" fill="#fff"/>
|
10
|
+
</clipPath>
|
11
|
+
<g clip-path="url(#r)">
|
12
|
+
<rect width="35" height="20" fill="#555"/>
|
13
|
+
|
14
|
+
<rect x="25" width="25" height="20" fill="#4c1"/>
|
15
|
+
|
16
|
+
<rect x="50" width="25" height="20" fill="#fe7d37"/>
|
17
|
+
|
18
|
+
<rect x="75" width="25" height="20" fill="#e05d44"/>
|
19
|
+
|
20
|
+
<rect width="100" height="20" fill="url(#s)"/>
|
21
|
+
</g>
|
22
|
+
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110">
|
23
|
+
|
24
|
+
<text aria-hidden="true" x="125" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)">CE</text>
|
25
|
+
<text x="125" y="140" transform="scale(.1)" fill="#fff">CE</text>
|
26
|
+
|
27
|
+
|
28
|
+
<g>
|
29
|
+
<title>Ok: 3</title>
|
30
|
+
<text aria-hidden="true" x="370" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)">3</text>
|
31
|
+
<text x="370" y="140" transform="scale(.1)" fill="#fff">3</text>
|
32
|
+
</g>
|
33
|
+
|
34
|
+
<g>
|
35
|
+
<title>Outdated Minor: 1</title>
|
36
|
+
<text aria-hidden="true" x="620" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)">1</text>
|
37
|
+
<text x="620" y="140" transform="scale(.1)" fill="#fff">1</text>
|
38
|
+
</g>
|
39
|
+
|
40
|
+
<g>
|
41
|
+
<title>Outdated Major: 3</title>
|
42
|
+
<text aria-hidden="true" x="870" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)">3</text>
|
43
|
+
<text x="870" y="140" transform="scale(.1)" fill="#fff">3</text>
|
44
|
+
</g>
|
45
|
+
|
46
|
+
</g>
|
47
|
+
</svg>
|
48
|
+
EOF
|
49
|
+
describe BadgeWorker do
|
50
|
+
let(:worker) { BadgeWorker.new }
|
51
|
+
let(:identifier) { 'github/gollum/gollum' }
|
52
|
+
|
53
|
+
let(:dependencies) {
|
54
|
+
mock_dependencies('gollum')
|
55
|
+
}
|
56
|
+
|
57
|
+
context 'performing' do
|
58
|
+
|
59
|
+
it 'generates an svg for outdated dependencies' do
|
60
|
+
expect(worker).to receive(:get_from_store).with(identifier).and_return(dependencies)
|
61
|
+
expect(worker).to receive(:add_to_store).with("svg-#{identifier}", standard_svg.chomp).and_return(true)
|
62
|
+
worker.perform(identifier)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'shows an error badge when there are no dependencies' do
|
66
|
+
expect(worker).to receive(:get_from_store).with(identifier).and_return(nil)
|
67
|
+
expect(worker).to receive(:add_to_store).with("svg-#{identifier}", CuttingEdge::BADGE_ERROR).and_return(true)
|
68
|
+
worker.perform(identifier)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'shows an ok badge when up to date' do
|
72
|
+
expect(worker).to receive(:get_from_store).with(identifier).and_return(mock_dependencies('ok'))
|
73
|
+
expect(worker).to receive(:add_to_store).with("svg-#{identifier}", CuttingEdge::BADGE_OK).and_return(true)
|
74
|
+
worker.perform(identifier)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
class MockResponse
|
2
|
+
def initialize(status, body)
|
3
|
+
@status = status
|
4
|
+
@body = body
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :status
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
@body
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe DependencyWorker do
|
15
|
+
let(:worker) { DependencyWorker.new }
|
16
|
+
let(:identifier) { 'github/gollum/gollum' }
|
17
|
+
let(:test_email) { 'cutting_edge@localhost' }
|
18
|
+
let(:lang) { 'ruby' }
|
19
|
+
let(:locations) {
|
20
|
+
{
|
21
|
+
'gollum.gemspec' => 'http://github.bla/gollum/gollum/gollum.gemspec',
|
22
|
+
'Gemfile' => 'http://github.bla/gollum/gollum/Gemfile'
|
23
|
+
}
|
24
|
+
}
|
25
|
+
let(:dependency_types) { [:runtime] }
|
26
|
+
let(:old_dependencies) {
|
27
|
+
mock_dependencies('gollum')
|
28
|
+
}
|
29
|
+
let(:new_dependencies) {
|
30
|
+
mock_dependencies('gollum-updated')
|
31
|
+
}
|
32
|
+
|
33
|
+
context 'http fetching files' do
|
34
|
+
let(:response_ok) { MockResponse.new(200, 'body') }
|
35
|
+
let(:response_not_found) { MockResponse.new(404, 'Not found') }
|
36
|
+
let(:url) { locations.first.last }
|
37
|
+
before {
|
38
|
+
worker.instance_variable_set(:@provider, ::CuttingEdge::GitlabRepository)
|
39
|
+
}
|
40
|
+
|
41
|
+
it 'uses default headers' do
|
42
|
+
worker.instance_variable_set(:@provider, ::CuttingEdge::GithubRepository)
|
43
|
+
expect(HTTP).to receive(:headers).with({:accept => 'application/vnd.github.v3.raw'}).exactly(:once).and_call_original
|
44
|
+
expect_any_instance_of(HTTP::Client).to receive(:get).with(url).and_return(response_ok)
|
45
|
+
expect(worker.send(:http_get, url)).to eq 'body'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns body when http get is successful' do
|
49
|
+
expect_any_instance_of(HTTP::Client).to receive(:get).with(url).and_return(response_ok)
|
50
|
+
expect(worker.send(:http_get, url)).to eq 'body'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'uses an auth header when it is set for project' do
|
54
|
+
token = 'token'
|
55
|
+
header = "Bearer #{token}"
|
56
|
+
worker.instance_variable_set(:@auth_token, token)
|
57
|
+
expect(::CuttingEdge::GitlabRepository).to receive(:headers).with(token).exactly(:once).and_call_original
|
58
|
+
expect(HTTP).to receive(:headers).with(:authorization => 'Bearer token').exactly(:once).and_call_original
|
59
|
+
expect_any_instance_of(HTTP::Client).to receive(:get).with(url).and_return(response_ok)
|
60
|
+
expect(worker.send(:http_get, url)).to eq 'body'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns body when http get is unsuccessful' do
|
64
|
+
expect_any_instance_of(HTTP::Client).to receive(:get).with(url).and_return(response_not_found)
|
65
|
+
expect(worker.send(:http_get, url)).to be_nil
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'handles a timeout' do
|
69
|
+
expect_any_instance_of(HTTP::Client).to receive(:get).with(url).and_raise(HTTP::TimeoutError)
|
70
|
+
expect(worker).to receive(:log_info)
|
71
|
+
expect(worker.send(:http_get, url)).to be_nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'generates results for fetched requirements' do
|
76
|
+
worker.instance_variable_set(:@lang, RubyLang)
|
77
|
+
fetched = [RubyLang.unknown_dependency('unknown_requirement'), Gem::Version.new('1.0')]
|
78
|
+
result_no_requirement = {:no_requirement=>[{:latest=>"1.0", :name=>"unknown_requirement", :required=>"unknown", :type=>:runtime, :url=>nil}], :ok=>[], :outdated_major=>[], :outdated_minor=>[], :outdated_patch=>[], :unknown=>[]}
|
79
|
+
expect(worker.send(:get_results, [fetched], dependency_types)).to eq result_no_requirement
|
80
|
+
|
81
|
+
fetched = [Gem::Dependency.new('unknown_version', '1.0', :runtime), nil]
|
82
|
+
result_no_version = {:no_requirement=>[], :ok=>[], :outdated_major=>[], :outdated_minor=>[], :outdated_patch=>[], :unknown=>[{:latest=>"", :name=>"unknown_version", :required=>"= 1.0", :type=>:runtime, :url=>"https://rubygems.org/gems/unknown_version"}]}
|
83
|
+
expect(worker.send(:get_results, [fetched], dependency_types)).to eq result_no_version
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'performing' do
|
87
|
+
|
88
|
+
before(:each) {
|
89
|
+
expect(worker).to receive(:get_from_store).with(identifier).and_return(old_dependencies)
|
90
|
+
expect(worker).to receive(:http_get).and_return('fake').exactly(locations.length).times
|
91
|
+
}
|
92
|
+
|
93
|
+
context 'when the dependencies have changed' do
|
94
|
+
|
95
|
+
before(:each) {
|
96
|
+
locations.each_key do |loc|
|
97
|
+
expect(RubyLang).to receive(:parse_file).with(loc, 'fake').and_return(mock_fetched_requirements('gollum', loc, true))
|
98
|
+
end
|
99
|
+
expect(worker).to receive(:badge_worker).with(identifier).and_return(true)
|
100
|
+
expect(worker).to receive(:mail_worker).with(identifier, test_email).and_return(true)
|
101
|
+
}
|
102
|
+
|
103
|
+
it 'updates the store with newest dependencies' do
|
104
|
+
expect(worker).to receive(:add_to_store).with(identifier, new_dependencies).and_return(true)
|
105
|
+
expect(worker.instance_variable_get(:@nothing_changed)).to be_nil
|
106
|
+
worker.perform(identifier, lang, locations, dependency_types, test_email)
|
107
|
+
expect(worker.instance_variable_get(:@nothing_changed)).to be false
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'when the dependencies have not changed' do
|
113
|
+
|
114
|
+
before(:each) {
|
115
|
+
locations.each_key do |loc|
|
116
|
+
expect(RubyLang).to receive(:parse_file).with(loc, 'fake').and_return(mock_fetched_requirements('gollum', loc, false))
|
117
|
+
end
|
118
|
+
expect(worker).not_to receive(:badge_worker)
|
119
|
+
expect(worker).not_to receive(:mail_worker)
|
120
|
+
}
|
121
|
+
|
122
|
+
it 'does not update the store' do
|
123
|
+
expect(worker).not_to receive(:add_to_store)
|
124
|
+
expect(worker.instance_variable_get(:@nothing_changed)).to be_nil
|
125
|
+
worker.perform(identifier, lang, locations, dependency_types, test_email)
|
126
|
+
expect(worker.instance_variable_get(:@nothing_changed)).to be true
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'mail'
|
2
|
+
Mail.defaults do
|
3
|
+
delivery_method :test
|
4
|
+
end
|
5
|
+
|
6
|
+
describe MailWorker do
|
7
|
+
let(:worker) { MailWorker.new }
|
8
|
+
let(:identifier) { 'github/repotag/cutting_edge' }
|
9
|
+
let(:test_email) { 'cutting_edge@localhost' }
|
10
|
+
let(:dependencies) {
|
11
|
+
mock_dependencies('gollum')
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:each) {
|
15
|
+
expect(worker).to receive(:get_from_store).with(identifier).and_return(dependencies)
|
16
|
+
}
|
17
|
+
|
18
|
+
it 'returns nil when to address is nil ' do
|
19
|
+
expect(worker.perform(identifier, nil)).to eq nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sends an update mail' do
|
23
|
+
expect(Mail::TestMailer.deliveries).to be_empty
|
24
|
+
worker.perform(identifier, test_email)
|
25
|
+
expect(Mail::TestMailer.deliveries).to_not be_empty
|
26
|
+
|
27
|
+
mail = Mail::TestMailer.deliveries.first
|
28
|
+
expect(mail.from.first).to eq 'cutting_edge@localhost'
|
29
|
+
expect(mail.to.first).to eq test_email
|
30
|
+
expect(mail.subject).to eq "Dependency Status Changed For #{identifier}"
|
31
|
+
|
32
|
+
body = mail.body
|
33
|
+
expect(body.parts.length).to eq 2
|
34
|
+
|
35
|
+
html_body = body.parts.last.to_s
|
36
|
+
expect(html_body).to start_with('Content-Type: text/html')
|
37
|
+
expect(html_body).to include('This is <a href="http://localhost">CuttingEdge</a> informing you')
|
38
|
+
expect(html_body).to include("<a href=\"http://localhost/#{identifier}/info\">#{identifier}</a>")
|
39
|
+
expect(html_body).to include('In <b>gollum.gemspec</b>:')
|
40
|
+
expect(html_body).to include('<b>Outdated Major</b>:')
|
41
|
+
expect(html_body).to include('<li>rake ~> 12.3, >= 12.3.3 (latest: 13.0.1)</li>')
|
42
|
+
end
|
43
|
+
end
|