Compare commits
22 Commits
d1a0fc0222
...
2023-10-15
| Author | SHA1 | Date | |
|---|---|---|---|
| 5631ae386f | |||
| c31c556df5 | |||
| 5fae0c4fa9 | |||
| 144731062b | |||
| 72f3e03edd | |||
| b08693e866 | |||
| ae2360a480 | |||
| 213265a5d0 | |||
| dfb6b23e70 | |||
| c81a7a6160 | |||
| bbb710a5e1 | |||
| 7eaa8eacf8 | |||
| d78e361c29 | |||
| 1ae6c02472 | |||
| 11206854ad | |||
| 33089e6fbd | |||
| 5cec47efcc | |||
| cabb4daded | |||
| 1e61a76ded | |||
| ac3e9f14cd | |||
| 5ee9351362 | |||
| 2964e06639 |
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
gem "falcon", "~> 0.42.3"
|
|
||||||
|
|
||||||
gem "roda", "~> 3.72"
|
gem "roda", "~> 3.72"
|
||||||
|
|
||||||
@@ -27,3 +26,7 @@ group :test do
|
|||||||
gem "simplecov"
|
gem "simplecov"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
gem "puma", "~> 6.4"
|
||||||
|
|
||||||
|
gem "rackup", "~> 2.1"
|
||||||
|
|||||||
+9
-64
@@ -3,30 +3,7 @@ GEM
|
|||||||
specs:
|
specs:
|
||||||
addressable (2.8.5)
|
addressable (2.8.5)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
async (2.6.4)
|
|
||||||
console (~> 1.10)
|
|
||||||
fiber-annotation
|
|
||||||
io-event (~> 1.1)
|
|
||||||
timers (~> 4.1)
|
|
||||||
async-container (0.16.12)
|
|
||||||
async
|
|
||||||
async-io
|
|
||||||
async-http (0.60.2)
|
|
||||||
async (>= 1.25)
|
|
||||||
async-io (>= 1.28)
|
|
||||||
async-pool (>= 0.2)
|
|
||||||
protocol-http (~> 0.24.0)
|
|
||||||
protocol-http1 (~> 0.15.0)
|
|
||||||
protocol-http2 (~> 0.15.0)
|
|
||||||
traces (>= 0.10.0)
|
|
||||||
async-http-cache (0.4.3)
|
|
||||||
async-http (~> 0.56)
|
|
||||||
async-io (1.36.0)
|
|
||||||
async
|
|
||||||
async-pool (0.4.0)
|
|
||||||
async (>= 1.25)
|
|
||||||
bigdecimal (3.1.4)
|
bigdecimal (3.1.4)
|
||||||
build-environment (1.13.0)
|
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
capybara (3.39.2)
|
capybara (3.39.2)
|
||||||
addressable
|
addressable
|
||||||
@@ -37,9 +14,6 @@ GEM
|
|||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
regexp_parser (>= 1.5, < 3.0)
|
regexp_parser (>= 1.5, < 3.0)
|
||||||
xpath (~> 3.2)
|
xpath (~> 3.2)
|
||||||
console (1.23.2)
|
|
||||||
fiber-annotation
|
|
||||||
fiber-local
|
|
||||||
cucumber (9.0.2)
|
cucumber (9.0.2)
|
||||||
builder (~> 3.2, >= 3.2.4)
|
builder (~> 3.2, >= 3.2.4)
|
||||||
cucumber-ci-environment (~> 9.2, >= 9.2.0)
|
cucumber-ci-environment (~> 9.2, >= 9.2.0)
|
||||||
@@ -67,51 +41,25 @@ GEM
|
|||||||
diff-lcs (1.5.0)
|
diff-lcs (1.5.0)
|
||||||
docile (1.4.0)
|
docile (1.4.0)
|
||||||
erubi (1.12.0)
|
erubi (1.12.0)
|
||||||
falcon (0.42.3)
|
|
||||||
async
|
|
||||||
async-container (~> 0.16.0)
|
|
||||||
async-http (~> 0.57)
|
|
||||||
async-http-cache (~> 0.4.0)
|
|
||||||
async-io (~> 1.22)
|
|
||||||
build-environment (~> 1.13)
|
|
||||||
bundler
|
|
||||||
localhost (~> 1.1)
|
|
||||||
openssl (~> 3.0)
|
|
||||||
process-metrics (~> 0.2.0)
|
|
||||||
protocol-rack (~> 0.1)
|
|
||||||
samovar (~> 2.1)
|
|
||||||
ffi (1.16.2)
|
ffi (1.16.2)
|
||||||
fiber-annotation (0.2.0)
|
|
||||||
fiber-local (1.0.0)
|
|
||||||
io-event (1.3.2)
|
|
||||||
localhost (1.1.10)
|
|
||||||
mapping (1.1.1)
|
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.4)
|
mini_portile2 (2.8.4)
|
||||||
multi_test (1.1.0)
|
multi_test (1.1.0)
|
||||||
|
nio4r (2.5.9)
|
||||||
nokogiri (1.15.4)
|
nokogiri (1.15.4)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
openssl (3.2.0)
|
|
||||||
process-metrics (0.2.1)
|
|
||||||
console (~> 1.8)
|
|
||||||
samovar (~> 2.1)
|
|
||||||
protocol-hpack (1.4.2)
|
|
||||||
protocol-http (0.24.7)
|
|
||||||
protocol-http1 (0.15.1)
|
|
||||||
protocol-http (~> 0.22)
|
|
||||||
protocol-http2 (0.15.1)
|
|
||||||
protocol-hpack (~> 1.4)
|
|
||||||
protocol-http (~> 0.18)
|
|
||||||
protocol-rack (0.2.6)
|
|
||||||
protocol-http (~> 0.23)
|
|
||||||
rack (>= 1.0)
|
|
||||||
public_suffix (5.0.3)
|
public_suffix (5.0.3)
|
||||||
|
puma (6.4.0)
|
||||||
|
nio4r (~> 2.0)
|
||||||
racc (1.7.1)
|
racc (1.7.1)
|
||||||
rack (3.0.8)
|
rack (3.0.8)
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
|
rackup (2.1.0)
|
||||||
|
rack (>= 3)
|
||||||
|
webrick (~> 1.8)
|
||||||
regexp_parser (2.8.1)
|
regexp_parser (2.8.1)
|
||||||
rexml (3.2.6)
|
rexml (3.2.6)
|
||||||
roda (3.72.0)
|
roda (3.72.0)
|
||||||
@@ -130,9 +78,6 @@ GEM
|
|||||||
rspec-support (~> 3.12.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-support (3.12.1)
|
rspec-support (3.12.1)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
samovar (2.2.0)
|
|
||||||
console (~> 1.0)
|
|
||||||
mapping (~> 1.0)
|
|
||||||
selenium-webdriver (4.13.1)
|
selenium-webdriver (4.13.1)
|
||||||
rexml (~> 3.2, >= 3.2.5)
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
rubyzip (>= 1.2.2, < 3.0)
|
rubyzip (>= 1.2.2, < 3.0)
|
||||||
@@ -150,8 +95,7 @@ GEM
|
|||||||
sys-uname (1.2.3)
|
sys-uname (1.2.3)
|
||||||
ffi (~> 1.1)
|
ffi (~> 1.1)
|
||||||
tilt (2.3.0)
|
tilt (2.3.0)
|
||||||
timers (4.3.5)
|
webrick (1.8.1)
|
||||||
traces (0.11.1)
|
|
||||||
websocket (1.2.10)
|
websocket (1.2.10)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
@@ -163,7 +107,8 @@ DEPENDENCIES
|
|||||||
capybara (~> 3.39)
|
capybara (~> 3.39)
|
||||||
cucumber (~> 9.0)
|
cucumber (~> 9.0)
|
||||||
erubi (~> 1.12)
|
erubi (~> 1.12)
|
||||||
falcon (~> 0.42.3)
|
puma (~> 6.4)
|
||||||
|
rackup (~> 2.1)
|
||||||
roda (~> 3.72)
|
roda (~> 3.72)
|
||||||
rspec (~> 3.12)
|
rspec (~> 3.12)
|
||||||
selenium-webdriver (~> 4.13)
|
selenium-webdriver (~> 4.13)
|
||||||
|
|||||||
Vendored
+4
-4
@@ -26,10 +26,10 @@ pipeline {
|
|||||||
stage('Run tests') {
|
stage('Run tests') {
|
||||||
steps {
|
steps {
|
||||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||||
sh 'cucumber features --format html --out coverage/cucumber.html'
|
sh 'cucumber features --format html --out cucumber.html'
|
||||||
}
|
}
|
||||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||||
sh 'rspec spec --format html --out coverage/spec.html'
|
sh 'rspec spec --format html --out spec.html'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,8 +40,8 @@ pipeline {
|
|||||||
allowMissing: false,
|
allowMissing: false,
|
||||||
alwaysLinkToLastBuild: false,
|
alwaysLinkToLastBuild: false,
|
||||||
keepAll: true,
|
keepAll: true,
|
||||||
reportDir: 'coverage',
|
reportDir: '.',
|
||||||
reportFiles: 'cucumber.html, spec.html, index.html',
|
reportFiles: 'cucumber.html, spec.html, coverage/index.html',
|
||||||
reportName: 'Test Results',
|
reportName: 'Test Results',
|
||||||
reportTitles: 'Cucumber Results, RSpec Results, Test Coverage'])
|
reportTitles: 'Cucumber Results, RSpec Results, Test Coverage'])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ require 'roda'
|
|||||||
require 'securerandom'
|
require 'securerandom'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'sequel'
|
require 'sequel'
|
||||||
|
require 'open-uri'
|
||||||
|
|
||||||
class App < Roda
|
class App < Roda
|
||||||
plugin :sessions, secret: ENV.delete('APP_SESSION_SECRET')
|
plugin :sessions, secret: ENV.delete('APP_SESSION_SECRET')
|
||||||
@@ -33,6 +34,20 @@ class App < Roda
|
|||||||
flash['message'] = "Please enter a valid URL";
|
flash['message'] = "Please enter a valid URL";
|
||||||
r.redirect '/'
|
r.redirect '/'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
OpenURI.open_uri(url)
|
||||||
|
rescue URI::BadURIError
|
||||||
|
flash['message'] = "Invalid URL"
|
||||||
|
r.redirect '/'
|
||||||
|
rescue OpenURI::HTTPError
|
||||||
|
flash['message'] = "URL not found"
|
||||||
|
r.redirect '/'
|
||||||
|
rescue SocketError => e
|
||||||
|
flash['message'] = "URL does not resolve"
|
||||||
|
r.redirect '/'
|
||||||
|
end
|
||||||
|
|
||||||
if links.filter(:url => url).first.nil?
|
if links.filter(:url => url).first.nil?
|
||||||
code = SecureRandom.urlsafe_base64 4
|
code = SecureRandom.urlsafe_base64 4
|
||||||
links.insert(url: url, code: code)
|
links.insert(url: url, code: code)
|
||||||
@@ -41,10 +56,48 @@ class App < Roda
|
|||||||
code = links.filter(:url => url).first[:code]
|
code = links.filter(:url => url).first[:code]
|
||||||
@message ||= "Link exists"
|
@message ||= "Link exists"
|
||||||
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + code
|
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + code
|
||||||
if 'application/json' == r.headers['CONTENT_TYPE']
|
|
||||||
return {url: url, code: code, link: @new_link}.to_json
|
|
||||||
end
|
|
||||||
view :create
|
view :create
|
||||||
end
|
end
|
||||||
|
r.on "links" do
|
||||||
|
r.post do
|
||||||
|
if 'application/json' != r.headers['CONTENT_TYPE']
|
||||||
|
response.status = 400
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
if url.empty?
|
||||||
|
response.status = 400
|
||||||
|
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
|
||||||
|
rescue OpenURI::HTTPError
|
||||||
|
response.status = 400
|
||||||
|
return {message: "url not found"}.to_json
|
||||||
|
rescue SocketError => e
|
||||||
|
response.status = 400
|
||||||
|
return {message: "url does not resolve"}.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
if links.filter(:url => url).first.nil?
|
||||||
|
code = SecureRandom.urlsafe_base64 4
|
||||||
|
links.insert(url: url, code: 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
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ Then('I should be redirected to {string}') do |string|
|
|||||||
end
|
end
|
||||||
|
|
||||||
Then('The status code should be {int}') do |code|
|
Then('The status code should be {int}') do |code|
|
||||||
puts page.status_code.should eq(code)
|
page.status_code.should eq(code)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,3 +26,27 @@ Feature: Submit
|
|||||||
And I click the "Submit" button
|
And I click the "Submit" button
|
||||||
Then I should be on "/create" page
|
Then I should be on "/create" page
|
||||||
And I should see the message "Link exists"
|
And I should see the message "Link exists"
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Submitting the form with a URL that is 404
|
||||||
|
Given I visit the "/" page
|
||||||
|
When I type "http://google.com/example" in the "url" field
|
||||||
|
And I click the "Submit" button
|
||||||
|
Then I should be on "/" page
|
||||||
|
And I should see the message "URL not found"
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Submitting the form with a URL that does not resolve
|
||||||
|
Given I visit the "/" page
|
||||||
|
When I type "http://bad.tld" in the "url" field
|
||||||
|
And I click the "Submit" button
|
||||||
|
Then I should be on "/" page
|
||||||
|
And I should see the message "URL does not resolve"
|
||||||
|
|
||||||
|
@db-test
|
||||||
|
Scenario: Submitting the form with an invalid URL
|
||||||
|
Given I visit the "/" page
|
||||||
|
When I type "not-an-url" in the "url" field
|
||||||
|
And I click the "Submit" button
|
||||||
|
Then I should be on "/" page
|
||||||
|
And I should see the message "Invalid URL"
|
||||||
|
|||||||
+74
-1
@@ -1,3 +1,6 @@
|
|||||||
|
require 'simplecov'
|
||||||
|
SimpleCov.start
|
||||||
|
|
||||||
require_relative '../.env'
|
require_relative '../.env'
|
||||||
ENV["DB_NAME"] = "test_#{ENV["DB_NAME"]}"
|
ENV["DB_NAME"] = "test_#{ENV["DB_NAME"]}"
|
||||||
require_relative '../app'
|
require_relative '../app'
|
||||||
@@ -7,13 +10,25 @@ require 'sequel'
|
|||||||
require 'rspec'
|
require 'rspec'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
|
# DB initialization
|
||||||
|
Sequel.extension :migration
|
||||||
|
Sequel.sqlite("db/#{ENV['DB_NAME']}") do |db|
|
||||||
|
Sequel::Migrator.apply(db, "db/migrations")
|
||||||
|
end
|
||||||
|
|
||||||
def app
|
def app
|
||||||
App
|
App
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Submit API request to create new link" do
|
describe "Submit API request to create new link" do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
it "should return link data in json format when a valid url is entered" do
|
before :each do
|
||||||
|
@links = Sequel.sqlite("db/#{ENV['DB_NAME']}")[:links]
|
||||||
|
end
|
||||||
|
after :each do
|
||||||
|
@links.delete
|
||||||
|
end
|
||||||
|
it "should return link data in json format when a valid url is submitted" do
|
||||||
data = {
|
data = {
|
||||||
url: 'http://google.com'
|
url: 'http://google.com'
|
||||||
}
|
}
|
||||||
@@ -24,5 +39,63 @@ describe "Submit API request to create new link" do
|
|||||||
expect(response_json['code']).not_to eq(nil)
|
expect(response_json['code']).not_to eq(nil)
|
||||||
expect(response_json['link']).to include(response_json['code'])
|
expect(response_json['link']).to include(response_json['code'])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'invalid url parameter' message when an empty url is submitted" do
|
||||||
|
data = {
|
||||||
|
url: ''
|
||||||
|
}
|
||||||
|
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
|
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")
|
||||||
|
expect(last_response.status).to eq(400)
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['message']).to eq('missing url parameter')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'invalid url parameter' message when an invalid url is submitted" do
|
||||||
|
data = {
|
||||||
|
url: 'not-an-url'
|
||||||
|
}
|
||||||
|
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 'url not found' message when a 404 url is submitted" do
|
||||||
|
data = {
|
||||||
|
url: 'http://google.com/example'
|
||||||
|
}
|
||||||
|
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')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return with a 400 status and 'url not found' message when a URL with no DNS is submitted" do
|
||||||
|
data = {
|
||||||
|
url: 'http://bad.tld'
|
||||||
|
}
|
||||||
|
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
|
||||||
|
data = {
|
||||||
|
url: 'http://google.com'
|
||||||
|
}
|
||||||
|
post('/links', data.to_json)
|
||||||
|
expect(last_response.status).to eq(400)
|
||||||
|
response_json = JSON.parse(last_response.body)
|
||||||
|
expect(response_json['message']).to eq('not a valid json request')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user