diff --git a/.gitignore b/.gitignore index 6ad15e95..e0293705 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .tool-versions .DS_Store .idea +.logs/ diff --git a/.rspec b/.rspec new file mode 100644 index 00000000..7a2cc1a6 --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--require spec_helper +--format documentation +--color diff --git a/Gemfile b/Gemfile index cb54e8c6..dafe3fac 100644 --- a/Gemfile +++ b/Gemfile @@ -12,3 +12,8 @@ gem 'rack-ssl-enforcer' gem "rackup", "~> 2.2" gem 'rexml' gem 'rtl' + +group :test do + gem 'rspec', '~> 3.13' + gem 'rack-test', '~> 2.1' +end diff --git a/Gemfile.lock b/Gemfile.lock index b2cb049e..28e55ad8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,6 +5,7 @@ GEM builder (3.3.0) concurrent-ruby (1.2.2) daemons (1.4.1) + diff-lcs (1.6.2) eventmachine (1.2.7) i18n (1.14.1) concurrent-ruby (~> 1.0) @@ -24,9 +25,24 @@ GEM base64 (>= 0.1.0) rack (>= 3.0.0) rack-ssl-enforcer (0.2.9) + rack-test (2.2.0) + rack (>= 1.3) rackup (2.2.1) rack (>= 3) rexml (3.4.1) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.8) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) rtl (0.6.0) ruby2_keywords (0.0.5) sinatra (4.1.1) @@ -52,8 +68,10 @@ DEPENDENCIES maruku puma (~> 6.6) rack-ssl-enforcer + rack-test (~> 2.1) rackup (~> 2.2) rexml + rspec (~> 3.13) rtl sinatra (~> 4.1.0) thin diff --git a/spec/smoke_spec.rb b/spec/smoke_spec.rb new file mode 100644 index 00000000..3e6cac18 --- /dev/null +++ b/spec/smoke_spec.rb @@ -0,0 +1,68 @@ +# 12factor.net smoke test -- verifies the Sinatra app boots and serves +# its core routes without raising. Layer L4 of the verification ladder. +# +# Asserts (boot-only; CVE-floor regressions belong in L7, separately): +# 1. Loading web.rb does not raise (Sinatra/rack/i18n init succeeds) +# 2. GET / returns 200 with HTML body containing the Twelve-Factor brand +# 3. GET /ja/ returns 200 (localized path; exercises i18n routing) +# 4. The app responds to a non-existent path with 404 (not 500) +# 5. Rack and Sinatra both load at meaningful versions (sanity check +# that no-op gem bumps haven't accidentally downgraded the stack) +# +# These assertions catch the regressions most likely from a Ruby gem +# bump (rack, sinatra, rack-session, i18n): require-time errors, +# middleware-stack misconfig, route compile failures. CVE-specific +# version floors are a separate concern (per-fix L7 regression tests +# documented in the relevant PRs). + +require 'rack' + +RSpec.describe '12factor smoke -- Sinatra app boot' do + describe 'app loads' do + it 'web.rb required without error during spec_helper load' do + # If we got here, spec_helper loaded web.rb successfully. + expect(defined?(Sinatra::Application)).to eq('constant') + end + end + + describe 'GET /' do + before { get '/' } + + it 'returns HTTP 200' do + expect(last_response.status).to eq(200) + end + + it 'returns an HTML body referencing Twelve-Factor' do + expect(last_response.body).to include('Twelve-Factor') + end + end + + describe 'GET /ja/' do + before { get '/ja/' } + + it 'returns HTTP 200 (localized path -- i18n routing alive)' do + expect(last_response.status).to eq(200) + end + end + + describe 'GET /__nonexistent__' do + before { get '/__nonexistent__' } + + it 'returns 404, not 500' do + expect(last_response.status).to eq(404) + end + end + + describe 'core stack loads at meaningful versions' do + it 'Rack is at version 3.x or higher' do + # Sanity check, not a CVE floor. CVE-floor assertions belong in + # L7 regression tests on the specific fix PR, not the smoke. + expect(Gem::Version.new(Rack.release)).to be >= Gem::Version.new('3.0.0') + end + + it 'Sinatra is loaded' do + expect(defined?(Sinatra::VERSION)).to eq('constant') + expect(Gem::Version.new(Sinatra::VERSION)).to be >= Gem::Version.new('3.0.0') + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..5807c648 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,22 @@ +# Minimal RSpec setup for the 12factor Sinatra smoke suite. +# Layer L4 (smoke) of the workspace verification ladder. +# See heroku/3pp-grackle/docs/verification-specs/12factor.md. + +ENV['RACK_ENV'] ||= 'test' + +require 'rack/test' +require_relative '../web.rb' + +RSpec.configure do |config| + config.include Rack::Test::Methods + config.expect_with :rspec do |c| + c.syntax = :expect + end + config.disable_monkey_patching! +end + +# Sinatra's classic style: the entire `Sinatra::Application` IS the app. +# Rack::Test::Methods uses `app` as the rack app under test. +def app + Sinatra::Application +end