60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remind101/slashdeploy/342375402e40c424a1c87e190f612d68f77d163e/public/favicon.ico
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/spec/features/add_to_slack_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.feature 'Add to Slack' do
4 | fixtures :all
5 |
6 | after do
7 | OmniAuth.config.mock_auth[:slack] = nil
8 | end
9 |
10 | scenario 'clicking the Add to Slack button' do
11 | OmniAuth.config.mock_auth[:slack] = OmniAuth::AuthHash.new(
12 | provider: 'slack',
13 | uid: '123545',
14 | info: {
15 | nickname: 'joe',
16 | team_id: 'XXXXXXXXXX',
17 | team_domain: 'acme'
18 | },
19 | credentials: {
20 | token: 'xoxt-23984754863-2348975623103'
21 | },
22 | extra: {
23 | raw_info: {
24 | url: "https://some-org_name.slack.com"
25 | },
26 | bot_info: {
27 | bot_user_id: 'UTTTTTTTTTTR',
28 | bot_access_token: 'xoxb-XXXXXXXXXXXX-TTTTTTTTTTTTTT'
29 | }
30 | }
31 | )
32 |
33 | expect do
34 | visit '/auth/slack/callback'
35 | end.to change { SlackBot.count }.by(1)
36 | # expect(page).to have_content 'Success!'
37 | end
38 |
39 | scenario 'clicking the Add to Slack button a second time' do
40 | OmniAuth.config.mock_auth[:slack] = OmniAuth::AuthHash.new(
41 | provider: 'slack',
42 | uid: '123545',
43 | info: {
44 | nickname: 'joe',
45 | team_id: 'XXXXXXXXXX',
46 | team_domain: 'acme'
47 | },
48 | credentials: {
49 | token: 'xoxt-23984754863-2348975623103'
50 | },
51 | extra: {
52 | raw_info: {
53 | url: "https://some-org_name.slack.com"
54 | },
55 | bot_info: {
56 | bot_user_id: 'UTTTTTTTTTTR',
57 | bot_access_token: 'xoxb-XXXXXXXXXXXX-TTTTTTTTTTTTTT'
58 | }
59 | }
60 | )
61 |
62 | expect do
63 | visit '/auth/slack/callback'
64 | end.to change { SlackBot.count }.by(1)
65 |
66 | expect do
67 | visit '/auth/slack/callback'
68 | end.to change { SlackBot.count }.by(0)
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/spec/fixtures/github/installation_event.json:
--------------------------------------------------------------------------------
1 | {
2 | "action": "created",
3 | "installation": {
4 | "id": 1234,
5 | "account": {
6 | "login": "octocat",
7 | "id": 1,
8 | "avatar_url": "https://github.com/images/error/octocat_happy.gif",
9 | "gravatar_id": "",
10 | "url": "https://api.github.com/users/octocat",
11 | "html_url": "https://github.com/octocat",
12 | "followers_url": "https://api.github.com/users/octocat/followers",
13 | "following_url": "https://api.github.com/users/octocat/following{/other_user}",
14 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
15 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
16 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
17 | "organizations_url": "https://api.github.com/users/octocat/orgs",
18 | "repos_url": "https://api.github.com/users/octocat/repos",
19 | "events_url": "https://api.github.com/users/octocat/events{/privacy}",
20 | "received_events_url": "https://api.github.com/users/octocat/received_events",
21 | "type": "User",
22 | "site_admin": false
23 | },
24 | "repository_selection": "selected",
25 | "access_tokens_url": "https://api.github.com/installations/2/access_tokens",
26 | "repositories_url": "https://api.github.com/installation/repositories"
27 | },
28 | "sender": {
29 | "login": "octocat",
30 | "id": 1,
31 | "avatar_url": "https://github.com/images/error/octocat_happy.gif",
32 | "gravatar_id": "",
33 | "url": "https://api.github.com/users/octocat",
34 | "html_url": "https://github.com/octocat",
35 | "followers_url": "https://api.github.com/users/octocat/followers",
36 | "following_url": "https://api.github.com/users/octocat/following{/other_user}",
37 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
38 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
39 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
40 | "organizations_url": "https://api.github.com/users/octocat/orgs",
41 | "repos_url": "https://api.github.com/users/octocat/repos",
42 | "events_url": "https://api.github.com/users/octocat/events{/privacy}",
43 | "received_events_url": "https://api.github.com/users/octocat/received_events",
44 | "type": "User",
45 | "site_admin": false
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/spec/fixtures/github_accounts.yml:
--------------------------------------------------------------------------------
1 | david:
2 | user: david
3 | id: 36412047
4 | token: "05e567b87752821edcb615552f8917c7"
5 | login: david
6 | steve:
7 | user: steve
8 | id: 28462522
9 | token: "ff878051fd34eb72a002190a9111508c"
10 | login: steve
11 | bob:
12 | user: bob
13 | id: 19744705
14 | token: "3970a23cd8975706ce2efa4260b2f655"
15 | login: bob
16 |
--------------------------------------------------------------------------------
/spec/fixtures/installations.yml:
--------------------------------------------------------------------------------
1 | baxterthehacker:
2 | id: 1234
3 |
--------------------------------------------------------------------------------
/spec/fixtures/repositories.yml:
--------------------------------------------------------------------------------
1 | 'baxterthehacker/public-repo':
2 | name: 'baxterthehacker/public-repo'
3 | github_secret: secret
4 | installation_id: 1234
5 | raw_config: "---\n"
6 | 'acme-inc/api':
7 | name: 'acme-inc/api'
8 | github_secret: secret
9 | installation_id: 1234
10 | raw_config: |
11 | environments:
12 | production: {}
13 | staging:
14 | aliases:
15 | - stage
16 | cd/no_contexts:
17 | continuous_delivery:
18 | ref: refs/heads/master
19 |
--------------------------------------------------------------------------------
/spec/fixtures/slack_accounts.yml:
--------------------------------------------------------------------------------
1 | david:
2 | user: david
3 | id: "U012AB1AB"
4 | user_name: david
5 | slack_team: acme
6 | david_baxterthehacker:
7 | user: david
8 | id: "U012AB1AC"
9 | user_name: david
10 | slack_team: baxterthehacker
11 | steve:
12 | user: steve
13 | id: "U123BC2BD"
14 | user_name: steve
15 | slack_team: acme
16 | bob:
17 | user: bob
18 | id: "U987AD3CD"
19 | user_name: bob
20 | slack_team: skynet
21 |
--------------------------------------------------------------------------------
/spec/fixtures/slack_teams.yml:
--------------------------------------------------------------------------------
1 | acme:
2 | domain: "acme"
3 | github_organization: "acme-inc"
4 | baxterthehacker:
5 | domain: "baxterthehacker"
6 | github_organization: "baxterthehacker"
7 | skynet:
8 | domain: "skynet"
9 |
--------------------------------------------------------------------------------
/spec/fixtures/users.yml:
--------------------------------------------------------------------------------
1 | david: {}
2 | steve: {}
3 | bob: {} # nobody likes bob
4 |
--------------------------------------------------------------------------------
/spec/github/github_event_handler_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe GitHubEventHandler do
4 | fixtures :installations
5 |
6 | let(:handler) do
7 | Class.new(GitHubEventHandler) do
8 | def run
9 | end
10 | end
11 | end
12 |
13 | describe '#call from installation' do
14 | context 'when the repo is not found' do
15 | # This could happen if the integration is installed organization wide.
16 | it 'verifies the signature and creates the repository' do
17 | req = Rack::MockRequest.new(handler)
18 | expect do
19 | resp = req.post \
20 | '/',
21 | input: {
22 | repository: {
23 | full_name: 'acme-inc/frontend'
24 | },
25 | installation: {
26 | id: 1234
27 | }
28 | }.to_json,
29 | 'CONTENT_TYPE' => 'application/json',
30 | 'HTTP_X_HUB_SIGNATURE' => 'sha1=cf3087c974f5bee8c93210f61b0186e0722c605d'
31 | expect(resp.status).to eq 200
32 | end.to change { Repository.count }
33 | end
34 | end
35 |
36 | context 'when the signature does not match' do
37 | it 'returns a 403' do
38 | Repository.create!(name: 'acme-inc/api', github_secret: 'secret')
39 | req = Rack::MockRequest.new(handler)
40 | resp = req.post \
41 | '/',
42 | input: {
43 | repository: {
44 | full_name: 'acme-inc/api'
45 | },
46 | installation: {
47 | id: 1234
48 | }
49 | }.to_json,
50 | 'CONTENT_TYPE' => 'application/json',
51 | 'HTTP_X_HUB_SIGNATURE' => 'sha1=abcd'
52 | expect(resp.status).to eq 403
53 | end
54 | end
55 |
56 | context 'when the signature matches' do
57 | it 'returns a 200 and calls the handler' do
58 | Repository.create!(name: 'acme-inc/api', github_secret: 'secret')
59 | req = Rack::MockRequest.new(handler)
60 | resp = req.post \
61 | '/',
62 | input: {
63 | repository: {
64 | full_name: 'acme-inc/api'
65 | },
66 | installation: {
67 | id: 1234
68 | }
69 | }.to_json,
70 | 'CONTENT_TYPE' => 'application/json',
71 | 'HTTP_X_HUB_SIGNATURE' => 'sha1=1290d145b7ac29e87238b4b129bc10076e22387f'
72 | expect(resp.status).to eq 200
73 | end
74 | end
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/spec/hookshot/router_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Hookshot::Router do
4 | describe '#call' do
5 | it 'routes to the correct apps' do
6 | router = Hookshot::Router.new
7 | router.handle :push, -> (_env) { [200, {}, ['push event']] }
8 | router.handle :deployment, -> (_env) { [200, {}, ['deployment event']] }
9 |
10 | status, _headers, body = router.call(env_for_event('push'))
11 | expect(status).to eq 200
12 | expect(body).to eq ['push event']
13 |
14 | status, _headers, body = router.call(env_for_event('deployment'))
15 | expect(status).to eq 200
16 | expect(body).to eq ['deployment event']
17 |
18 | status, _headers, _body = router.call(env_for_event('ping'))
19 | expect(status).to eq 204
20 | end
21 | end
22 |
23 | def env_for_event(event)
24 | env = Rack::MockRequest.env_for('/')
25 | env['HTTP_X_GITHUB_EVENT'] = event
26 | env
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/hookshot_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Hookshot do
4 | describe '.signature' do
5 | it 'calculates the signature' do
6 | signature = Hookshot.signature '{"event":"data"}', '1234'
7 | expect(signature).to eq 'ade133892a181fba3a21c163cd5cbc3f5f8e915c'
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/models/deployment_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Deployment, type: :model do
4 | describe '#organization' do
5 | it 'returns the organization' do
6 | deployment = Deployment.new(
7 | url: 'https://api.github.com/repos/octocat/example/deployments/1',
8 | repository: 'octocat/example'
9 | )
10 | expect(deployment.organization).to eq 'octocat'
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/models/environment_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Environment, type: :model do
4 | describe '#in_channel' do
5 | it 'defaults to true for production environments' do
6 | environment = Environment.new(name: 'production')
7 | expect(environment.in_channel).to be_truthy
8 | end
9 |
10 | it 'defaults to false for other environments' do
11 | environment = Environment.new(name: 'staging')
12 | expect(environment.in_channel).to be_falsy
13 | end
14 | end
15 |
16 | describe '#default_ref' do
17 | context 'when the environment has a default ref provided' do
18 | it 'returns that value' do
19 | environment = Environment.new(default_ref: 'develop')
20 | expect(environment.default_ref).to eq 'develop'
21 | end
22 | end
23 |
24 | context 'when the environment does not have a default ref' do
25 | it 'returns the global default' do
26 | environment = Environment.new(default_ref: '')
27 | expect(environment.default_ref).to eq 'master'
28 |
29 | environment = Environment.new
30 | expect(environment.default_ref).to eq 'master'
31 | end
32 | end
33 | end
34 |
35 | describe '#match_name' do
36 | context 'when the repository has a config set' do
37 | it 'returns the correct value' do
38 | repo = Repository.with_name('acme-inc/api')
39 | repo.configure! <<-YAML
40 | environments:
41 | production:
42 | aliases: [prod]
43 | YAML
44 |
45 | environment = Environment.new(name: 'production', repository: repo)
46 | expect(environment.match_name?('production')).to eq true
47 | expect(environment.match_name?('prod')).to eq true
48 | expect(environment.match_name?('pro')).to eq false
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/spec/models/lock_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Lock, type: :model do
4 | describe '#lock!' do
5 | fixtures :users
6 | let(:user) { users(:david) }
7 |
8 | it 'only allows for 1 active lock per environment' do
9 | repo = Repository.with_name('acme-inc/api')
10 | repo.raw_config = <<-YAML
11 | environments:
12 | production: {}
13 | staging: {}
14 | YAML
15 |
16 | # Trying the lock the same environment should result in an error.
17 | staging = repo.environment('staging')
18 | expect { staging.lock! user }.to change { staging.locks.count }.by(1)
19 | expect do
20 | expect { staging.lock! user }.to raise_error ActiveRecord::RecordNotUnique
21 | end.to_not change { staging.locks.count }
22 |
23 | # Trying to lock a different environment for the same repo should be fine.
24 | prod = repo.environment('production')
25 | expect { prod.lock! user }.to change { prod.locks.count }.by(1)
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/models/message_action_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe MessageAction, type: :model do
4 | end
5 |
--------------------------------------------------------------------------------
/spec/models/repository_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Repository, type: :model do
4 | describe '#name' do
5 | it 'gets validated as a GitHub repository name' do
6 | repo = Repository.new(name: 'foo')
7 | expect(repo).to be_invalid
8 | expect(repo.errors[:name]).to eq ['not a valid GitHub repository']
9 | end
10 | end
11 |
12 | describe '#organization' do
13 | it 'returns the organization' do
14 | repo = Repository.new(name: 'ejholmes/foo')
15 | expect(repo.organization).to eq 'ejholmes'
16 | end
17 | end
18 |
19 | describe '#environment' do
20 | context 'when given and environment name' do
21 | it 'returns the environment with that name' do
22 | repo = Repository.with_name('acme-inc/api')
23 | repo.raw_config = <<-YAML
24 | environments:
25 | production: {}
26 | staging: {}
27 | YAML
28 | environment = repo.environment('staging')
29 | expect(environment).to_not be_nil
30 | expect(environment.name).to eq 'staging'
31 | end
32 | end
33 | end
34 |
35 | describe '#default_environment' do
36 |
37 | before do
38 | @repo = Repository.with_name('acme-inc/api')
39 | @repo.configure! <<-YAML
40 | default_environment: production
41 | environments:
42 | production:
43 | aliases: [prod]
44 | stage:
45 | aliases: [staging]
46 | YAML
47 |
48 | @env_prod = Environment.new(name: 'production', repository: @repo)
49 | @env_stage = Environment.new(name: 'stage', repository: @repo)
50 | end
51 |
52 | context 'when not given a name' do
53 | it 'returns the default environment' do
54 | environment = @repo.environment
55 | expect(environment).to_not be_nil
56 | expect(environment.name).to eq "production"
57 | end
58 | end
59 |
60 | context 'when not given an alias' do
61 | it 'repo returns the correct environment' do
62 | environment = @repo.environment("prod")
63 | expect(environment).to_not be_nil
64 | expect(environment.name).to eq "production"
65 | end
66 | end
67 |
68 | context 'when default_environment set' do
69 | it 'repository and environment objects react correctly' do
70 | expect(@env_prod.is_default?).to eq true
71 | expect(@env_stage.is_default?).to eq false
72 | expect(@repo.default_environment.name).to eq "production"
73 | end
74 | end
75 | end
76 | end
77 |
--------------------------------------------------------------------------------
/spec/models/slack_team_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe SlackTeam, type: :model do
4 | end
5 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe User, type: :model do
4 | describe '#unlock_all!' do
5 |
6 | # slashdeploy/spec/fixtures/users.yml
7 | fixtures :users
8 | let(:user1) { users(:david) }
9 | let(:user2) { users(:steve) }
10 |
11 | it 'allows david to unlock both environments with unlock_all without messing with steve' do
12 | config = <<-yaml.strip_heredoc
13 | environments:
14 | stage: {}
15 | prod: {}
16 | yaml
17 |
18 | repo1 = Repository.with_name('acme-inc/api1')
19 | repo1.update_attributes! raw_config: config
20 | repo2 = Repository.with_name('acme-inc/api2')
21 | repo2.update_attributes! raw_config: config
22 |
23 | # david runs lock repo1 in stage environment.
24 | repo1_stage = repo1.environment('stage')
25 | expect { repo1_stage.lock! user1 }
26 | .to change { repo1_stage.locks.active.count }.from(0).to(1)
27 | .and change { user1.locks.active.count }.from(0).to(1)
28 |
29 | # david runs lock repo1 in prod environment.
30 | repo1_prod = repo1.environment('prod')
31 | expect { repo1_prod.lock! user1 }
32 | .to change { repo1_prod.locks.active.count }.from(0).to(1)
33 | .and change { user1.locks.active.count }.from(1).to(2)
34 |
35 | # david unlocks and then relocks repo1 in prod environment.
36 | repo1_prod.active_lock.unlock!()
37 | expect { repo1_prod.lock! user1 }
38 | .to change { repo1_prod.locks.active.count }.from(0).to(1)
39 | .and change { user1.locks.active.count }.from(1).to(2)
40 |
41 | # steve runs lock repo2 in stage environment.
42 | repo2_stage = repo2.environment('stage')
43 | expect { repo2_stage.lock! user2 }
44 | .to change { repo2_stage.locks.active.count }.from(0).to(1)
45 | .and change { user2.locks.active.count }.from(0).to(1)
46 |
47 | # david runs unlock_all!
48 | unlocked_locks = user1.unlock_all!
49 |
50 | # expect the unlock_all! method should return locks which were unlocked.
51 | expect(unlocked_locks.count).to eq(2)
52 | expect(unlocked_locks[0].repository.name).to eq("acme-inc/api1")
53 |
54 | # expect david to have no active locks.
55 | expect(user1.locks.active.count).to eq(0)
56 |
57 | # expect david to have 3 unactive locks.
58 | expect(user1.locks.count).to eq(3)
59 |
60 | # expect steve to have 1 active locks.
61 | expect(user2.locks.active.count).to eq(1)
62 |
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/spec/omniauth/strategies/jwt_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'omniauth/strategies/jwt'
3 |
4 | RSpec.describe OmniAuth::Strategies::JWT do
5 | include Rack::Test::Methods
6 |
7 | let(:secret) { 'abcd' }
8 | let(:backend) { -> (_env) { [200, {}, ['Hello World']] } }
9 | let(:app) { described_class.new backend, callback_path: '/auth/jwt/callback', secret: secret }
10 |
11 | before do
12 | allow(OmniAuth.config).to receive(:test_mode).and_return(false)
13 | end
14 |
15 | context 'callback phase' do
16 | before do
17 | env 'rack.session', ''
18 | end
19 |
20 | it 'raises an error when there is no JWT token' do
21 | get '/auth/jwt/callback'
22 | expect(last_request.env['omniauth.error']).to be_present
23 | end
24 |
25 | it 'raises an error when the JWT token is invalid' do
26 | get '/auth/jwt/callback', 'jwt': 'foo'
27 | expect(last_request.env['omniauth.error']).to be_present
28 | end
29 |
30 | it 'authenticates succesfully when the JWT token is valid' do
31 | claims = {
32 | id: 5,
33 | exp: 1.minutes.from_now.to_i,
34 | iat: Time.now.to_i
35 | }
36 | get '/auth/jwt/callback', 'jwt': JWT.encode(claims, secret)
37 | expect(last_request.env['omniauth.auth'].uid).to eq 5
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/perty/logger_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'stringio'
3 | require 'perty/logger'
4 |
5 | RSpec.describe Perty::Logger do
6 | it 'logs to a logger' do
7 | buf = StringIO.new
8 | logger = Perty::Logger.new(Logger.new(buf))
9 | logger.info 'foo'
10 | expect(buf.string).to eq "foo\n"
11 | end
12 |
13 | describe '#with_requet_id' do
14 | it 'prefixes the log lines with the request id' do
15 | buf = StringIO.new
16 | logger = Perty::Logger.new(Logger.new(buf))
17 | logger.with_request_id('id') { logger.info 'foo' }
18 | expect(buf.string).to eq "request_id=id foo\n"
19 | end
20 | end
21 |
22 | describe '#with_module' do
23 | it 'prefixes the log line with a module' do
24 | buf = StringIO.new
25 | logger = Perty::Logger.new(Logger.new(buf))
26 | logger.with_module('slack commands') { logger.info 'foo' }
27 | expect(buf.string).to eq "[slack commands] foo\n"
28 | end
29 |
30 | it 'allows for nested modules' do
31 | buf = StringIO.new
32 | logger = Perty::Logger.new(Logger.new(buf))
33 | logger.with_module('slack commands') do
34 | logger.with_module('DeployCommand') do
35 | logger.info 'foo'
36 | end
37 | logger.info 'bar'
38 | end
39 | expect(buf.string).to eq <<-MSG.strip_heredoc
40 | [slack commands] [DeployCommand] foo
41 | [slack commands] bar
42 | MSG
43 | end
44 | end
45 |
46 | describe 'with request id and module' do
47 | it 'puts the request id first' do
48 | buf = StringIO.new
49 | logger = Perty::Logger.new(Logger.new(buf))
50 | logger.with_module('slack commands') { logger.with_request_id('id') { logger.info 'foo' } }
51 | expect(buf.string).to eq "[slack commands] request_id=id foo\n"
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/spec/slack/client/faraday_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Slack::Client::Faraday do
4 | let(:client) { described_class.build }
5 |
6 | describe '#direct_message' do
7 | let(:slack_account) { stub_model(SlackAccount, id: 'U123BC2BD', bot_access_token: 'access_token') }
8 |
9 | describe 'when the message is properly formatted' do
10 | it 'posts a chat message' do
11 | stub_request(:post, 'https://slack.com/api/chat.postMessage')
12 | .with(body: { 'channel' => 'U123BC2BD', 'text' => 'Hello World', 'token' => 'access_token' })
13 | .to_return(status: 200, body: '{"ok":true}', headers: { 'Content-Type' => 'application/json' })
14 | message = Slack::Message.new text: 'Hello World'
15 | client.direct_message(slack_account, message)
16 | end
17 | end
18 |
19 | describe 'when the message has attachements' do
20 | it 'posts a chat message' do
21 | stub_request(:post, 'https://slack.com/api/chat.postMessage')
22 | .with(body: { 'attachments' => "[{\"mrkdwn_in\":[],\"text\":\"Hello World\",\"fields\":[],\"actions\":[]}]", 'channel' => 'U123BC2BD', 'text' => '', 'token' => 'access_token' })
23 | .to_return(status: 200, body: '{"ok":true}', headers: { 'Content-Type' => 'application/json' })
24 | message = Slack::Message.new attachments: [Slack::Attachment.new(text: 'Hello World')]
25 | client.direct_message(slack_account, message)
26 | end
27 | end
28 |
29 | describe 'when the message is malformed' do
30 | it 'raises an error' do
31 | stub_request(:post, 'https://slack.com/api/chat.postMessage')
32 | .with(body: { 'channel' => 'U123BC2BD', 'text' => 'Hello World', 'token' => 'access_token' })
33 | .to_return(status: 200, body: '{"ok":false,"error":"invalid_array_arg"}', headers: { 'Content-Type' => 'application/json' })
34 | message = Slack::Message.new text: 'Hello World'
35 | expect do
36 | client.direct_message(slack_account, message)
37 | end.to raise_error Slack::Client::Error, 'invalid_array_arg'
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/spec/slack/message_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Slack::Message do
4 | it 'builds messages' do
5 | m = Slack::Message.new
6 | m.text = 'Hello World'
7 | m.attachments = [
8 | Slack::Attachment.new(
9 | text: 'Some attachment'
10 | )
11 | ]
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/slash/middleware/verify_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Slash::Middleware::Verify do
4 | let(:handler) { instance_double(Slash::Handler) }
5 |
6 | describe '#call' do
7 | context 'when the token matches' do
8 | it 'calls the handler' do
9 | h = described_class.new handler, 'secret'
10 | env = { 'cmd' => Slash::Command.from_params('token' => 'secret'), 'type' => 'cmd' }
11 |
12 | expect(handler).to receive(:call).with(env)
13 | h.call(env)
14 | end
15 | end
16 |
17 | context 'when the token does not match' do
18 | it 'raises' do
19 | h = described_class.new handler, 'secret'
20 | env = { 'cmd' => Slash::Command.from_params('token' => 'l33thacks'), 'type' => 'cmd' }
21 |
22 | expect(handler).to_not receive(:call).with(env)
23 | expect { h.call(env) }.to raise_error Slash::UnverifiedError
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/spec/slash/rack_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Slash::Rack do
4 | let(:handler) { instance_double(Slash::Handler) }
5 | let(:app) { described_class.new handler }
6 |
7 | describe '#call' do
8 | context 'when a response is returned from the handler' do
9 | it 'returns the json representation of the response' do
10 | env = Rack::MockRequest.env_for(
11 | '/',
12 | method:
13 | 'POST',
14 | input: 'command=/deploy&text=thing&token=foo'
15 | )
16 | expect(handler).to receive(:call).with(
17 | 'cmd' => Slash::Command.new(
18 | Slash::CommandPayload.new(
19 | command: '/deploy',
20 | text: 'thing',
21 | token: 'foo'
22 | )
23 | ),
24 | 'type' => 'cmd'
25 | ).and_return(Slash.say(Slack::Message.new(text: 'Hello')))
26 | status, headers, body = app.call(env)
27 | expect(status).to eq 200
28 | expect(headers).to eq('Content-Type' => 'application/json')
29 | expect(body).to eq ['{"text":"Hello","attachments":[],"response_type":"in_channel"}']
30 | end
31 | end
32 |
33 | context 'when a response is not returned from the handler' do
34 | it 'returns an empty response' do
35 | env = Rack::MockRequest.env_for('/')
36 | expect(handler).to receive(:call).and_return(nil)
37 | status, headers, body = app.call(env)
38 | expect(status).to eq 200
39 | expect(headers).to eq({})
40 | expect(body).to eq ['']
41 | end
42 | end
43 |
44 | context 'when the handler raises a Slash::UnverifiedError' do
45 | it 'returns a 403' do
46 | env = Rack::MockRequest.env_for('/')
47 | expect(handler).to receive(:call).and_raise(Slash::UnverifiedError)
48 | status, headers, body = app.call(env)
49 | expect(status).to eq 403
50 | expect(headers).to eq({})
51 | expect(body).to eq ['']
52 | end
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/spec/slash/response_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Slash::Response do
4 | describe '#to_json' do
5 | it 'returns a suitable json response for an incoming webhook' do
6 | message = Slack::Message.new(text: 'Hello World')
7 |
8 | response = Slash::Response.new(message: message)
9 | expect(response.to_json).to eq '{"text":"Hello World","attachments":[]}'
10 |
11 | response = Slash::Response.new(message: message, in_channel: true)
12 | expect(response.to_json).to eq '{"text":"Hello World","attachments":[],"response_type":"in_channel"}'
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/slash/router_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | RSpec.describe Slash::Router do
4 | describe '#match' do
5 | it 'adds a route that matches the text against the regular expression' do
6 | help = instance_double(Slash::Handler)
7 | lock = instance_double(Slash::Handler)
8 |
9 | router = Slash::Router.new
10 | router.match Slash.match_regexp(/^help$/), help
11 | router.match Slash.match_regexp(/^lock (?