Update gems, modernize test stack, fix flaky tests

This commit is contained in:
Kyle Drake 2019-09-21 01:19:47 -07:00
parent e223b687b2
commit 69c32d17ed
14 changed files with 583 additions and 578 deletions

View file

@ -71,13 +71,14 @@ group :test do
gem 'mocha', require: nil gem 'mocha', require: nil
gem 'rake', require: nil gem 'rake', require: nil
gem 'poltergeist' gem 'poltergeist'
gem 'capybara_minitest_spec'
gem 'capybara', require: nil #, '2.10.1', require: nil gem 'capybara', require: nil #, '2.10.1', require: nil
gem 'rack_session_access', require: nil gem 'rack_session_access', require: nil
gem 'webmock', '3.5.1', require: nil gem 'webmock', '3.5.1', require: nil
gem 'stripe-ruby-mock', '2.5.6', require: 'stripe_mock' gem 'stripe-ruby-mock', '2.5.8', require: 'stripe_mock'
gem 'timecop' gem 'timecop'
gem 'mock_redis' gem 'mock_redis'
gem 'simplecov', require: nil gem 'simplecov', require: nil
gem 'm' gem 'm'
gem 'apparition'
gem 'poltergeist', require: nil
end end

View file

@ -15,21 +15,25 @@ GEM
specs: specs:
acme-client (0.6.3) acme-client (0.6.3)
faraday (~> 0.9, >= 0.9.1) faraday (~> 0.9, >= 0.9.1)
activesupport (5.2.3) activesupport (6.0.0)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
minitest (~> 5.1) minitest (~> 5.1)
tzinfo (~> 1.1) tzinfo (~> 1.1)
addressable (2.6.0) zeitwerk (~> 2.1, >= 2.1.8)
public_suffix (>= 2.0.2, < 4.0) addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
annoy (0.5.6) annoy (0.5.6)
highline (>= 1.5.0) highline (>= 1.5.0)
ansi (1.5.0) ansi (1.5.0)
apparition (0.4.0)
capybara (~> 3.13, < 4)
websocket-driver (>= 0.6.5)
base32 (0.3.2) base32 (0.3.2)
bcrypt (3.1.13) bcrypt (3.1.13)
builder (3.2.3) builder (3.2.3)
byebug (11.0.1) byebug (11.0.1)
capybara (3.25.0) capybara (3.29.0)
addressable addressable
mini_mime (>= 0.1.3) mini_mime (>= 0.1.3)
nokogiri (~> 1.8) nokogiri (~> 1.8)
@ -37,9 +41,6 @@ GEM
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
regexp_parser (~> 1.5) regexp_parser (~> 1.5)
xpath (~> 3.2) xpath (~> 3.2)
capybara_minitest_spec (1.0.7)
capybara (>= 2)
minitest (>= 4)
certified (1.0.0) certified (1.0.0)
climate_control (0.2.0) climate_control (0.2.0)
cliver (0.3.2) cliver (0.3.2)
@ -57,21 +58,15 @@ GEM
crass (1.0.4) crass (1.0.4)
dante (0.2.0) dante (0.2.0)
docile (1.3.2) docile (1.3.2)
domain_name (0.5.20180417) domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
drydock (0.6.9) drydock (0.6.9)
equatable (0.6.1)
erubis (2.7.0) erubis (2.7.0)
exifr (1.3.6) exifr (1.3.6)
fabrication (2.20.2) fabrication (2.20.2)
facter (2.5.1) facter (2.5.6)
faker (1.9.4) faker (2.4.0)
i18n (>= 0.7) i18n (~> 1.6.0)
pastel (~> 0.7.2)
thor (~> 0.20.0)
tty-pager (~> 0.12.0)
tty-screen (~> 0.6.5)
tty-tree (~> 0.3.0)
faraday (0.15.4) faraday (0.15.4)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
faraday_middleware (0.13.1) faraday_middleware (0.13.1)
@ -83,12 +78,12 @@ GEM
sax-machine (>= 1.0) sax-machine (>= 1.0)
ffi (1.11.1) ffi (1.11.1)
filesize (0.2.0) filesize (0.2.0)
fspath (3.1.1) fspath (3.1.2)
gandi (3.3.28) gandi (3.3.28)
hashie hashie
xmlrpc xmlrpc
geoip (1.6.4) geoip (1.6.4)
hashdiff (0.4.0) hashdiff (1.0.0)
hashie (3.6.0) hashie (3.6.0)
highline (2.0.2) highline (2.0.2)
hiredis (0.6.3) hiredis (0.6.3)
@ -100,23 +95,24 @@ GEM
http-cookie (~> 1.0) http-cookie (~> 1.0)
http-form_data (~> 2.0) http-form_data (~> 2.0)
http_parser.rb (~> 0.6.0) http_parser.rb (~> 0.6.0)
http-accept (1.7.0)
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
http-form_data (2.1.1) http-form_data (2.1.1)
http_parser.rb (0.6.0) http_parser.rb (0.6.0)
i18n (1.6.0) i18n (1.6.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
image_optim (0.26.4) image_optim (0.26.5)
exifr (~> 1.2, >= 1.2.2) exifr (~> 1.2, >= 1.2.2)
fspath (~> 3.0) fspath (~> 3.0)
image_size (>= 1.5, < 3) image_size (>= 1.5, < 3)
in_threads (~> 1.3) in_threads (~> 1.3)
progress (~> 3.0, >= 3.0.1) progress (~> 3.0, >= 3.0.1)
image_optim_pack (0.5.3) image_optim_pack (0.6.0)
fspath (>= 2.1, < 4) fspath (>= 2.1, < 4)
image_optim (~> 0.19) image_optim (~> 0.19)
image_size (2.0.1) image_size (2.0.2)
in_threads (1.5.2) in_threads (1.5.3)
io-extra (1.3.0) io-extra (1.3.0)
ipaddress (0.8.3) ipaddress (0.8.3)
json (2.2.0) json (2.2.0)
@ -132,13 +128,13 @@ GEM
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
metaclass (0.0.4) metaclass (0.0.4)
method_source (0.9.2) method_source (0.9.2)
mime-types (3.2.2) mime-types (3.3)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2019.0331) mime-types-data (3.2019.0904)
mini_mime (1.0.1) mini_mime (1.0.2)
mini_portile2 (2.4.0) mini_portile2 (2.4.0)
minitest (5.11.3) minitest (5.11.3)
minitest-reporters (1.3.6) minitest-reporters (1.3.8)
ansi ansi
builder builder
minitest (>= 5.0) minitest (>= 5.0)
@ -146,15 +142,15 @@ GEM
mocha (1.9.0) mocha (1.9.0)
metaclass (~> 0.0.1) metaclass (~> 0.0.1)
mock_redis (0.21.0) mock_redis (0.21.0)
monetize (1.9.1) monetize (1.9.2)
money (~> 6.12) money (~> 6.12)
money (6.13.4) money (6.13.4)
i18n (>= 0.6.4, <= 2) i18n (>= 0.6.4, <= 2)
msgpack (1.3.0) msgpack (1.3.1)
multi_json (1.13.1) multi_json (1.13.1)
multipart-post (2.1.1) multipart-post (2.1.1)
mustermann (1.0.3) mustermann (1.0.3)
net-http-persistent (3.0.1) net-http-persistent (3.1.0)
connection_pool (~> 2.2) connection_pool (~> 2.2)
net-scp (2.0.0) net-scp (2.0.0)
net-ssh (>= 2.6.5, < 6.0.0) net-ssh (>= 2.6.5, < 6.0.0)
@ -166,24 +162,21 @@ GEM
nokogumbo (2.0.1) nokogumbo (2.0.1)
nokogiri (~> 1.8, >= 1.8.4) nokogiri (~> 1.8, >= 1.8.4)
ox (2.11.0) ox (2.11.0)
pastel (0.7.3)
equatable (~> 0.6)
tty-color (~> 0.5)
paypal-recurring (1.1.0) paypal-recurring (1.1.0)
pg (1.1.4) pg (1.1.4)
poltergeist (1.18.1) poltergeist (1.18.1)
capybara (>= 2.1, < 4) capybara (>= 2.1, < 4)
cliver (~> 0.3.1) cliver (~> 0.3.1)
websocket-driver (>= 0.2.0) websocket-driver (>= 0.2.0)
progress (3.5.1) progress (3.5.2)
pry (0.12.2) pry (0.12.2)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.9.0) method_source (~> 0.9.0)
pry-byebug (3.7.0) pry-byebug (3.7.0)
byebug (~> 11.0) byebug (~> 11.0)
pry (~> 0.10) pry (~> 0.10)
public_suffix (3.1.1) public_suffix (4.0.1)
puma (4.1.0) puma (4.1.1)
nio4r (~> 2.0) nio4r (~> 2.0)
rack (2.0.7) rack (2.0.7)
rack-cache (1.9.0) rack-cache (1.9.0)
@ -202,13 +195,14 @@ GEM
redis (3.3.5) redis (3.3.5)
redis-namespace (1.6.0) redis-namespace (1.6.0)
redis (>= 3.0.4) redis (>= 3.0.4)
regexp_parser (1.5.1) regexp_parser (1.6.0)
rest-client (2.0.2) rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0) http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0) mime-types (>= 1.16, < 4.0)
netrc (~> 0.8) netrc (~> 0.8)
rinku (2.0.6) rinku (2.0.6)
rmagick (3.2.0) rmagick (4.0.0)
ruby-progressbar (1.10.1) ruby-progressbar (1.10.1)
rye (0.9.13) rye (0.9.13)
annoy annoy
@ -218,7 +212,7 @@ GEM
net-ssh (>= 2.0.13) net-ssh (>= 2.0.13)
sysinfo (>= 0.8.1) sysinfo (>= 0.8.1)
safe_yaml (1.0.5) safe_yaml (1.0.5)
sanitize (5.0.0) sanitize (5.1.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.8.0) nokogiri (>= 1.8.0)
nokogumbo (~> 2.0) nokogumbo (~> 2.0)
@ -228,7 +222,7 @@ GEM
rb-fsevent (~> 0.9, >= 0.9.4) rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7) rb-inotify (~> 0.9, >= 0.9.7)
sax-machine (1.3.2) sax-machine (1.3.2)
sequel (5.22.0) sequel (5.24.0)
sequel_pg (1.12.2) sequel_pg (1.12.2)
pg (>= 0.18.0) pg (>= 0.18.0)
sequel (>= 4.38.0) sequel (>= 4.38.0)
@ -256,15 +250,10 @@ GEM
sinatra-xsendfile (0.4.2) sinatra-xsendfile (0.4.2)
sinatra (>= 0.9.1) sinatra (>= 0.9.1)
storable (0.8.9) storable (0.8.9)
strings (0.1.5)
strings-ansi (~> 0.1)
unicode-display_width (~> 1.5)
unicode_utils (~> 1.4)
strings-ansi (0.1.0)
stripe (4.2.0) stripe (4.2.0)
faraday (~> 0.13) faraday (~> 0.13)
net-http-persistent (~> 3.0) net-http-persistent (~> 3.0)
stripe-ruby-mock (2.5.6) stripe-ruby-mock (2.5.8)
dante (>= 0.2.0) dante (>= 0.2.0)
multi_json (~> 1.0) multi_json (~> 1.0)
stripe (>= 2.0.3) stripe (>= 2.0.3)
@ -280,22 +269,12 @@ GEM
thread_safe (0.3.6) thread_safe (0.3.6)
tilt (2.0.9) tilt (2.0.9)
timecop (0.9.1) timecop (0.9.1)
tins (1.20.3) tins (1.21.1)
tty-color (0.5.0)
tty-pager (0.12.1)
strings (~> 0.1.4)
tty-screen (~> 0.6)
tty-which (~> 0.4)
tty-screen (0.6.5)
tty-tree (0.3.0)
tty-which (0.4.1)
tzinfo (1.2.5) tzinfo (1.2.5)
thread_safe (~> 0.1) thread_safe (~> 0.1)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.6) unf_ext (0.0.7.6)
unicode-display_width (1.6.0)
unicode_utils (1.4.0)
uuidtools (2.1.5) uuidtools (2.1.5)
webmock (3.5.1) webmock (3.5.1)
addressable (>= 2.3.6) addressable (>= 2.3.6)
@ -304,10 +283,11 @@ GEM
websocket-driver (0.7.1) websocket-driver (0.7.1)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.4) websocket-extensions (0.1.4)
will_paginate (3.1.7) will_paginate (3.1.8)
xmlrpc (0.3.0) xmlrpc (0.3.0)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.1.10)
zipruby (0.3.6) zipruby (0.3.6)
PLATFORMS PLATFORMS
@ -317,10 +297,10 @@ DEPENDENCIES
acme-client (= 0.6.3) acme-client (= 0.6.3)
activesupport activesupport
addressable addressable
apparition
base32 base32
bcrypt bcrypt
capybara capybara
capybara_minitest_spec
certified certified
coveralls coveralls
dav4rack! dav4rack!
@ -377,7 +357,7 @@ DEPENDENCIES
sinatra-flash sinatra-flash
sinatra-xsendfile sinatra-xsendfile
stripe (= 4.2.0) stripe (= 4.2.0)
stripe-ruby-mock (= 2.5.6) stripe-ruby-mock (= 2.5.8)
terrapin terrapin
thread thread
tilt tilt

View file

@ -69,7 +69,7 @@ post '/create' do
return {result: 'error'}.to_json return {result: 'error'}.to_json
end end
if !@site.valid? || Site.ip_create_limit?(request.ip) if Site.ip_create_limit?(request.ip)
flash[:error] = 'Your IP address has created too many sites, please try again later or contact support.' flash[:error] = 'Your IP address has created too many sites, please try again later or contact support.'
return {result: 'error'}.to_json return {result: 'error'}.to_json
end end
@ -78,6 +78,11 @@ post '/create' do
flash[:error] = 'Cannot use a disposable email address.' flash[:error] = 'Cannot use a disposable email address.'
return {result: 'error'}.to_json return {result: 'error'}.to_json
end end
if !@site.valid?
flash[:error] = @site.errors.first.last.first
return {result: 'error'}.to_json
end
end end
@site.email_confirmed = true if self.class.development? @site.email_confirmed = true if self.class.development?

View file

@ -105,7 +105,7 @@ Sequel::Model.plugin :validation_helpers
Sequel::Model.plugin :force_encoding, 'UTF-8' Sequel::Model.plugin :force_encoding, 'UTF-8'
Sequel::Model.plugin :defaults_setter Sequel::Model.plugin :defaults_setter
Sequel::Model.plugin :create_timestamp Sequel::Model.plugin :create_timestamp
Sequel.default_timezone = 'UTC' Sequel.default_timezone = :utc
Sequel::Migrator.apply DB, './migrations' Sequel::Migrator.apply DB, './migrations'
Stripe.api_key = $config['stripe_api_key'] Stripe.api_key = $config['stripe_api_key']

View file

@ -324,7 +324,7 @@ class Site < Sequel::Model
return false if ip.blank? return false if ip.blank?
return true if Site.where(is_banned: true). return true if Site.where(is_banned: true).
where(ip: ip). where(ip: ip).
where(['updated_at > ?', Time.now-BANNED_TIME]). where(['banned_at > ?', Time.now-BANNED_TIME]).
first first
return true if BlockedIp[ip] return true if BlockedIp[ip]

View file

@ -51,4 +51,56 @@ describe '/admin' do
end end
end end
describe 'email blasting' do
before do
EmailWorker.jobs.clear
@admin_site = Fabricate :site, is_admin: true
end
it 'works' do
DB['update sites set changed_count=?', 0].first
relevant_emails = []
sites_emailed_count = Site::EMAIL_BLAST_MAXIMUM_PER_DAY*2
sites_emailed_count.times {
site = Fabricate :site, updated_at: Time.now, changed_count: 1
relevant_emails << site.email
}
EmailWorker.jobs.clear
time = Time.now
Timecop.freeze(time) do
visit '/admin/email'
fill_in 'subject', with: 'Subject Test'
fill_in 'body', with: 'Body Test'
click_button 'Send'
relevant_jobs = EmailWorker.jobs.select{|j| relevant_emails.include?(j['args'].first['to']) }
relevant_jobs.length.must_equal sites_emailed_count
relevant_jobs.each do |job|
args = job['args'].first
args['from'].must_equal 'Kyle from Neocities <kyle@neocities.org>'
args['subject'].must_equal 'Subject Test'
args['body'].must_equal 'Body Test'
end
relevant_jobs.select {|j| j['at'].nil? || j['at'] == Time.now.to_f}.length.must_equal 1
relevant_jobs.select {|j| j['at'] == (Time.now + 0.5).to_f}.length.must_equal 1
relevant_jobs.select {|j| j['at'] == (time+1.day.to_i).to_f}.length.must_equal 1
relevant_jobs.select {|j| j['at'] == (time+1.day.to_i+0.5).to_f}.length.must_equal 1
end
end
end
end end

View file

@ -1,9 +1,5 @@
require_relative './environment.rb' require_relative './environment.rb'
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: false)
end
describe 'signup' do describe 'signup' do
include Capybara::DSL include Capybara::DSL
@ -17,7 +13,7 @@ describe 'signup' do
end end
before do before do
Capybara.default_driver = :poltergeist Capybara.default_driver = :apparition
Capybara.reset_sessions! Capybara.reset_sessions!
visit '/education' visit '/education'
page.must_have_content 'Neocities' # Used to force load wait page.must_have_content 'Neocities' # Used to force load wait

View file

@ -1,5 +1,30 @@
require_relative '../environment' require_relative '../environment'
require 'capybara/minitest'
require 'capybara/minitest/spec'
require 'rack_session_access/capybara'
require 'capybara/apparition'
Capybara.app = Sinatra::Application
include Capybara::Minitest::Assertions
Capybara.default_max_wait_time = 5
#Capybara.register_driver :apparition do |app|
# Capybara::Apparition::Driver.new(app, headless: false)
#end
=begin
def setup
Capybara.current_driver = :apparition
end
def teardown
Capybara.reset_sessions!
Capybara.use_default_driver
end
=end
=begin
require 'capybara' require 'capybara'
require 'capybara/dsl' require 'capybara/dsl'
require 'capybara/poltergeist' require 'capybara/poltergeist'
@ -10,3 +35,4 @@ Capybara.app = Sinatra::Application
def teardown def teardown
Capybara.reset_sessions! Capybara.reset_sessions!
end end
=end

View file

@ -1,9 +1,5 @@
require_relative './environment.rb' require_relative './environment.rb'
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: false)
end
describe 'signup' do describe 'signup' do
include Capybara::DSL include Capybara::DSL
@ -28,10 +24,9 @@ describe 'signup' do
end end
before do before do
Capybara.default_driver = :poltergeist Capybara.default_driver = :apparition
Capybara.reset_sessions! Capybara.reset_sessions!
visit_signup visit_signup
page.must_have_content 'Neocities' # Used to force load wait
end end
after do after do
@ -65,15 +60,15 @@ describe 'signup' do
end end
it 'fails if site with same ip has been banned' do it 'fails if site with same ip has been banned' do
@banned_site = Fabricate :site @banned_site = Fabricate :site, ip: '127.0.0.1'
@banned_site.is_banned = true @banned_site.ban!
@banned_site.save_changes
fill_in_valid fill_in_valid
click_signup_button click_signup_button
site = Site[username: @site[:username]]
Site[username: @site[:username]].must_be_nil Site[username: @site[:username]].must_be_nil
current_path.must_equal '/' current_path.must_equal '/'
page.wont_have_content 'Welcome to Neocities' page.wont_have_content 'Welcome to Neocities'
@banned_site.update ip: nil
end end
it 'fails if IP is banned from blocked ips list' do it 'fails if IP is banned from blocked ips list' do

View file

@ -1,14 +1,10 @@
require_relative './environment.rb' require_relative './environment.rb'
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: false)
end
describe '/supporter' do describe '/supporter' do
include Capybara::DSL include Capybara::DSL
before do before do
Capybara.default_driver = :poltergeist Capybara.default_driver = :apparition
Capybara.reset_sessions! Capybara.reset_sessions!
@site = Fabricate :site @site = Fabricate :site
@ -33,14 +29,16 @@ describe '/supporter' do
it 'should work for fresh signup' do it 'should work for fresh signup' do
visit '/supporter' visit '/supporter'
find('#cc_number', visible: false).set '4242424242424242' find('.cc-number input[type=text]').set '4242424242424242'
find('#cc_exp_month', visible: false).set '01' all('.cc-exp input[type=text]').first.set '01'
find('#cc_exp_year', visible: false).set Date.today.next_year.year.to_s[2..3] all('.cc-exp input[type=text]').last.set Date.today.next_year.year.to_s[2..3]
find('#cc_name', visible: false).set 'Penelope' find('.cc-name').set 'Penelope'
find('#cc_cvc', visible: false).set '123' all('.flip-tab').first.click
find('#stripe_token', visible: false).set @stripe_helper.generate_card_token find('.cc-cvc').set '123'
page.evaluate_script("document.getElementById('stripe_token').value = '#{@stripe_helper.generate_card_token}'")
click_link 'Upgrade for $5/mo' click_link 'Upgrade for $5/mo'
page.current_path.must_equal '/supporter/thanks' page.current_path.must_equal '/supporter/thanks'
all('.txt-Center')
page.body.must_match /You have become a Neocities Supporter/ page.body.must_match /You have become a Neocities Supporter/
@site.reload @site.reload
@site.stripe_customer_id.wont_be_nil @site.stripe_customer_id.wont_be_nil

View file

@ -1,56 +0,0 @@
require_relative './environment.rb'
require 'rack/test'
include Rack::Test::Methods
def app
Sinatra::Application
end
describe 'email blasting' do
before do
EmailWorker.jobs.clear
@admin_site = Fabricate :site, is_admin: true
end
it 'works' do
DB['update sites set changed_count=?', 0].first
relevant_emails = []
sites_emailed_count = Site::EMAIL_BLAST_MAXIMUM_PER_DAY*2
sites_emailed_count.times {
site = Fabricate :site, updated_at: Time.now, changed_count: 1
relevant_emails << site.email
}
EmailWorker.jobs.clear
time = Time.now
Timecop.freeze(time) do
post '/admin/email', {
:csrf_token => 'abcd',
:subject => 'Subject Test',
:body => 'Body Test'}, {
'rack.session' => { 'id' => @admin_site.id, '_csrf_token' => 'abcd' }
}
relevant_jobs = EmailWorker.jobs.select{|j| relevant_emails.include?(j['args'].first['to']) }
relevant_jobs.length.must_equal sites_emailed_count
relevant_jobs.each do |job|
args = job['args'].first
args['from'].must_equal 'Kyle from Neocities <kyle@neocities.org>'
args['subject'].must_equal 'Subject Test'
args['body'].must_equal 'Body Test'
end
relevant_jobs.select {|j| j['at'].nil? || j['at'] == Time.now.to_f}.length.must_equal 1
relevant_jobs.select {|j| j['at'] == (Time.now + 0.5).to_f}.length.must_equal 1
relevant_jobs.select {|j| j['at'] == (time+1.day.to_i).to_f}.length.must_equal 1
relevant_jobs.select {|j| j['at'] == (time+1.day.to_i+0.5).to_f}.length.must_equal 1
end
end
end

View file

@ -1,320 +1,329 @@
require_relative './environment.rb' require_relative './environment.rb'
require 'rack/test' require 'rack/test'
include Rack::Test::Methods describe 'api' do
include Rack::Test::Methods
def app def app
Sinatra::Application Sinatra::Application
end
def create_site(opts={})
site_attr = Fabricate.attributes_for :site
@site = Site.create site_attr.merge(opts)
@user = site_attr[:username]
@pass = site_attr[:password]
end
describe 'api not found' do
it 'returns json for missing route' do
get '/api/sdlfkjsdlfjds'
last_response.status.must_equal 404
res[:result].must_equal 'error'
res[:error_type].must_equal 'not_found'
end end
end
describe 'api list' do def create_site(opts={})
it 'returns all files without path' do site_attr = Fabricate.attributes_for :site
create_site @site = Site.create site_attr.merge(opts)
basic_authorize @user, @pass @user = site_attr[:username]
get '/api/list' @pass = site_attr[:password]
end
res[:result].must_equal 'success' def site_file_exists?(file)
res[:files].length.must_equal @site.site_files.length File.exist?(@site.files_path(file))
end
res[:files].each do |file| def res
site_file = @site.site_files.select {|s| s[:path] == file[:path]}.first JSON.parse last_response.body, symbolize_names: true
site_file[:is_directory].must_equal file[:is_directory] end
site_file[:size].must_equal file[:size]
site_file[:updated_at].rfc2822.must_equal file[:updated_at] describe 'not found' do
site_file[:sha1_hash].must_equal file[:sha1_hash] it 'returns json for missing route' do
get '/api/sdlfkjsdlfjds'
last_response.status.must_equal 404
res[:result].must_equal 'error'
res[:error_type].must_equal 'not_found'
end end
end end
it 'shows empty array for missing path' do describe 'list' do
create_site it 'returns all files without path' do
basic_authorize @user, @pass create_site
get '/api/list', path: '/fail' basic_authorize @user, @pass
res[:result].must_equal 'success' get '/api/list'
res[:files].must_equal []
res[:result].must_equal 'success'
res[:files].length.must_equal @site.site_files.length
res[:files].each do |file|
site_file = @site.site_files.select {|s| s[:path] == file[:path]}.first
site_file[:is_directory].must_equal file[:is_directory]
site_file[:size].must_equal file[:size]
site_file[:updated_at].rfc2822.must_equal file[:updated_at]
site_file[:sha1_hash].must_equal file[:sha1_hash]
end
end
it 'shows empty array for missing path' do
create_site
basic_authorize @user, @pass
get '/api/list', path: '/fail'
res[:result].must_equal 'success'
res[:files].must_equal []
end
it 'shows files in path' do
create_site
tempfile = Tempfile.new
tempfile.write('meep html')
@site.store_files [{filename: '/derp/test.html', tempfile: tempfile}]
basic_authorize @user, @pass
get '/api/list', path: '/derp'
res[:result].must_equal 'success'
res[:files].length.must_equal 1
file = res[:files].first
file[:path].must_equal 'derp/test.html'
file[:updated_at].must_equal @site.site_files.select {|s| s.path == 'derp/test.html'}.first.updated_at.rfc2822
end
end end
it 'shows files in path' do describe 'info' do
create_site it 'fails for no input' do
tempfile = Tempfile.new get '/api/info'
tempfile.write('meep html') res[:error_type] = 'missing_sitename'
@site.store_files [{filename: '/derp/test.html', tempfile: tempfile}] end
basic_authorize @user, @pass
get '/api/list', path: '/derp'
res[:result].must_equal 'success'
res[:files].length.must_equal 1
file = res[:files].first
file[:path].must_equal 'derp/test.html'
file[:updated_at].must_equal @site.site_files.select {|s| s.path == 'derp/test.html'}.first.updated_at.rfc2822
end
end
describe 'api info' do it 'fails for banned sites' do
it 'fails for no input' do create_site
get '/api/info' @site.update is_banned: true
res[:error_type] = 'missing_sitename' get '/api/info', sitename: @site.username
res[:error_type].must_equal 'site_not_found'
@site.reload.api_calls.must_equal 0
end
it 'fails for nonexistent site' do
get '/api/info', sitename: 'notexist'
res[:error_type].must_equal 'site_not_found'
end
it 'succeeds for valid sitename' do
create_site
@site.update hits: 31337, domain: 'derp.com', new_tags_string: 'derpie, man'
@site.add_archive ipfs_hash: 'QmXGTaGWTT1uUtfSb2sBAvArMEVLK4rQEcQg5bv7wwdzwU'
get '/api/info', sitename: @user
res[:result].must_equal 'success'
res[:info][:sitename].must_equal @site.username
res[:info][:hits].must_equal 31337
res[:info][:created_at].must_equal @site.created_at.rfc2822
res[:info][:last_updated].must_be_nil
res[:info][:domain].must_equal 'derp.com'
res[:info][:tags].must_equal ['derpie', 'man']
res[:info][:latest_ipfs_hash].must_equal 'QmXGTaGWTT1uUtfSb2sBAvArMEVLK4rQEcQg5bv7wwdzwU'
@site.reload.api_calls.must_equal 0
end
it 'shows latest ipfs hash as nil when not present' do
create_site
get '/api/info', sitename: @user
res[:info][:latest_ipfs_hash].must_be_nil
end
it 'fails for bad auth' do
basic_authorize 'derp', 'fake'
get '/api/info'
res[:error_type].must_equal 'invalid_auth'
end
it 'succeeds for api auth' do
create_site
@site.update hits: 12345
basic_authorize @user, @pass
get '/api/info'
res[:info][:hits] == 12345
end
end end
it 'fails for banned sites' do describe 'delete' do
create_site it 'fails with no or bad auth' do
@site.update is_banned: true post '/api/delete', filenames: ['hi.html']
get '/api/info', sitename: @site.username res[:error_type].must_equal 'invalid_auth'
res[:error_type].must_equal 'site_not_found' create_site
@site.reload.api_calls.must_equal 0 basic_authorize 'derp', 'fake'
post '/api/delete', filenames: ['hi.html']
res[:error_type].must_equal 'invalid_auth'
end
it 'fails with missing filename argument' do
create_site
basic_authorize @user, @pass
post '/api/delete'
res[:error_type].must_equal 'missing_filenames'
end
it 'fails to delete index.html' do
create_site
basic_authorize @user, @pass
post '/api/delete', filenames: ['index.html']
res[:error_type].must_equal 'cannot_delete_index'
end
it 'succeeds with weird filenames' do
create_site
basic_authorize @user, @pass
@site.store_files [{filename: 't$st.jpg', tempfile: Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')}]
post '/api/delete', filenames: ['t$st.jpg']
res[:result].must_equal 'success'
create_site
basic_authorize @user, @pass
post '/api/delete', filenames: ['./config.yml']
res[:error_type].must_equal 'missing_files'
end
it 'fails with missing files' do
create_site
basic_authorize @user, @pass
@site.store_files [{filename: 'test.jpg', tempfile: Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')}]
post '/api/delete', filenames: ['doesntexist.jpg']
res[:error_type].must_equal 'missing_files'
end
it 'fails to delete site directory' do
create_site
basic_authorize @user, @pass
post '/api/delete', filenames: ['/']
res[:error_type].must_equal 'cannot_delete_site_directory'
File.exist?(@site.files_path).must_equal true
end
it 'fails to delete other directories' do
create_site
@other_site = @site
create_site
basic_authorize @user, @pass
post '/api/delete', filenames: ["../#{@other_site.username}"]
File.exist?(@other_site.base_files_path).must_equal true
res[:error_type].must_equal 'missing_files'
post '/api/delete', filenames: ["../#{@other_site.username}/index.html"]
File.exist?(@other_site.base_files_path+'/index.html').must_equal true
res[:error_type].must_equal 'missing_files'
end
it 'succeeds with valid filenames' do
create_site
basic_authorize @user, @pass
@site.store_files [{filename: 'test.jpg', tempfile: Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')}]
@site.store_files [{filename: 'test2.jpg', tempfile: Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')}]
post '/api/delete', filenames: ['test.jpg', 'test2.jpg']
res[:result].must_equal 'success'
site_file_exists?('test.jpg').must_equal false
site_file_exists?('test2.jpg').must_equal false
end
end end
it 'fails for nonexistent site' do describe 'key' do
get '/api/info', sitename: 'notexist' it 'generates new key with valid login' do
res[:error_type].must_equal 'site_not_found' create_site
basic_authorize @user, @pass
get '/api/key'
res[:result].must_equal 'success'
res[:api_key].must_equal @site.reload.api_key
end
it 'returns existing key' do
create_site
@site.generate_api_key!
basic_authorize @user, @pass
get '/api/key'
res[:api_key].must_equal @site.api_key
end
it 'fails for bad login' do
create_site
basic_authorize 'zero', 'cool'
get '/api/key'
res[:error_type].must_equal 'invalid_auth'
end
end end
it 'succeeds for valid sitename' do describe 'upload hash' do
create_site it 'succeeds' do
@site.update hits: 31337, domain: 'derp.com', new_tags_string: 'derpie, man' create_site
@site.add_archive ipfs_hash: 'QmXGTaGWTT1uUtfSb2sBAvArMEVLK4rQEcQg5bv7wwdzwU' basic_authorize @user, @pass
get '/api/info', sitename: @user test_hash = Digest::SHA1.file('./tests/files/test.jpg').hexdigest
res[:result].must_equal 'success'
res[:info][:sitename].must_equal @site.username post '/api/upload', {
res[:info][:hits].must_equal 31337 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'),
res[:info][:created_at].must_equal @site.created_at.rfc2822 'test2.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
res[:info][:last_updated].must_be_nil }
res[:info][:domain].must_equal 'derp.com'
res[:info][:tags].must_equal ['derpie', 'man'] post '/api/upload_hash', "test.jpg" => test_hash, "test2.jpg" => Digest::SHA1.hexdigest('herpderp')
res[:info][:latest_ipfs_hash].must_equal 'QmXGTaGWTT1uUtfSb2sBAvArMEVLK4rQEcQg5bv7wwdzwU'
@site.reload.api_calls.must_equal 0 res[:result].must_equal 'success'
res[:files][:'test.jpg'].must_equal true
res[:files][:'test2.jpg'].must_equal false
end
end end
it 'shows latest ipfs hash as nil when not present' do describe 'rename' do
create_site before do
get '/api/info', sitename: @user create_site
res[:info][:latest_ipfs_hash].must_be_nil basic_authorize @user, @pass
post '/api/upload', {
'testdir/test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
}
end
it 'succeeds' do
post '/api/rename', path: 'testdir/test.jpg', new_path: 'testdir/test2.jpg'
res[:result].must_equal 'success'
end
it 'fails to overwrite index file' do
post '/api/rename', path: 'testdir/test.jpg', new_path: 'index.html'
res[:result].must_equal 'error'
res[:error_type].must_equal 'rename_error'
res[:message].must_equal 'file already exists'
end
it 'fails to overwrite existing file' do
post '/api/rename', path: 'testdir/test.jpg', new_path: 'not_found.html'
res[:result].must_equal 'error'
res[:error_type].must_equal 'rename_error'
end
it 'succeeds with directory' do
@site.create_directory 'derpiedir'
post '/api/rename', path: 'derpiedir', new_path: 'notderpiedir'
res[:result].must_equal 'success'
end
end end
it 'fails for bad auth' do describe 'upload' do
basic_authorize 'derp', 'fake' it 'fails with no auth' do
get '/api/info' post '/api/upload'
res[:error_type].must_equal 'invalid_auth' res[:result].must_equal 'error'
end res[:error_type].must_equal 'invalid_auth'
end
it 'succeeds for api auth' do it 'fails for bad auth' do
create_site basic_authorize 'username', 'password'
@site.update hits: 12345 post '/api/upload'
basic_authorize @user, @pass res[:error_type].must_equal 'invalid_auth'
get '/api/info' end
res[:info][:hits] == 12345
end
end
describe 'api delete' do it 'fails with missing files' do
it 'fails with no or bad auth' do create_site
post '/api/delete', filenames: ['hi.html'] basic_authorize @user, @pass
res[:error_type].must_equal 'invalid_auth' post '/api/upload'
create_site res[:error_type].must_equal 'missing_files'
basic_authorize 'derp', 'fake' end
post '/api/delete', filenames: ['hi.html']
res[:error_type].must_equal 'invalid_auth'
end
it 'fails with missing filename argument' do it 'succeeds with valid api key' do
create_site create_site
basic_authorize @user, @pass @site.api_key.must_be_nil
post '/api/delete' @site.generate_api_key!
res[:error_type].must_equal 'missing_filenames' @site.reload.api_key.wont_equal nil
end header 'Authorization', "Bearer #{@site.api_key}"
post '/api/upload', 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
res[:result].must_equal 'success'
site_file_exists?('test.jpg').must_equal true
end
it 'fails to delete index.html' do it 'fails with bad api key' do
create_site create_site
basic_authorize @user, @pass @site.generate_api_key!
post '/api/delete', filenames: ['index.html'] header 'Authorization', "Bearer zerocool"
res[:error_type].must_equal 'cannot_delete_index' post '/api/upload', 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
end res[:result].must_equal 'error'
res[:error_type].must_equal 'invalid_auth'
it 'succeeds with weird filenames' do end
create_site
basic_authorize @user, @pass
@site.store_files [{filename: 't$st.jpg', tempfile: Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')}]
post '/api/delete', filenames: ['t$st.jpg']
res[:result].must_equal 'success'
create_site
basic_authorize @user, @pass
post '/api/delete', filenames: ['./config.yml']
res[:error_type].must_equal 'missing_files'
end
it 'fails with missing files' do
create_site
basic_authorize @user, @pass
@site.store_files [{filename: 'test.jpg', tempfile: Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')}]
post '/api/delete', filenames: ['doesntexist.jpg']
res[:error_type].must_equal 'missing_files'
end
it 'fails to delete site directory' do
create_site
basic_authorize @user, @pass
post '/api/delete', filenames: ['/']
res[:error_type].must_equal 'cannot_delete_site_directory'
File.exist?(@site.files_path).must_equal true
end
it 'fails to delete other directories' do
create_site
@other_site = @site
create_site
basic_authorize @user, @pass
post '/api/delete', filenames: ["../#{@other_site.username}"]
File.exist?(@other_site.base_files_path).must_equal true
res[:error_type].must_equal 'missing_files'
post '/api/delete', filenames: ["../#{@other_site.username}/index.html"]
File.exist?(@other_site.base_files_path+'/index.html').must_equal true
res[:error_type].must_equal 'missing_files'
end
it 'succeeds with valid filenames' do
create_site
basic_authorize @user, @pass
@site.store_files [{filename: 'test.jpg', tempfile: Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')}]
@site.store_files [{filename: 'test2.jpg', tempfile: Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')}]
post '/api/delete', filenames: ['test.jpg', 'test2.jpg']
res[:result].must_equal 'success'
site_file_exists?('test.jpg').must_equal false
site_file_exists?('test2.jpg').must_equal false
end
end
describe 'api key' do
it 'generates new key with valid login' do
create_site
basic_authorize @user, @pass
get '/api/key'
res[:result].must_equal 'success'
res[:api_key].must_equal @site.reload.api_key
end
it 'returns existing key' do
create_site
@site.generate_api_key!
basic_authorize @user, @pass
get '/api/key'
res[:api_key].must_equal @site.api_key
end
it 'fails for bad login' do
create_site
basic_authorize 'zero', 'cool'
get '/api/key'
res[:error_type].must_equal 'invalid_auth'
end
end
describe 'api upload hash' do
it 'succeeds' do
create_site
basic_authorize @user, @pass
test_hash = Digest::SHA1.file('./tests/files/test.jpg').hexdigest
post '/api/upload', {
'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'),
'test2.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
}
post '/api/upload_hash', "test.jpg" => test_hash, "test2.jpg" => Digest::SHA1.hexdigest('herpderp')
res[:result].must_equal 'success'
res[:files][:'test.jpg'].must_equal true
res[:files][:'test2.jpg'].must_equal false
end
end
describe 'api rename' do
before do
create_site
basic_authorize @user, @pass
post '/api/upload', {
'testdir/test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
}
end
it 'succeeds' do
post '/api/rename', path: 'testdir/test.jpg', new_path: 'testdir/test2.jpg'
res[:result].must_equal 'success'
end
it 'fails to overwrite index file' do
post '/api/rename', path: 'testdir/test.jpg', new_path: 'index.html'
res[:result].must_equal 'error'
res[:error_type].must_equal 'rename_error'
res[:message].must_equal 'file already exists'
end
it 'fails to overwrite existing file' do
post '/api/rename', path: 'testdir/test.jpg', new_path: 'not_found.html'
res[:result].must_equal 'error'
res[:error_type].must_equal 'rename_error'
end
it 'succeeds with directory' do
@site.create_directory 'derpiedir'
post '/api/rename', path: 'derpiedir', new_path: 'notderpiedir'
res[:result].must_equal 'success'
end
end
describe 'api upload' do
it 'fails with no auth' do
post '/api/upload'
res[:result].must_equal 'error'
res[:error_type].must_equal 'invalid_auth'
end
it 'fails for bad auth' do
basic_authorize 'username', 'password'
post '/api/upload'
res[:error_type].must_equal 'invalid_auth'
end
it 'fails with missing files' do
create_site
basic_authorize @user, @pass
post '/api/upload'
res[:error_type].must_equal 'missing_files'
end
it 'succeeds with valid api key' do
create_site
@site.api_key.must_be_nil
@site.generate_api_key!
@site.reload.api_key.wont_equal nil
header 'Authorization', "Bearer #{@site.api_key}"
post '/api/upload', 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
res[:result].must_equal 'success'
site_file_exists?('test.jpg').must_equal true
end
it 'fails with bad api key' do
create_site
@site.generate_api_key!
header 'Authorization', "Bearer zerocool"
post '/api/upload', 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
res[:result].must_equal 'error'
res[:error_type].must_equal 'invalid_auth'
end
=begin =begin
# Getting too slow to run this test # Getting too slow to run this test
@ -332,125 +341,118 @@ describe 'api upload' do
end end
=end =end
it 'resists directory traversal attack' do it 'resists directory traversal attack' do
create_site create_site
basic_authorize @user, @pass basic_authorize @user, @pass
post '/api/upload', { post '/api/upload', {
'../lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') '../lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
} }
res[:result].must_equal 'success' res[:result].must_equal 'success'
File.exist?(File.join(Site::SITE_FILES_ROOT, Site.sharding_dir(@site.username), @site.username, 'lol.jpg')).must_equal true File.exist?(File.join(Site::SITE_FILES_ROOT, Site.sharding_dir(@site.username), @site.username, 'lol.jpg')).must_equal true
@site.reload.api_calls.must_equal 1 @site.reload.api_calls.must_equal 1
end end
it 'scrubs root path slash' do it 'scrubs root path slash' do
create_site create_site
basic_authorize @user, @pass basic_authorize @user, @pass
post '/api/upload', { post '/api/upload', {
'/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') '/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
} }
res[:result].must_equal 'success' res[:result].must_equal 'success'
File.exist?(File.join(Site::SITE_FILES_ROOT, Site.sharding_dir(@site.username), @site.username, 'lol.jpg')).must_equal true File.exist?(File.join(Site::SITE_FILES_ROOT, Site.sharding_dir(@site.username), @site.username, 'lol.jpg')).must_equal true
end end
it 'fails for missing file name' do it 'fails for missing file name' do
create_site create_site
basic_authorize @user, @pass basic_authorize @user, @pass
post '/api/upload', { post '/api/upload', {
'/' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') '/' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
} }
res[:error_type].must_equal 'invalid_file_type' res[:error_type].must_equal 'invalid_file_type'
end end
it 'fails for file with no extension' do it 'fails for file with no extension' do
create_site create_site
basic_authorize @user, @pass basic_authorize @user, @pass
post '/api/upload', { post '/api/upload', {
'derpie' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') 'derpie' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
} }
res[:error_type].must_equal 'invalid_file_type' res[:error_type].must_equal 'invalid_file_type'
end end
it 'creates path for file uploads' do it 'creates path for file uploads' do
create_site create_site
basic_authorize @user, @pass basic_authorize @user, @pass
post '/api/upload', {
'derpie/derpingtons/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
}
res[:result].must_equal 'success'
File.exist?(@site.files_path('derpie/derpingtons/lol.jpg')).must_equal true
end
it 'records api calls that require auth' do
create_site
basic_authorize @user, @pass
2.times {
post '/api/upload', { post '/api/upload', {
'derpie/derpingtons/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') 'derpie/derpingtons/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
} }
} res[:result].must_equal 'success'
File.exist?(@site.files_path('derpie/derpingtons/lol.jpg')).must_equal true
end
@site.reload.api_calls.must_equal 2 it 'records api calls that require auth' do
end create_site
basic_authorize @user, @pass
it 'fails for invalid files' do 2.times {
create_site post '/api/upload', {
basic_authorize @user, @pass 'derpie/derpingtons/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
post '/api/upload', { }
'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'), }
'nord.avi' => Rack::Test::UploadedFile.new('./tests/files/flowercrime.wav', 'image/jpeg')
}
res[:error_type].must_equal 'invalid_file_type'
site_file_exists?('test.jpg').must_equal false
site_file_exists?('nord.avi').must_equal false
end
it 'succeeds with single file' do @site.reload.api_calls.must_equal 2
create_site end
basic_authorize @user, @pass
post '/api/upload', 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
res[:result].must_equal 'success'
site_file_exists?('test.jpg').must_equal true
end
it 'succeeds with two files' do it 'fails for invalid files' do
create_site create_site
basic_authorize @user, @pass basic_authorize @user, @pass
post '/api/upload', { post '/api/upload', {
'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'), 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'),
'test2.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') 'nord.avi' => Rack::Test::UploadedFile.new('./tests/files/flowercrime.wav', 'image/jpeg')
} }
res[:result].must_equal 'success' res[:error_type].must_equal 'invalid_file_type'
site_file_exists?('test.jpg').must_equal true site_file_exists?('test.jpg').must_equal false
site_file_exists?('test2.jpg').must_equal true site_file_exists?('nord.avi').must_equal false
end end
it 'fails with unwhitelisted file' do it 'succeeds with single file' do
create_site create_site
basic_authorize @user, @pass basic_authorize @user, @pass
post '/api/upload', 'flowercrime.wav' => Rack::Test::UploadedFile.new('./tests/files/flowercrime.wav', 'audio/x-wav') post '/api/upload', 'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
res[:result].must_equal 'error' res[:result].must_equal 'success'
res[:error_type].must_equal 'invalid_file_type' site_file_exists?('test.jpg').must_equal true
site_file_exists?('flowercrime.wav').must_equal false end
end
it 'succeeds for unwhitelisted file on supported plans' do it 'succeeds with two files' do
no_file_restriction_plans = Site::PLAN_FEATURES.select {|p,v| v[:no_file_restrictions] == true} create_site
no_file_restriction_plans.each do |plan_type,hash| basic_authorize @user, @pass
create_site plan_type: plan_type post '/api/upload', {
'test.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg'),
'test2.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
}
res[:result].must_equal 'success'
site_file_exists?('test.jpg').must_equal true
site_file_exists?('test2.jpg').must_equal true
end
it 'fails with unwhitelisted file' do
create_site
basic_authorize @user, @pass basic_authorize @user, @pass
post '/api/upload', 'flowercrime.wav' => Rack::Test::UploadedFile.new('./tests/files/flowercrime.wav', 'audio/x-wav') post '/api/upload', 'flowercrime.wav' => Rack::Test::UploadedFile.new('./tests/files/flowercrime.wav', 'audio/x-wav')
res[:result].must_equal 'success' res[:result].must_equal 'error'
site_file_exists?('flowercrime.wav').must_equal true res[:error_type].must_equal 'invalid_file_type'
site_file_exists?('flowercrime.wav').must_equal false
end
it 'succeeds for unwhitelisted file on supported plans' do
no_file_restriction_plans = Site::PLAN_FEATURES.select {|p,v| v[:no_file_restrictions] == true}
no_file_restriction_plans.each do |plan_type,hash|
create_site plan_type: plan_type
basic_authorize @user, @pass
post '/api/upload', 'flowercrime.wav' => Rack::Test::UploadedFile.new('./tests/files/flowercrime.wav', 'audio/x-wav')
res[:result].must_equal 'success'
site_file_exists?('flowercrime.wav').must_equal true
end
end end
end end
end end
def site_file_exists?(file)
File.exist?(@site.files_path(file))
end
def res
JSON.parse last_response.body, symbolize_names: true
end

View file

@ -1,20 +1,21 @@
require_relative './environment.rb' require_relative './environment.rb'
require 'rack/test'
include Rack::Test::Methods
def app
Sinatra::Application
end
def upload(hash)
post '/site_files/upload', hash.merge(csrf_token: 'abcd'), {'rack.session' => { 'id' => @site.id, '_csrf_token' => 'abcd' }}
end
def delete_file(hash)
post '/site_files/delete', hash.merge(csrf_token: 'abcd'), {'rack.session' => { 'id' => @site.id, '_csrf_token' => 'abcd' }}
end
describe 'site_files' do describe 'site_files' do
include Rack::Test::Methods
def app
Sinatra::Application
end
def upload(hash)
post '/site_files/upload', hash.merge(csrf_token: 'abcd'), {'rack.session' => { 'id' => @site.id, '_csrf_token' => 'abcd' }}
end
def delete_file(hash)
post '/site_files/delete', hash.merge(csrf_token: 'abcd'), {'rack.session' => { 'id' => @site.id, '_csrf_token' => 'abcd' }}
end
before do before do
@site = Fabricate :site @site = Fabricate :site
ThumbnailWorker.jobs.clear ThumbnailWorker.jobs.clear
@ -32,9 +33,10 @@ describe 'site_files' do
uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
upload 'files[]' => uploaded_file upload 'files[]' => uploaded_file
@site.site_files.last.path.must_equal 'test.jpg' testfile = @site.site_files_dataset.where(path: 'test.jpg').first
@site.site_files.last.rename('derp.jpg') testfile.wont_equal nil
@site.site_files.last.path.must_equal('derp.jpg') testfile.rename 'derp.jpg'
@site.site_files_dataset.where(path: 'derp.jpg').first.wont_equal nil
PurgeCacheWorker.jobs.first['args'].last.must_equal '/test.jpg' PurgeCacheWorker.jobs.first['args'].last.must_equal '/test.jpg'
File.exist?(@site.files_path('derp.jpg')).must_equal true File.exist?(@site.files_path('derp.jpg')).must_equal true
end end
@ -43,21 +45,25 @@ describe 'site_files' do
uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
upload 'files[]' => uploaded_file upload 'files[]' => uploaded_file
@site.site_files.last.path.must_equal 'test.jpg' testfile = @site.site_files_dataset.where(path: 'test.jpg').first
res = @site.site_files.last.rename('dasharezone.exe') res = testfile.rename('dasharezone.exe')
res.must_equal [false, 'unsupported file type'] res.must_equal [false, 'unsupported file type']
@site.site_files.last.path.must_equal('test.jpg') @site.site_files_dataset.where(path: 'test.jpg').first.wont_equal nil
end end
it 'works for directory' do it 'works for directory' do
@site.create_directory 'dirone' @site.create_directory 'dirone'
@site.site_files.last.path.must_equal 'dirone' @site.site_files.select {|sf| sf.path == 'dirone'}.length.must_equal 1
@site.site_files.last.is_directory.must_equal true
res = @site.site_files.last.rename('dasharezone') dirone = @site.site_files_dataset.where(path: 'dirone').first
dirone.wont_equal nil
dirone.is_directory.must_equal true
res = dirone.rename('dasharezone')
res.must_equal [true, nil] res.must_equal [true, nil]
@site.site_files.last.path.must_equal('dasharezone') dasharezone = @site.site_files_dataset.where(path: 'dasharezone').first
@site.site_files.last.is_directory.must_equal true dasharezone.wont_equal nil
dasharezone.is_directory.must_equal true
PurgeCacheWorker.jobs.first['args'].last.must_equal 'dirone' PurgeCacheWorker.jobs.first['args'].last.must_equal 'dirone'
PurgeCacheWorker.jobs.last['args'].last.must_equal 'dasharezone' PurgeCacheWorker.jobs.last['args'].last.must_equal 'dasharezone'
@ -91,14 +97,14 @@ describe 'site_files' do
'files[]' => Rack::Test::UploadedFile.new('./tests/files/index.html', 'image/jpeg') 'files[]' => Rack::Test::UploadedFile.new('./tests/files/index.html', 'image/jpeg')
) )
res = @site.site_files.last.rename('test/test.jpg') res = @site.site_files_dataset.where(path: 'test/index.html').first.rename('test/test.jpg')
res.must_equal [false, 'file already exists'] res.must_equal [false, 'file already exists']
end end
it 'doesnt wipe out existing dir' do it 'doesnt wipe out existing dir' do
@site.create_directory 'dirone' @site.create_directory 'dirone'
@site.create_directory 'dirtwo' @site.create_directory 'dirtwo'
res = @site.site_files.last.rename 'dirone' res = @site.site_files.select{|sf| sf.path == 'dirtwo'}.first.rename 'dirone'
res.must_equal [false, 'directory already exists'] res.must_equal [false, 'directory already exists']
end end
@ -110,17 +116,17 @@ describe 'site_files' do
it 'works with unicode characters' do it 'works with unicode characters' do
uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
upload 'files[]' => uploaded_file upload 'files[]' => uploaded_file
@site.site_files.last.rename("HELL💩؋.jpg") @site.site_files_dataset.where(path: 'test.jpg').first.rename("HELL💩؋.jpg")
@site.site_files.last.path.must_equal "HELL💩؋.jpg" @site.site_files_dataset.where(path: "HELL💩؋.jpg").first.wont_equal nil
end end
it 'scrubs weird carriage return shit characters' do it 'scrubs weird carriage return shit characters' do
uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg') uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
upload 'files[]' => uploaded_file upload 'files[]' => uploaded_file
proc { proc {
@site.site_files.last.rename("\r\n\t.jpg") @site.site_files_dataset.where(path: 'test.jpg').first.rename("\r\n\t.jpg")
}.must_raise ArgumentError }.must_raise ArgumentError
@site.site_files.last.path.must_equal "test.jpg" @site.site_files_dataset.where(path: 'test.jpg').first.wont_equal nil
end end
end end

View file

@ -1,13 +1,13 @@
require_relative './environment.rb' require_relative './environment.rb'
require 'rack/test' require 'rack/test'
include Rack::Test::Methods
def app
Sinatra::Application
end
describe 'tipping' do describe 'tipping' do
include Rack::Test::Methods
def app
Sinatra::Application
end
before do before do
EmailWorker.jobs.clear EmailWorker.jobs.clear
@site = Fabricate :site @site = Fabricate :site