Merge pull request 'rubocop' (#29) from rubocop into master
Reviewed-on: #29 closes bucky/url-shortener#28pull/31/head
commit
c29d53a33b
|
|
@ -0,0 +1,6 @@
|
|||
AllCops:
|
||||
NewCops: enable
|
||||
SuggestExtensions: false
|
||||
|
||||
Metrics/BlockLength:
|
||||
AllowedMethods: ['describe', 'context', 'route', 'r.on', 'r.post']
|
||||
29
Gemfile
29
Gemfile
|
|
@ -1,32 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'roda', '~> 3.72'
|
||||
|
||||
gem "roda", "~> 3.72"
|
||||
gem 'sequel', '~> 5.72'
|
||||
|
||||
gem "sequel", "~> 5.72"
|
||||
gem 'tilt', '~> 2.2'
|
||||
|
||||
gem "tilt", "~> 2.2"
|
||||
gem 'sqlite3', '~> 1.6'
|
||||
|
||||
gem "sqlite3", "~> 1.6"
|
||||
|
||||
gem "erubi", "~> 1.12"
|
||||
gem 'erubi', '~> 1.12'
|
||||
|
||||
group :test do
|
||||
gem 'cucumber', '~> 9.0'
|
||||
|
||||
gem "cucumber", "~> 9.0"
|
||||
gem 'capybara', '~> 3.39'
|
||||
|
||||
gem "capybara", "~> 3.39"
|
||||
gem 'rspec', '~> 3.12'
|
||||
|
||||
gem "rspec", "~> 3.12"
|
||||
gem 'selenium-webdriver', '~> 4.13'
|
||||
|
||||
gem "selenium-webdriver", "~> 4.13"
|
||||
|
||||
gem "simplecov"
|
||||
gem 'simplecov'
|
||||
|
||||
gem 'rubocop', require: false
|
||||
end
|
||||
|
||||
gem "puma", "~> 6.4"
|
||||
gem 'puma', '~> 6.4'
|
||||
|
||||
gem "rackup", "~> 2.1"
|
||||
gem 'rackup', '~> 2.1'
|
||||
|
|
|
|||
26
Gemfile.lock
26
Gemfile.lock
|
|
@ -3,6 +3,8 @@ GEM
|
|||
specs:
|
||||
addressable (2.8.5)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
ast (2.4.2)
|
||||
base64 (0.1.1)
|
||||
bigdecimal (3.1.4)
|
||||
builder (3.2.4)
|
||||
capybara (3.39.2)
|
||||
|
|
@ -42,6 +44,8 @@ GEM
|
|||
docile (1.4.0)
|
||||
erubi (1.12.0)
|
||||
ffi (1.16.2)
|
||||
json (2.6.3)
|
||||
language_server-protocol (3.17.0.3)
|
||||
matrix (0.4.2)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.4)
|
||||
|
|
@ -50,6 +54,10 @@ GEM
|
|||
nokogiri (1.15.4)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
parallel (1.23.0)
|
||||
parser (3.2.2.4)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
public_suffix (5.0.3)
|
||||
puma (6.4.0)
|
||||
nio4r (~> 2.0)
|
||||
|
|
@ -60,6 +68,7 @@ GEM
|
|||
rackup (2.1.0)
|
||||
rack (>= 3)
|
||||
webrick (~> 1.8)
|
||||
rainbow (3.1.1)
|
||||
regexp_parser (2.8.1)
|
||||
rexml (3.2.6)
|
||||
roda (3.72.0)
|
||||
|
|
@ -77,6 +86,21 @@ GEM
|
|||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-support (3.12.1)
|
||||
rubocop (1.57.1)
|
||||
base64 (~> 0.1.1)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.2.2.4)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.28.1, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.29.0)
|
||||
parser (>= 3.2.1.0)
|
||||
ruby-progressbar (1.13.0)
|
||||
rubyzip (2.3.2)
|
||||
selenium-webdriver (4.13.1)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
|
|
@ -95,6 +119,7 @@ GEM
|
|||
sys-uname (1.2.3)
|
||||
ffi (~> 1.1)
|
||||
tilt (2.3.0)
|
||||
unicode-display_width (2.5.0)
|
||||
webrick (1.8.1)
|
||||
websocket (1.2.10)
|
||||
xpath (3.2.0)
|
||||
|
|
@ -111,6 +136,7 @@ DEPENDENCIES
|
|||
rackup (~> 2.1)
|
||||
roda (~> 3.72)
|
||||
rspec (~> 3.12)
|
||||
rubocop
|
||||
selenium-webdriver (~> 4.13)
|
||||
sequel (~> 5.72)
|
||||
simplecov
|
||||
|
|
|
|||
|
|
@ -9,24 +9,34 @@ pipeline {
|
|||
steps {
|
||||
sh ''' #!/usr/local/bin/bash
|
||||
rbenv local 3.2.2
|
||||
echo "ENV[\\\"APP_SESSION_SECRET\\\"] ||= $(ruby -rsecurerandom -e 'puts SecureRandom.base64(64).inspect()')" > .env.rb
|
||||
echo "ENV[\\\"DB_NAME\\\"] ||= \\\"${DB_NAME}\\\"" >> .env.rb
|
||||
echo "# frozen_string_literal: true\n" > .env.rb
|
||||
echo "ENV['APP_SESSION_SECRET'] ||= '$(ruby -rsecurerandom -e 'puts SecureRandom.base64(64)')'" >> .env.rb
|
||||
echo "ENV['DB_NAME'] ||= '${DB_NAME}'" >> .env.rb
|
||||
cat .env.rb
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Build dependencies') {
|
||||
steps {
|
||||
sh 'bundle config set --local path "vendor"'
|
||||
sh 'bundle install'
|
||||
sh 'sequel -m db/migrations sqlite://db/${DB_NAME}'
|
||||
}
|
||||
}
|
||||
stage('Code Linting') {
|
||||
steps {
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
sh 'bundle exec rubocop --format html --out rubocop.html'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Run tests') {
|
||||
steps {
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
sh 'cucumber features --format html --out cucumber.html'
|
||||
sh 'bundle exec cucumber features --format html --out cucumber.html'
|
||||
}
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
sh 'rspec spec --format html --out spec.html'
|
||||
sh 'bundle exec rspec spec --format html --out spec.html'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,9 +47,9 @@ pipeline {
|
|||
alwaysLinkToLastBuild: false,
|
||||
keepAll: true,
|
||||
reportDir: '.',
|
||||
reportFiles: 'cucumber.html, spec.html, coverage/index.html',
|
||||
reportFiles: 'rubocop.html, cucumber.html, spec.html, coverage/index.html',
|
||||
reportName: 'Test Results',
|
||||
reportTitles: 'Cucumber Results, RSpec Results, Test Coverage'])
|
||||
reportTitles: 'Rubocop Results, Cucumber Results, RSpec Results, Test Coverage'])
|
||||
}
|
||||
}
|
||||
stage('Build production deliverable') {
|
||||
|
|
@ -53,7 +63,7 @@ pipeline {
|
|||
cp .env.rb /tmp/url-shortener/
|
||||
cp .ruby-version /tmp/url-shortener/
|
||||
cd /tmp/url-shortener
|
||||
rm -rf features spec coverage db/*.db .git* Jenkinsfile *.html
|
||||
rm -rf features spec coverage db/*.db .git* Jenkinsfile *.html .rubocop.yml
|
||||
cd /tmp
|
||||
tar -czvf $ZIP_FILE url-shortener/
|
||||
mv /tmp/$ZIP_FILE $CUR_DIR/
|
||||
|
|
|
|||
63
app.rb
63
app.rb
|
|
@ -1,9 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'roda'
|
||||
require 'securerandom'
|
||||
require 'json'
|
||||
require 'sequel'
|
||||
require 'open-uri'
|
||||
|
||||
# URL Shortener App class
|
||||
class App < Roda
|
||||
plugin :sessions, secret: ENV.delete('APP_SESSION_SECRET')
|
||||
plugin :render, escape: true
|
||||
|
|
@ -11,92 +14,92 @@ class App < Roda
|
|||
plugin :json_parser
|
||||
plugin :request_headers
|
||||
|
||||
DB = Sequel.sqlite("db/#{ENV['DB_NAME']}")
|
||||
DB = Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}")
|
||||
links = DB[:links]
|
||||
|
||||
route do |r|
|
||||
r.root do
|
||||
@message = flash['message'] || "Enter a URL"
|
||||
@message = flash['message'] || 'Enter a URL'
|
||||
view :home
|
||||
end
|
||||
|
||||
r.get String do |url_code|
|
||||
link = links.filter(:code => url_code)
|
||||
link = links.filter(code: url_code)
|
||||
r.redirect link.first[:url] unless link.first.nil?
|
||||
@message = "Link #{url_code} doesn't exist"
|
||||
response.status = 404
|
||||
view :home
|
||||
end
|
||||
|
||||
r.post "create" do
|
||||
r.post 'create' do
|
||||
url = r.params['url']
|
||||
if url.nil? or url.empty?
|
||||
flash['message'] = "Please enter a valid URL";
|
||||
if url.nil? || url.empty?
|
||||
flash['message'] = 'Please enter a valid URL'
|
||||
r.redirect '/'
|
||||
end
|
||||
|
||||
begin
|
||||
OpenURI.open_uri(url)
|
||||
rescue URI::BadURIError
|
||||
flash['message'] = "Invalid URL"
|
||||
flash['message'] = 'Invalid URL'
|
||||
r.redirect '/'
|
||||
rescue OpenURI::HTTPError
|
||||
flash['message'] = "URL not found"
|
||||
flash['message'] = 'URL not found'
|
||||
r.redirect '/'
|
||||
rescue SocketError => e
|
||||
flash['message'] = "URL does not resolve"
|
||||
rescue SocketError
|
||||
flash['message'] = 'URL does not resolve'
|
||||
r.redirect '/'
|
||||
end
|
||||
|
||||
if links.filter(:url => url).first.nil?
|
||||
if links.filter(url:).first.nil?
|
||||
code = SecureRandom.urlsafe_base64 4
|
||||
links.insert(url: url, code: code)
|
||||
@message = "Link created"
|
||||
links.insert(url:, code:)
|
||||
@message = 'Link created'
|
||||
end
|
||||
code = links.filter(:url => url).first[:code]
|
||||
@message ||= "Link exists"
|
||||
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + code
|
||||
code = links.filter(url:).first[:code]
|
||||
@message ||= 'Link exists'
|
||||
@new_link = "http://#{request.env['HTTP_HOST']}/#{code}"
|
||||
view :create
|
||||
end
|
||||
r.on "links" do
|
||||
r.on 'links' do
|
||||
r.post do
|
||||
if 'application/json' != r.headers['CONTENT_TYPE']
|
||||
if r.headers['CONTENT_TYPE'] != 'application/json'
|
||||
response.status = 400
|
||||
return {message: "not a valid json request"}.to_json
|
||||
return { message: 'not a valid json request' }.to_json
|
||||
end
|
||||
|
||||
url = r.params['url']
|
||||
if url.nil?
|
||||
response.status = 400
|
||||
return {message: "missing url parameter"}.to_json
|
||||
return { message: 'missing url parameter' }.to_json
|
||||
end
|
||||
|
||||
if url.empty?
|
||||
response.status = 400
|
||||
return {message: "invalid url parameter"}.to_json
|
||||
return { message: 'invalid url parameter' }.to_json
|
||||
end
|
||||
|
||||
begin
|
||||
OpenURI.open_uri(url)
|
||||
rescue URI::BadURIError
|
||||
response.status = 400
|
||||
return {message: "invalid url parameter"}.to_json
|
||||
return { message: 'invalid url parameter' }.to_json
|
||||
rescue OpenURI::HTTPError
|
||||
response.status = 400
|
||||
return {message: "url not found"}.to_json
|
||||
rescue SocketError => e
|
||||
return { message: 'url not found' }.to_json
|
||||
rescue SocketError
|
||||
response.status = 400
|
||||
return {message: "url does not resolve"}.to_json
|
||||
return { message: 'url does not resolve' }.to_json
|
||||
end
|
||||
|
||||
if links.filter(:url => url).first.nil?
|
||||
if links.filter(url:).first.nil?
|
||||
code = SecureRandom.urlsafe_base64 4
|
||||
links.insert(url: url, code: code)
|
||||
links.insert(url:, code:)
|
||||
end
|
||||
|
||||
code = links.filter(:url => url).first[:code]
|
||||
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + code
|
||||
return {url: url, code: code, link: @new_link}.to_json
|
||||
code = links.filter(url:).first[:code]
|
||||
@new_link = "http://#{request.env['HTTP_HOST']}/#{code}"
|
||||
return { url:, code:, link: @new_link }.to_json
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Sequel.migration do
|
||||
change do
|
||||
create_table :links do
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# BEFORE
|
||||
Before('@db-test') do
|
||||
@links = Sequel.sqlite("db/#{ENV['DB_NAME']}")[:links]
|
||||
@links = Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}")[:links]
|
||||
end
|
||||
|
||||
|
||||
# GIVEN
|
||||
|
||||
Given('I visit the {string} page') do |string|
|
||||
|
|
@ -11,14 +12,13 @@ Given('I visit the {string} page') do |string|
|
|||
end
|
||||
|
||||
Given('A link already exists with the url {string}') do |string|
|
||||
@links.insert(url: string, code: "aaaaaa")
|
||||
@links.insert(url: string, code: 'aaaaaa')
|
||||
end
|
||||
|
||||
Given('A link already exists with the url {string} and code {string}') do |url, code|
|
||||
@links.insert(url: url, code: code)
|
||||
@links.insert(url:, code:)
|
||||
end
|
||||
|
||||
|
||||
# WHEN
|
||||
|
||||
When('I click the {string} button') do |string|
|
||||
|
|
@ -33,7 +33,6 @@ When('I visit the {string} location') do |string|
|
|||
visit string
|
||||
end
|
||||
|
||||
|
||||
# THEN
|
||||
|
||||
Then('I should see text {string}') do |string|
|
||||
|
|
@ -66,7 +65,6 @@ Then('The status code should be {int}') do |code|
|
|||
page.status_code.should eq(code)
|
||||
end
|
||||
|
||||
|
||||
# AFTER
|
||||
|
||||
After('@db-test') do
|
||||
|
|
|
|||
|
|
@ -1,25 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'simplecov'
|
||||
SimpleCov.start
|
||||
|
||||
require_relative '../../.env'
|
||||
ENV["DB_NAME"] = "test_#{ENV["DB_NAME"]}"
|
||||
ENV['DB_NAME'] = "test_#{ENV.fetch('DB_NAME', nil)}"
|
||||
require_relative '../../app'
|
||||
require 'rubygems'
|
||||
require 'roda'
|
||||
require 'sequel'
|
||||
require 'capybara'
|
||||
require 'capybara/dsl'
|
||||
require 'capybara/cucumber'
|
||||
require 'rspec'
|
||||
###
|
||||
require 'rspec/expectations'
|
||||
require 'rspec/matchers'
|
||||
|
||||
# DB initialization
|
||||
Sequel.extension :migration
|
||||
Sequel.sqlite("db/#{ENV['DB_NAME']}") do |db|
|
||||
Sequel::Migrator.apply(db, "db/migrations")
|
||||
Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}") do |db|
|
||||
Sequel::Migrator.apply(db, 'db/migrations')
|
||||
end
|
||||
|
||||
# attach app to Capybara
|
||||
Capybara.app = App
|
||||
|
||||
include Capybara::DSL
|
||||
include RSpec::Expectations
|
||||
include RSpec::Matchers
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'simplecov'
|
||||
SimpleCov.start
|
||||
|
||||
require_relative '../.env'
|
||||
ENV["DB_NAME"] = "test_#{ENV["DB_NAME"]}"
|
||||
ENV['DB_NAME'] = "test_#{ENV.fetch('DB_NAME', nil)}"
|
||||
require_relative '../app'
|
||||
require 'rubygems'
|
||||
require 'roda'
|
||||
|
|
@ -12,27 +14,27 @@ require 'rack/test'
|
|||
|
||||
# DB initialization
|
||||
Sequel.extension :migration
|
||||
Sequel.sqlite("db/#{ENV['DB_NAME']}") do |db|
|
||||
Sequel::Migrator.apply(db, "db/migrations")
|
||||
Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}") do |db|
|
||||
Sequel::Migrator.apply(db, 'db/migrations')
|
||||
end
|
||||
|
||||
def app
|
||||
App
|
||||
end
|
||||
|
||||
describe "Submit API request to create new link" do
|
||||
describe 'Submit API request to create new link' do
|
||||
include Rack::Test::Methods
|
||||
before :each do
|
||||
@links = Sequel.sqlite("db/#{ENV['DB_NAME']}")[:links]
|
||||
@links = Sequel.sqlite("db/#{ENV.fetch('DB_NAME', nil)}")[:links]
|
||||
end
|
||||
after :each do
|
||||
@links.delete
|
||||
end
|
||||
it "should return link data in json format when a valid url is submitted" do
|
||||
it 'should return link data in json format when a valid url is submitted' do
|
||||
data = {
|
||||
url: 'http://google.com'
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||
expect(last_response).to be_ok
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['url']).to eq(data[:url])
|
||||
|
|
@ -44,16 +46,15 @@ describe "Submit API request to create new link" do
|
|||
data = {
|
||||
url: ''
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('invalid url parameter')
|
||||
end
|
||||
|
||||
it "should return with a 400 status and 'missing url parameter' message when an empty url is submitted" do
|
||||
data = {
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
data = {}
|
||||
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('missing url parameter')
|
||||
|
|
@ -63,7 +64,7 @@ describe "Submit API request to create new link" do
|
|||
data = {
|
||||
url: 'not-an-url'
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('invalid url parameter')
|
||||
|
|
@ -73,7 +74,7 @@ describe "Submit API request to create new link" do
|
|||
data = {
|
||||
url: 'http://google.com/example'
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('url not found')
|
||||
|
|
@ -83,13 +84,14 @@ describe "Submit API request to create new link" do
|
|||
data = {
|
||||
url: 'http://bad.tld'
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
post('/links', data.to_json, 'CONTENT_TYPE' => 'application/json')
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('url does not resolve')
|
||||
end
|
||||
|
||||
it "should return with a 400 status and 'not a valid json request' message when a request is made with the wrong content type header" do
|
||||
it "should return with a 400 status and 'not a valid json request'
|
||||
message when a request is made with the wrong content type header" do
|
||||
data = {
|
||||
url: 'http://google.com'
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue