mirror of
https://github.com/neocities/neocities.git
synced 2025-04-25 01:32:36 +02:00
Update gems, modernize test stack, fix flaky tests
This commit is contained in:
parent
e223b687b2
commit
69c32d17ed
14 changed files with 583 additions and 578 deletions
5
Gemfile
5
Gemfile
|
@ -71,13 +71,14 @@ group :test do
|
|||
gem 'mocha', require: nil
|
||||
gem 'rake', require: nil
|
||||
gem 'poltergeist'
|
||||
gem 'capybara_minitest_spec'
|
||||
gem 'capybara', require: nil #, '2.10.1', require: nil
|
||||
gem 'rack_session_access', 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 'mock_redis'
|
||||
gem 'simplecov', require: nil
|
||||
gem 'm'
|
||||
gem 'apparition'
|
||||
gem 'poltergeist', require: nil
|
||||
end
|
||||
|
|
102
Gemfile.lock
102
Gemfile.lock
|
@ -15,21 +15,25 @@ GEM
|
|||
specs:
|
||||
acme-client (0.6.3)
|
||||
faraday (~> 0.9, >= 0.9.1)
|
||||
activesupport (5.2.3)
|
||||
activesupport (6.0.0)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.6.0)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
zeitwerk (~> 2.1, >= 2.1.8)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
annoy (0.5.6)
|
||||
highline (>= 1.5.0)
|
||||
ansi (1.5.0)
|
||||
apparition (0.4.0)
|
||||
capybara (~> 3.13, < 4)
|
||||
websocket-driver (>= 0.6.5)
|
||||
base32 (0.3.2)
|
||||
bcrypt (3.1.13)
|
||||
builder (3.2.3)
|
||||
byebug (11.0.1)
|
||||
capybara (3.25.0)
|
||||
capybara (3.29.0)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
|
@ -37,9 +41,6 @@ GEM
|
|||
rack-test (>= 0.6.3)
|
||||
regexp_parser (~> 1.5)
|
||||
xpath (~> 3.2)
|
||||
capybara_minitest_spec (1.0.7)
|
||||
capybara (>= 2)
|
||||
minitest (>= 4)
|
||||
certified (1.0.0)
|
||||
climate_control (0.2.0)
|
||||
cliver (0.3.2)
|
||||
|
@ -57,21 +58,15 @@ GEM
|
|||
crass (1.0.4)
|
||||
dante (0.2.0)
|
||||
docile (1.3.2)
|
||||
domain_name (0.5.20180417)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
drydock (0.6.9)
|
||||
equatable (0.6.1)
|
||||
erubis (2.7.0)
|
||||
exifr (1.3.6)
|
||||
fabrication (2.20.2)
|
||||
facter (2.5.1)
|
||||
faker (1.9.4)
|
||||
i18n (>= 0.7)
|
||||
pastel (~> 0.7.2)
|
||||
thor (~> 0.20.0)
|
||||
tty-pager (~> 0.12.0)
|
||||
tty-screen (~> 0.6.5)
|
||||
tty-tree (~> 0.3.0)
|
||||
facter (2.5.6)
|
||||
faker (2.4.0)
|
||||
i18n (~> 1.6.0)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday_middleware (0.13.1)
|
||||
|
@ -83,12 +78,12 @@ GEM
|
|||
sax-machine (>= 1.0)
|
||||
ffi (1.11.1)
|
||||
filesize (0.2.0)
|
||||
fspath (3.1.1)
|
||||
fspath (3.1.2)
|
||||
gandi (3.3.28)
|
||||
hashie
|
||||
xmlrpc
|
||||
geoip (1.6.4)
|
||||
hashdiff (0.4.0)
|
||||
hashdiff (1.0.0)
|
||||
hashie (3.6.0)
|
||||
highline (2.0.2)
|
||||
hiredis (0.6.3)
|
||||
|
@ -100,23 +95,24 @@ GEM
|
|||
http-cookie (~> 1.0)
|
||||
http-form_data (~> 2.0)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
http-accept (1.7.0)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
http-form_data (2.1.1)
|
||||
http_parser.rb (0.6.0)
|
||||
i18n (1.6.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_optim (0.26.4)
|
||||
image_optim (0.26.5)
|
||||
exifr (~> 1.2, >= 1.2.2)
|
||||
fspath (~> 3.0)
|
||||
image_size (>= 1.5, < 3)
|
||||
in_threads (~> 1.3)
|
||||
progress (~> 3.0, >= 3.0.1)
|
||||
image_optim_pack (0.5.3)
|
||||
image_optim_pack (0.6.0)
|
||||
fspath (>= 2.1, < 4)
|
||||
image_optim (~> 0.19)
|
||||
image_size (2.0.1)
|
||||
in_threads (1.5.2)
|
||||
image_size (2.0.2)
|
||||
in_threads (1.5.3)
|
||||
io-extra (1.3.0)
|
||||
ipaddress (0.8.3)
|
||||
json (2.2.0)
|
||||
|
@ -132,13 +128,13 @@ GEM
|
|||
mini_mime (>= 0.1.1)
|
||||
metaclass (0.0.4)
|
||||
method_source (0.9.2)
|
||||
mime-types (3.2.2)
|
||||
mime-types (3.3)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2019.0331)
|
||||
mini_mime (1.0.1)
|
||||
mime-types-data (3.2019.0904)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.11.3)
|
||||
minitest-reporters (1.3.6)
|
||||
minitest-reporters (1.3.8)
|
||||
ansi
|
||||
builder
|
||||
minitest (>= 5.0)
|
||||
|
@ -146,15 +142,15 @@ GEM
|
|||
mocha (1.9.0)
|
||||
metaclass (~> 0.0.1)
|
||||
mock_redis (0.21.0)
|
||||
monetize (1.9.1)
|
||||
monetize (1.9.2)
|
||||
money (~> 6.12)
|
||||
money (6.13.4)
|
||||
i18n (>= 0.6.4, <= 2)
|
||||
msgpack (1.3.0)
|
||||
msgpack (1.3.1)
|
||||
multi_json (1.13.1)
|
||||
multipart-post (2.1.1)
|
||||
mustermann (1.0.3)
|
||||
net-http-persistent (3.0.1)
|
||||
net-http-persistent (3.1.0)
|
||||
connection_pool (~> 2.2)
|
||||
net-scp (2.0.0)
|
||||
net-ssh (>= 2.6.5, < 6.0.0)
|
||||
|
@ -166,24 +162,21 @@ GEM
|
|||
nokogumbo (2.0.1)
|
||||
nokogiri (~> 1.8, >= 1.8.4)
|
||||
ox (2.11.0)
|
||||
pastel (0.7.3)
|
||||
equatable (~> 0.6)
|
||||
tty-color (~> 0.5)
|
||||
paypal-recurring (1.1.0)
|
||||
pg (1.1.4)
|
||||
poltergeist (1.18.1)
|
||||
capybara (>= 2.1, < 4)
|
||||
cliver (~> 0.3.1)
|
||||
websocket-driver (>= 0.2.0)
|
||||
progress (3.5.1)
|
||||
progress (3.5.2)
|
||||
pry (0.12.2)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
pry-byebug (3.7.0)
|
||||
byebug (~> 11.0)
|
||||
pry (~> 0.10)
|
||||
public_suffix (3.1.1)
|
||||
puma (4.1.0)
|
||||
public_suffix (4.0.1)
|
||||
puma (4.1.1)
|
||||
nio4r (~> 2.0)
|
||||
rack (2.0.7)
|
||||
rack-cache (1.9.0)
|
||||
|
@ -202,13 +195,14 @@ GEM
|
|||
redis (3.3.5)
|
||||
redis-namespace (1.6.0)
|
||||
redis (>= 3.0.4)
|
||||
regexp_parser (1.5.1)
|
||||
rest-client (2.0.2)
|
||||
regexp_parser (1.6.0)
|
||||
rest-client (2.1.0)
|
||||
http-accept (>= 1.7.0, < 2.0)
|
||||
http-cookie (>= 1.0.2, < 2.0)
|
||||
mime-types (>= 1.16, < 4.0)
|
||||
netrc (~> 0.8)
|
||||
rinku (2.0.6)
|
||||
rmagick (3.2.0)
|
||||
rmagick (4.0.0)
|
||||
ruby-progressbar (1.10.1)
|
||||
rye (0.9.13)
|
||||
annoy
|
||||
|
@ -218,7 +212,7 @@ GEM
|
|||
net-ssh (>= 2.0.13)
|
||||
sysinfo (>= 0.8.1)
|
||||
safe_yaml (1.0.5)
|
||||
sanitize (5.0.0)
|
||||
sanitize (5.1.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.8.0)
|
||||
nokogumbo (~> 2.0)
|
||||
|
@ -228,7 +222,7 @@ GEM
|
|||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sax-machine (1.3.2)
|
||||
sequel (5.22.0)
|
||||
sequel (5.24.0)
|
||||
sequel_pg (1.12.2)
|
||||
pg (>= 0.18.0)
|
||||
sequel (>= 4.38.0)
|
||||
|
@ -256,15 +250,10 @@ GEM
|
|||
sinatra-xsendfile (0.4.2)
|
||||
sinatra (>= 0.9.1)
|
||||
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)
|
||||
faraday (~> 0.13)
|
||||
net-http-persistent (~> 3.0)
|
||||
stripe-ruby-mock (2.5.6)
|
||||
stripe-ruby-mock (2.5.8)
|
||||
dante (>= 0.2.0)
|
||||
multi_json (~> 1.0)
|
||||
stripe (>= 2.0.3)
|
||||
|
@ -280,22 +269,12 @@ GEM
|
|||
thread_safe (0.3.6)
|
||||
tilt (2.0.9)
|
||||
timecop (0.9.1)
|
||||
tins (1.20.3)
|
||||
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)
|
||||
tins (1.21.1)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.6)
|
||||
unicode-display_width (1.6.0)
|
||||
unicode_utils (1.4.0)
|
||||
uuidtools (2.1.5)
|
||||
webmock (3.5.1)
|
||||
addressable (>= 2.3.6)
|
||||
|
@ -304,10 +283,11 @@ GEM
|
|||
websocket-driver (0.7.1)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.4)
|
||||
will_paginate (3.1.7)
|
||||
will_paginate (3.1.8)
|
||||
xmlrpc (0.3.0)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.1.10)
|
||||
zipruby (0.3.6)
|
||||
|
||||
PLATFORMS
|
||||
|
@ -317,10 +297,10 @@ DEPENDENCIES
|
|||
acme-client (= 0.6.3)
|
||||
activesupport
|
||||
addressable
|
||||
apparition
|
||||
base32
|
||||
bcrypt
|
||||
capybara
|
||||
capybara_minitest_spec
|
||||
certified
|
||||
coveralls
|
||||
dav4rack!
|
||||
|
@ -377,7 +357,7 @@ DEPENDENCIES
|
|||
sinatra-flash
|
||||
sinatra-xsendfile
|
||||
stripe (= 4.2.0)
|
||||
stripe-ruby-mock (= 2.5.6)
|
||||
stripe-ruby-mock (= 2.5.8)
|
||||
terrapin
|
||||
thread
|
||||
tilt
|
||||
|
|
|
@ -69,7 +69,7 @@ post '/create' do
|
|||
return {result: 'error'}.to_json
|
||||
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.'
|
||||
return {result: 'error'}.to_json
|
||||
end
|
||||
|
@ -78,6 +78,11 @@ post '/create' do
|
|||
flash[:error] = 'Cannot use a disposable email address.'
|
||||
return {result: 'error'}.to_json
|
||||
end
|
||||
|
||||
if !@site.valid?
|
||||
flash[:error] = @site.errors.first.last.first
|
||||
return {result: 'error'}.to_json
|
||||
end
|
||||
end
|
||||
|
||||
@site.email_confirmed = true if self.class.development?
|
||||
|
|
|
@ -105,7 +105,7 @@ Sequel::Model.plugin :validation_helpers
|
|||
Sequel::Model.plugin :force_encoding, 'UTF-8'
|
||||
Sequel::Model.plugin :defaults_setter
|
||||
Sequel::Model.plugin :create_timestamp
|
||||
Sequel.default_timezone = 'UTC'
|
||||
Sequel.default_timezone = :utc
|
||||
Sequel::Migrator.apply DB, './migrations'
|
||||
|
||||
Stripe.api_key = $config['stripe_api_key']
|
||||
|
|
|
@ -324,7 +324,7 @@ class Site < Sequel::Model
|
|||
return false if ip.blank?
|
||||
return true if Site.where(is_banned: true).
|
||||
where(ip: ip).
|
||||
where(['updated_at > ?', Time.now-BANNED_TIME]).
|
||||
where(['banned_at > ?', Time.now-BANNED_TIME]).
|
||||
first
|
||||
|
||||
return true if BlockedIp[ip]
|
||||
|
|
|
@ -51,4 +51,56 @@ describe '/admin' do
|
|||
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
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
require_relative './environment.rb'
|
||||
|
||||
Capybara.register_driver :poltergeist do |app|
|
||||
Capybara::Poltergeist::Driver.new(app, js_errors: false)
|
||||
end
|
||||
|
||||
describe 'signup' do
|
||||
include Capybara::DSL
|
||||
|
||||
|
@ -17,7 +13,7 @@ describe 'signup' do
|
|||
end
|
||||
|
||||
before do
|
||||
Capybara.default_driver = :poltergeist
|
||||
Capybara.default_driver = :apparition
|
||||
Capybara.reset_sessions!
|
||||
visit '/education'
|
||||
page.must_have_content 'Neocities' # Used to force load wait
|
||||
|
|
|
@ -1,5 +1,30 @@
|
|||
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/dsl'
|
||||
require 'capybara/poltergeist'
|
||||
|
@ -10,3 +35,4 @@ Capybara.app = Sinatra::Application
|
|||
def teardown
|
||||
Capybara.reset_sessions!
|
||||
end
|
||||
=end
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
require_relative './environment.rb'
|
||||
|
||||
Capybara.register_driver :poltergeist do |app|
|
||||
Capybara::Poltergeist::Driver.new(app, js_errors: false)
|
||||
end
|
||||
|
||||
describe 'signup' do
|
||||
include Capybara::DSL
|
||||
|
||||
|
@ -28,10 +24,9 @@ describe 'signup' do
|
|||
end
|
||||
|
||||
before do
|
||||
Capybara.default_driver = :poltergeist
|
||||
Capybara.default_driver = :apparition
|
||||
Capybara.reset_sessions!
|
||||
visit_signup
|
||||
page.must_have_content 'Neocities' # Used to force load wait
|
||||
end
|
||||
|
||||
after do
|
||||
|
@ -65,15 +60,15 @@ describe 'signup' do
|
|||
end
|
||||
|
||||
it 'fails if site with same ip has been banned' do
|
||||
@banned_site = Fabricate :site
|
||||
@banned_site.is_banned = true
|
||||
@banned_site.save_changes
|
||||
|
||||
@banned_site = Fabricate :site, ip: '127.0.0.1'
|
||||
@banned_site.ban!
|
||||
fill_in_valid
|
||||
click_signup_button
|
||||
site = Site[username: @site[:username]]
|
||||
Site[username: @site[:username]].must_be_nil
|
||||
current_path.must_equal '/'
|
||||
page.wont_have_content 'Welcome to Neocities'
|
||||
@banned_site.update ip: nil
|
||||
end
|
||||
|
||||
it 'fails if IP is banned from blocked ips list' do
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
require_relative './environment.rb'
|
||||
|
||||
Capybara.register_driver :poltergeist do |app|
|
||||
Capybara::Poltergeist::Driver.new(app, js_errors: false)
|
||||
end
|
||||
|
||||
describe '/supporter' do
|
||||
include Capybara::DSL
|
||||
|
||||
before do
|
||||
Capybara.default_driver = :poltergeist
|
||||
Capybara.default_driver = :apparition
|
||||
Capybara.reset_sessions!
|
||||
|
||||
@site = Fabricate :site
|
||||
|
@ -33,14 +29,16 @@ describe '/supporter' do
|
|||
|
||||
it 'should work for fresh signup' do
|
||||
visit '/supporter'
|
||||
find('#cc_number', visible: false).set '4242424242424242'
|
||||
find('#cc_exp_month', visible: false).set '01'
|
||||
find('#cc_exp_year', visible: false).set Date.today.next_year.year.to_s[2..3]
|
||||
find('#cc_name', visible: false).set 'Penelope'
|
||||
find('#cc_cvc', visible: false).set '123'
|
||||
find('#stripe_token', visible: false).set @stripe_helper.generate_card_token
|
||||
find('.cc-number input[type=text]').set '4242424242424242'
|
||||
all('.cc-exp input[type=text]').first.set '01'
|
||||
all('.cc-exp input[type=text]').last.set Date.today.next_year.year.to_s[2..3]
|
||||
find('.cc-name').set 'Penelope'
|
||||
all('.flip-tab').first.click
|
||||
find('.cc-cvc').set '123'
|
||||
page.evaluate_script("document.getElementById('stripe_token').value = '#{@stripe_helper.generate_card_token}'")
|
||||
click_link 'Upgrade for $5/mo'
|
||||
page.current_path.must_equal '/supporter/thanks'
|
||||
all('.txt-Center')
|
||||
page.body.must_match /You have become a Neocities Supporter/
|
||||
@site.reload
|
||||
@site.stripe_customer_id.wont_be_nil
|
||||
|
|
|
@ -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
|
|
@ -1,320 +1,329 @@
|
|||
require_relative './environment.rb'
|
||||
require 'rack/test'
|
||||
|
||||
include Rack::Test::Methods
|
||||
describe 'api' do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app
|
||||
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'
|
||||
def app
|
||||
Sinatra::Application
|
||||
end
|
||||
end
|
||||
|
||||
describe 'api list' do
|
||||
it 'returns all files without path' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
get '/api/list'
|
||||
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
|
||||
|
||||
res[:result].must_equal 'success'
|
||||
res[:files].length.must_equal @site.site_files.length
|
||||
def site_file_exists?(file)
|
||||
File.exist?(@site.files_path(file))
|
||||
end
|
||||
|
||||
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]
|
||||
def res
|
||||
JSON.parse last_response.body, symbolize_names: true
|
||||
end
|
||||
|
||||
describe '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
|
||||
|
||||
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 []
|
||||
describe 'list' do
|
||||
it 'returns all files without path' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
get '/api/list'
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
describe 'info' do
|
||||
it 'fails for no input' do
|
||||
get '/api/info'
|
||||
res[:error_type] = 'missing_sitename'
|
||||
end
|
||||
|
||||
describe 'api info' do
|
||||
it 'fails for no input' do
|
||||
get '/api/info'
|
||||
res[:error_type] = 'missing_sitename'
|
||||
it 'fails for banned sites' do
|
||||
create_site
|
||||
@site.update is_banned: true
|
||||
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
|
||||
|
||||
it 'fails for banned sites' do
|
||||
create_site
|
||||
@site.update is_banned: true
|
||||
get '/api/info', sitename: @site.username
|
||||
res[:error_type].must_equal 'site_not_found'
|
||||
@site.reload.api_calls.must_equal 0
|
||||
describe 'delete' do
|
||||
it 'fails with no or bad auth' do
|
||||
post '/api/delete', filenames: ['hi.html']
|
||||
res[:error_type].must_equal 'invalid_auth'
|
||||
create_site
|
||||
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
|
||||
|
||||
it 'fails for nonexistent site' do
|
||||
get '/api/info', sitename: 'notexist'
|
||||
res[:error_type].must_equal 'site_not_found'
|
||||
describe '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
|
||||
|
||||
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
|
||||
describe '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
|
||||
|
||||
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
|
||||
describe '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
|
||||
|
||||
it 'fails for bad auth' do
|
||||
basic_authorize 'derp', 'fake'
|
||||
get '/api/info'
|
||||
res[:error_type].must_equal 'invalid_auth'
|
||||
end
|
||||
describe '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 'succeeds for api auth' do
|
||||
create_site
|
||||
@site.update hits: 12345
|
||||
basic_authorize @user, @pass
|
||||
get '/api/info'
|
||||
res[:info][:hits] == 12345
|
||||
end
|
||||
end
|
||||
it 'fails for bad auth' do
|
||||
basic_authorize 'username', 'password'
|
||||
post '/api/upload'
|
||||
res[:error_type].must_equal 'invalid_auth'
|
||||
end
|
||||
|
||||
describe 'api delete' do
|
||||
it 'fails with no or bad auth' do
|
||||
post '/api/delete', filenames: ['hi.html']
|
||||
res[:error_type].must_equal 'invalid_auth'
|
||||
create_site
|
||||
basic_authorize 'derp', 'fake'
|
||||
post '/api/delete', filenames: ['hi.html']
|
||||
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 'fails with missing filename argument' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/delete'
|
||||
res[:error_type].must_equal 'missing_filenames'
|
||||
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 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
|
||||
|
||||
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
|
||||
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
|
||||
# Getting too slow to run this test
|
||||
|
@ -332,125 +341,118 @@ describe 'api upload' do
|
|||
end
|
||||
=end
|
||||
|
||||
it 'resists directory traversal attack' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', {
|
||||
'../lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
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
|
||||
@site.reload.api_calls.must_equal 1
|
||||
end
|
||||
it 'resists directory traversal attack' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', {
|
||||
'../lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
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
|
||||
@site.reload.api_calls.must_equal 1
|
||||
end
|
||||
|
||||
it 'scrubs root path slash' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', {
|
||||
'/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
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
|
||||
end
|
||||
it 'scrubs root path slash' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', {
|
||||
'/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
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
|
||||
end
|
||||
|
||||
it 'fails for missing file name' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', {
|
||||
'/' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
res[:error_type].must_equal 'invalid_file_type'
|
||||
end
|
||||
it 'fails for missing file name' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', {
|
||||
'/' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
res[:error_type].must_equal 'invalid_file_type'
|
||||
end
|
||||
|
||||
it 'fails for file with no extension' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', {
|
||||
'derpie' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
res[:error_type].must_equal 'invalid_file_type'
|
||||
end
|
||||
it 'fails for file with no extension' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', {
|
||||
'derpie' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
res[:error_type].must_equal 'invalid_file_type'
|
||||
end
|
||||
|
||||
it 'creates path for file uploads' do
|
||||
create_site
|
||||
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 {
|
||||
it 'creates path for file uploads' do
|
||||
create_site
|
||||
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
|
||||
|
||||
@site.reload.api_calls.must_equal 2
|
||||
end
|
||||
it 'records api calls that require auth' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
|
||||
it 'fails for invalid files' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
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
|
||||
2.times {
|
||||
post '/api/upload', {
|
||||
'derpie/derpingtons/lol.jpg' => Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
}
|
||||
}
|
||||
|
||||
it 'succeeds with single file' do
|
||||
create_site
|
||||
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
|
||||
@site.reload.api_calls.must_equal 2
|
||||
end
|
||||
|
||||
it 'succeeds with two files' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
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 for invalid files' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
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 'fails with unwhitelisted file' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
post '/api/upload', 'flowercrime.wav' => Rack::Test::UploadedFile.new('./tests/files/flowercrime.wav', 'audio/x-wav')
|
||||
res[:result].must_equal 'error'
|
||||
res[:error_type].must_equal 'invalid_file_type'
|
||||
site_file_exists?('flowercrime.wav').must_equal false
|
||||
end
|
||||
it 'succeeds with single file' do
|
||||
create_site
|
||||
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 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
|
||||
it 'succeeds with two files' do
|
||||
create_site
|
||||
basic_authorize @user, @pass
|
||||
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
|
||||
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
|
||||
res[:result].must_equal 'error'
|
||||
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
|
||||
|
||||
def site_file_exists?(file)
|
||||
File.exist?(@site.files_path(file))
|
||||
end
|
||||
|
||||
def res
|
||||
JSON.parse last_response.body, symbolize_names: true
|
||||
end
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
require_relative './environment.rb'
|
||||
|
||||
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
|
||||
require 'rack/test'
|
||||
|
||||
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
|
||||
@site = Fabricate :site
|
||||
ThumbnailWorker.jobs.clear
|
||||
|
@ -32,9 +33,10 @@ describe 'site_files' do
|
|||
uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
upload 'files[]' => uploaded_file
|
||||
|
||||
@site.site_files.last.path.must_equal 'test.jpg'
|
||||
@site.site_files.last.rename('derp.jpg')
|
||||
@site.site_files.last.path.must_equal('derp.jpg')
|
||||
testfile = @site.site_files_dataset.where(path: 'test.jpg').first
|
||||
testfile.wont_equal nil
|
||||
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'
|
||||
File.exist?(@site.files_path('derp.jpg')).must_equal true
|
||||
end
|
||||
|
@ -43,21 +45,25 @@ describe 'site_files' do
|
|||
uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
upload 'files[]' => uploaded_file
|
||||
|
||||
@site.site_files.last.path.must_equal 'test.jpg'
|
||||
res = @site.site_files.last.rename('dasharezone.exe')
|
||||
testfile = @site.site_files_dataset.where(path: 'test.jpg').first
|
||||
res = testfile.rename('dasharezone.exe')
|
||||
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
|
||||
|
||||
|
||||
it 'works for directory' do
|
||||
@site.create_directory 'dirone'
|
||||
@site.site_files.last.path.must_equal 'dirone'
|
||||
@site.site_files.last.is_directory.must_equal true
|
||||
res = @site.site_files.last.rename('dasharezone')
|
||||
@site.site_files.select {|sf| sf.path == 'dirone'}.length.must_equal 1
|
||||
|
||||
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]
|
||||
@site.site_files.last.path.must_equal('dasharezone')
|
||||
@site.site_files.last.is_directory.must_equal true
|
||||
dasharezone = @site.site_files_dataset.where(path: 'dasharezone').first
|
||||
dasharezone.wont_equal nil
|
||||
dasharezone.is_directory.must_equal true
|
||||
|
||||
PurgeCacheWorker.jobs.first['args'].last.must_equal 'dirone'
|
||||
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')
|
||||
)
|
||||
|
||||
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']
|
||||
end
|
||||
|
||||
it 'doesnt wipe out existing dir' do
|
||||
@site.create_directory 'dirone'
|
||||
@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']
|
||||
end
|
||||
|
||||
|
@ -110,17 +116,17 @@ describe 'site_files' do
|
|||
it 'works with unicode characters' do
|
||||
uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
upload 'files[]' => uploaded_file
|
||||
@site.site_files.last.rename("HELL💩؋.jpg")
|
||||
@site.site_files.last.path.must_equal "HELL💩؋.jpg"
|
||||
@site.site_files_dataset.where(path: 'test.jpg').first.rename("HELL💩؋.jpg")
|
||||
@site.site_files_dataset.where(path: "HELL💩؋.jpg").first.wont_equal nil
|
||||
end
|
||||
|
||||
it 'scrubs weird carriage return shit characters' do
|
||||
uploaded_file = Rack::Test::UploadedFile.new('./tests/files/test.jpg', 'image/jpeg')
|
||||
upload 'files[]' => uploaded_file
|
||||
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
|
||||
@site.site_files.last.path.must_equal "test.jpg"
|
||||
@site.site_files_dataset.where(path: 'test.jpg').first.wont_equal nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
require_relative './environment.rb'
|
||||
require 'rack/test'
|
||||
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app
|
||||
Sinatra::Application
|
||||
end
|
||||
|
||||
describe 'tipping' do
|
||||
include Rack::Test::Methods
|
||||
|
||||
def app
|
||||
Sinatra::Application
|
||||
end
|
||||
|
||||
before do
|
||||
EmailWorker.jobs.clear
|
||||
@site = Fabricate :site
|
||||
|
|
Loading…
Add table
Reference in a new issue