diff --git a/.rubocop.yml b/.rubocop.yml index f88aedf2..b17a921b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,7 @@ AllCops: SuggestExtensions: false TargetRubyVersion: 2.7 Exclude: - - 'spec/rails/**/*' + - 'spec/apps/**/*' - 'vendor/**/*' Style/TrailingCommaInHashLiteral: diff --git a/Gemfile b/Gemfile index edcdb401..4fad7e7f 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,16 @@ source 'https://rubygems.org' gemspec gem 'rails', ENV['RAILS_VERSION'] || '6.0.3.7' + +if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') + gem 'hanami', ENV['HANAMI_VERSION'] || '2.1.0' + gem 'hanami-controller', ENV['HANAMI_VERSION'] || '2.1.0' + gem 'hanami-router', ENV['HANAMI_VERSION'] || '2.1.0' +end + gem 'roda' + +gem 'rails-dom-testing', '~> 2.2' gem 'rspec-rails' group :test do diff --git a/lib/rspec/openapi/record_builder.rb b/lib/rspec/openapi/record_builder.rb index 81e11ef1..31e7adff 100644 --- a/lib/rspec/openapi/record_builder.rb +++ b/lib/rspec/openapi/record_builder.rb @@ -55,7 +55,10 @@ def safe_parse_body(response, media_type) def extract_headers(request, response) request_headers = RSpec::OpenAPI.request_headers.each_with_object([]) do |header, headers_arr| header_key = header.gsub('-', '_').upcase.to_sym - header_value = request.get_header(['HTTP', header_key].join('_')) || request.get_header(header_key) + + header_value = request.get_header(['HTTP', header_key].join('_')) || + request.get_header(header_key) || + request.get_header(header_key.to_s) headers_arr << [header, header_value] if header_value end response_headers = RSpec::OpenAPI.response_headers.each_with_object([]) do |header, headers_arr| diff --git a/lib/rspec/openapi/schema_merger.rb b/lib/rspec/openapi/schema_merger.rb index 1dcfaeb2..b4119553 100644 --- a/lib/rspec/openapi/schema_merger.rb +++ b/lib/rspec/openapi/schema_merger.rb @@ -53,7 +53,8 @@ def merge_arrays(base, key, value) def merge_parameters(base, key, value) all_parameters = value | base[key] - unique_base_parameters = base[key].index_by { |parameter| [parameter[:name], parameter[:in]] } + unique_base_parameters = build_unique_params(base, key) + all_parameters = all_parameters.map do |parameter| base_parameter = unique_base_parameters[[parameter[:name], parameter[:in]]] || {} base_parameter ? base_parameter.merge(parameter) : parameter @@ -63,6 +64,12 @@ def merge_parameters(base, key, value) base[key] = all_parameters end + def build_unique_params(base, key) + base[key].each_with_object({}) do |parameter, hash| + hash[[parameter[:name], parameter[:in]]] = parameter + end + end + SIMILARITY_THRESHOLD = 0.5 def merge_closest_match!(options, spec) diff --git a/rspec-openapi.gemspec b/rspec-openapi.gemspec index f51d9278..05a03a6c 100644 --- a/rspec-openapi.gemspec +++ b/rspec-openapi.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'actionpack', '>= 5.2.0' + spec.add_dependency 'rails-dom-testing' spec.add_dependency 'rspec-core' spec.metadata['rubygems_mfa_required'] = 'true' end diff --git a/spec/apps/hanami/.gitignore b/spec/apps/hanami/.gitignore new file mode 100644 index 00000000..f1112aba --- /dev/null +++ b/spec/apps/hanami/.gitignore @@ -0,0 +1,2 @@ +.env +log/* diff --git a/spec/apps/hanami/Procfile.dev b/spec/apps/hanami/Procfile.dev new file mode 100644 index 00000000..9c043624 --- /dev/null +++ b/spec/apps/hanami/Procfile.dev @@ -0,0 +1 @@ +web: bundle exec hanami server diff --git a/spec/apps/hanami/Rakefile b/spec/apps/hanami/Rakefile new file mode 100644 index 00000000..c526f383 --- /dev/null +++ b/spec/apps/hanami/Rakefile @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require "hanami/rake_tasks" diff --git a/spec/apps/hanami/app/action.rb b/spec/apps/hanami/app/action.rb new file mode 100644 index 00000000..ec957c67 --- /dev/null +++ b/spec/apps/hanami/app/action.rb @@ -0,0 +1,8 @@ +# auto_register: false +# frozen_string_literal: true + +require 'hanami/action' + +class HanamiTest::Action < Hanami::Action + class RecordNotFound < StandardError; end +end diff --git a/spec/rails/app/assets/images/.keep b/spec/apps/hanami/app/actions/.keep similarity index 100% rename from spec/rails/app/assets/images/.keep rename to spec/apps/hanami/app/actions/.keep diff --git a/spec/apps/hanami/app/actions/extensions/create.rb b/spec/apps/hanami/app/actions/extensions/create.rb new file mode 100644 index 00000000..d74aed17 --- /dev/null +++ b/spec/apps/hanami/app/actions/extensions/create.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Extensions + class Create < HanamiTest::Action + def handle(_request, response) + response.body = [{ name: 'my-ext-1' }].to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/extensions/index.rb b/spec/apps/hanami/app/actions/extensions/index.rb new file mode 100644 index 00000000..be1a4a0b --- /dev/null +++ b/spec/apps/hanami/app/actions/extensions/index.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Extensions + class Index < HanamiTest::Action + def handle(_request, response) + response.body = { message: 'created' }.to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/images/index.rb b/spec/apps/hanami/app/actions/images/index.rb new file mode 100644 index 00000000..0e498e1d --- /dev/null +++ b/spec/apps/hanami/app/actions/images/index.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Images + class Index < HanamiTest::Action + format :json + + def handle(_request, response) + list = [ + { + name: 'file.png', + tags: [], # Keep this empty to check empty array is accepted + }, + ] + + response.body = list.to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/images/show.rb b/spec/apps/hanami/app/actions/images/show.rb new file mode 100644 index 00000000..3e1f13eb --- /dev/null +++ b/spec/apps/hanami/app/actions/images/show.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Images + class Show < HanamiTest::Action + def handle(_request, response) + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjPQBoAAAAASUVORK5CYII=' + .unpack('m').first + + response.format = :png + response.body = png + response.headers.merge!( + { + 'Content-Type' => 'image/png', + 'Content-Disposition' => 'inline', + } + ) + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/images/upload.rb b/spec/apps/hanami/app/actions/images/upload.rb new file mode 100644 index 00000000..9b04f68f --- /dev/null +++ b/spec/apps/hanami/app/actions/images/upload.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Images + class Upload < HanamiTest::Action + # format :form + + def handle(_request, response) + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjPQBoAAAAASUVORK5CYII=' + .unpack('m').first + + response.format = :png + response.body = png + response.headers.merge!( + { + 'Content-Type' => 'image/png', + 'Content-Disposition' => 'inline', + } + ) + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/images/upload_multiple.rb b/spec/apps/hanami/app/actions/images/upload_multiple.rb new file mode 100644 index 00000000..f468a843 --- /dev/null +++ b/spec/apps/hanami/app/actions/images/upload_multiple.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Images + class UploadMultiple < HanamiTest::Action + # format :multipart + + def handle(_request, response) + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjPQBoAAAAASUVORK5CYII=' + .unpack('m').first + + response.format = :png + response.body = png + response.headers.merge!( + { + 'Content-Type' => 'image/png', + 'Content-Disposition' => 'inline', + } + ) + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/images/upload_multiple_nested.rb b/spec/apps/hanami/app/actions/images/upload_multiple_nested.rb new file mode 100644 index 00000000..365f7709 --- /dev/null +++ b/spec/apps/hanami/app/actions/images/upload_multiple_nested.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Images + class UploadMultipleNested < HanamiTest::Action + def handle(_request, response) + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjPQBoAAAAASUVORK5CYII=' + .unpack('m').first + + response.format = :png + response.body = png + response.headers.merge!( + { + 'Content-Type' => 'image/png', + 'Content-Disposition' => 'inline', + } + ) + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/images/upload_nested.rb b/spec/apps/hanami/app/actions/images/upload_nested.rb new file mode 100644 index 00000000..480f0bbf --- /dev/null +++ b/spec/apps/hanami/app/actions/images/upload_nested.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Images + class UploadNested < HanamiTest::Action + def handle(_request, response) + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjPQBoAAAAASUVORK5CYII=' + .unpack('m').first + + response.format = :png + response.body = png + response.headers.merge!( + { + 'Content-Type' => 'image/png', + 'Content-Disposition' => 'inline', + } + ) + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/secret_items/index.rb b/spec/apps/hanami/app/actions/secret_items/index.rb new file mode 100644 index 00000000..b2166e35 --- /dev/null +++ b/spec/apps/hanami/app/actions/secret_items/index.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module SecretItems + class Index < HanamiTest::Action + format :json + + def handle(_request, response) + response.body = { items: ['secrets'] }.to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/tables/create.rb b/spec/apps/hanami/app/actions/tables/create.rb new file mode 100644 index 00000000..a20939aa --- /dev/null +++ b/spec/apps/hanami/app/actions/tables/create.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Tables + class Create < TableAction + format :json + + def handle(request, response) + if request.params[:name].blank? || request.params[:name] == 'some_invalid_name' + response.status = 422 + response.body = { error: 'invalid name parameter' }.to_json + else + response.status = 201 + response.body = find_table.to_json + end + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/tables/destroy.rb b/spec/apps/hanami/app/actions/tables/destroy.rb new file mode 100644 index 00000000..2d4d04bf --- /dev/null +++ b/spec/apps/hanami/app/actions/tables/destroy.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Tables + class Destroy < TableAction + def handle(request, response) + response.format = :json + if request.params[:no_content] + response.status = 202 + else + response.body = find_table(request.params[:id]).to_json + end + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/tables/index.rb b/spec/apps/hanami/app/actions/tables/index.rb new file mode 100644 index 00000000..65fe6f70 --- /dev/null +++ b/spec/apps/hanami/app/actions/tables/index.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Tables + class Index < TableAction + def handle(request, response) + response.headers['X-Cursor'] = 100 + + response.format = :json + + response.body = if request.params[:show_columns] + [find_table('42')].to_json + else + [find_table].to_json + end + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/tables/show.rb b/spec/apps/hanami/app/actions/tables/show.rb new file mode 100644 index 00000000..88bdb211 --- /dev/null +++ b/spec/apps/hanami/app/actions/tables/show.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Tables + class Show < TableAction + format :json + + def handle(request, response) + response.body = find_table(request.params[:id]).to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/tables/table_action.rb b/spec/apps/hanami/app/actions/tables/table_action.rb new file mode 100644 index 00000000..ed97748a --- /dev/null +++ b/spec/apps/hanami/app/actions/tables/table_action.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Tables + class TableAction < HanamiTest::Action + APIKEY = 'k0kubun'.freeze + + include TableRepository + + handle_exception RecordNotFound => :handle_not_fount_error + + before :authenticate + + private + + def handle_not_fount_error(_request, _response, _exception) + halt 404, { message: 'not found' }.to_json + end + + def authenticate(request, response) + return unless request.get_header('AUTHORIZATION') != APIKEY + + response.format = :json + halt 401, { message: 'Unauthorized' }.to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/tables/table_repository.rb b/spec/apps/hanami/app/actions/tables/table_repository.rb new file mode 100644 index 00000000..e4bf7f54 --- /dev/null +++ b/spec/apps/hanami/app/actions/tables/table_repository.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Tables + module TableRepository + class RecordNotFound < StandardError; end + + def find_table(id = nil) + time = Time.parse('2020-07-17 00:00:00') + case id + when '1', nil + { + id: 1, + name: 'access', + description: 'logs', + database: { + id: 2, + name: 'production', + }, + null_sample: nil, + storage_size: 12.3, + created_at: time.iso8601, + updated_at: time.iso8601, + } + when '42' + { + id: 42, + name: 'access', + description: 'logs', + database: { + id: 4242, + name: 'production', + }, + columns: [ + { name: 'id', column_type: 'integer' }, + { name: 'description', column_type: 'varchar' }, + ], + null_sample: nil, + storage_size: 12.3, + created_at: time.iso8601, + updated_at: time.iso8601, + } + else + raise RecordNotFound + end + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/tables/update.rb b/spec/apps/hanami/app/actions/tables/update.rb new file mode 100644 index 00000000..2638b9a3 --- /dev/null +++ b/spec/apps/hanami/app/actions/tables/update.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Tables + class Update < TableAction + def handle(request, response) + response.format = :json + response.body = find_table(request.params[:id]).to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/users/active.rb b/spec/apps/hanami/app/actions/users/active.rb new file mode 100644 index 00000000..70c8789f --- /dev/null +++ b/spec/apps/hanami/app/actions/users/active.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Users + class Active < UserAction + format :json + + def handle(request, response) + response.body = find_user(request.params[:id]).present?.to_json # present not exist in hanami + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/users/create.rb b/spec/apps/hanami/app/actions/users/create.rb new file mode 100644 index 00000000..189ce0ea --- /dev/null +++ b/spec/apps/hanami/app/actions/users/create.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Users + class Create < UserAction + format :json + + def handle(_request, response) + res = { + name: params[:name], + relations: { + avatar: { + url: params[:avatar_url] || 'https://example.com/avatar.png', + }, + pets: params[:pets] || [], + }, + } + + response.status = 201 + response.body = res.to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/users/show.rb b/spec/apps/hanami/app/actions/users/show.rb new file mode 100644 index 00000000..bc136ecd --- /dev/null +++ b/spec/apps/hanami/app/actions/users/show.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Users + class Show < UserAction + format :json + + def handle(request, response) + response.body = find_user(request.params[:id]).to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/users/user_action.rb b/spec/apps/hanami/app/actions/users/user_action.rb new file mode 100644 index 00000000..b757e707 --- /dev/null +++ b/spec/apps/hanami/app/actions/users/user_action.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Tables + class UserAction < HanamiTest::Action + include UserRepository + + handle_exception RecordNotFound => :handle_not_fount_error + + before :authenticate + + private + + def handle_not_fount_error(_request, response, _exception) + response.status = 404 + response.body = { message: 'not found' }.to_json + end + end + end + end +end diff --git a/spec/apps/hanami/app/actions/users/user_repository.rb b/spec/apps/hanami/app/actions/users/user_repository.rb new file mode 100644 index 00000000..f2228a5b --- /dev/null +++ b/spec/apps/hanami/app/actions/users/user_repository.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module HanamiTest + module Actions + module Users + module UserRepository + class RecordNotFound < StandardError; end + + def find_user(id = nil) + case id + when '1', nil + { + name: 'John Doe', + relations: { + avatar: { + url: 'https://example.com/avatar.jpg', + }, + pets: [ + { name: 'doge', age: 8 }, + ], + }, + } + when '2' + {} + else + raise RecordNotFound + end + end + end + end + end +end diff --git a/spec/apps/hanami/bin/dev b/spec/apps/hanami/bin/dev new file mode 100755 index 00000000..74ade166 --- /dev/null +++ b/spec/apps/hanami/bin/dev @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +if ! gem list foreman -i --silent; then + echo "Installing foreman..." + gem install foreman +fi + +exec foreman start -f Procfile.dev "$@" diff --git a/spec/apps/hanami/config.ru b/spec/apps/hanami/config.ru new file mode 100644 index 00000000..83dc7857 --- /dev/null +++ b/spec/apps/hanami/config.ru @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require 'hanami/boot' + +run Hanami.app diff --git a/spec/apps/hanami/config/app.rb b/spec/apps/hanami/config/app.rb new file mode 100644 index 00000000..102b93c9 --- /dev/null +++ b/spec/apps/hanami/config/app.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'hanami' + +module HanamiTest + class App < Hanami::App + config.middleware.use :body_parser, :json + end +end diff --git a/spec/apps/hanami/config/puma.rb b/spec/apps/hanami/config/puma.rb new file mode 100644 index 00000000..df893721 --- /dev/null +++ b/spec/apps/hanami/config/puma.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# +# Environment and port +# +port ENV.fetch('HANAMI_PORT', 2300) +environment ENV.fetch('HANAMI_ENV', 'development') + +# +# Threads within each Puma/Ruby process (aka worker) +# + +# Configure the minimum and maximum number of threads to use to answer requests. +max_threads_count = ENV.fetch('HANAMI_MAX_THREADS', 5) +min_threads_count = ENV.fetch('HANAMI_MIN_THREADS') { max_threads_count } + +threads min_threads_count, max_threads_count + +# +# Workers (aka Puma/Ruby processes) +# + +puma_concurrency = Integer(ENV.fetch('HANAMI_WEB_CONCURRENCY', 0)) +puma_cluster_mode = puma_concurrency > 1 + +# How many worker (Puma/Ruby) processes to run. +# Typically this is set to the number of available cores. +workers puma_concurrency + +# +# Cluster mode (aka multiple workers) +# + +if puma_cluster_mode + # Preload the application before starting the workers. Only in cluster mode. + preload_app! + + # Code to run immediately before master process forks workers (once on boot). + # + # These hooks can block if necessary to wait for background operations unknown + # to puma to finish before the process terminates. This can be used to close + # any connections to remote servers (database, redis, …) that were opened when + # preloading the code. + before_fork do + Hanami.shutdown + end +end diff --git a/spec/apps/hanami/config/routes.rb b/spec/apps/hanami/config/routes.rb new file mode 100644 index 00000000..6d6caf52 --- /dev/null +++ b/spec/apps/hanami/config/routes.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module HanamiTest + class Routes < Hanami::Routes + # Add your routes here. See https://guides.hanamirb.org/routing/overview/ for details. + get '/secret_items', to: 'secret_items.index' + + get '/tables', to: 'tables.index' + get '/tables/:id', to: 'tables.show' + post '/tables', to: 'tables.create' + patch '/tables/:id', to: 'tables.update' + delete '/tables/:id', to: 'tables.destroy' + + get '/images', to: 'images.index' + get '/images/:id', to: 'images.show' + post '/images/upload', to: 'images.upload' + post '/images/upload_nested', to: 'images.upload_nested' + post '/images/upload_multiple', to: 'images.upload_multiple' + post '/images/upload_multiple_nested', to: 'images.upload_multiple_nested' + + post '/users', to: 'users.create' + get '/users/:id', to: 'users.show' + get '/users/active', to: 'users.active' + + get '/test_block', to: ->(_env) { [200, { 'Content-Type' => 'text/plain' }, ['A TEST']] } + + slice :my_engine, at: '/my_engine' do + get '/test', to: ->(_env) { [200, { 'Content-Type' => 'text/plain' }, ['ANOTHER TEST']] } + get '/eng/example', to: 'eng.example' + end + + scope 'admin' do + scope 'masters' do + get '/extensions', to: 'extensions.index' + post '/extensions', to: 'extensions.create' + end + end + end +end diff --git a/spec/apps/hanami/config/settings.rb b/spec/apps/hanami/config/settings.rb new file mode 100644 index 00000000..126f822f --- /dev/null +++ b/spec/apps/hanami/config/settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module HanamiTest + class Settings < Hanami::Settings + # Define your app settings here, for example: + # + # setting :my_flag, default: false, constructor: Types::Params::Bool + end +end diff --git a/spec/apps/hanami/doc/openapi.json b/spec/apps/hanami/doc/openapi.json new file mode 100644 index 00000000..9d628946 --- /dev/null +++ b/spec/apps/hanami/doc/openapi.json @@ -0,0 +1,1007 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "OpenAPI Documentation", + "version": "1.0.0", + "description": "My beautiful hanami API", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers": [ + { + "url": "http://localhost:3000" + } + ], + "paths": { + "/images": { + "get": { + "summary": "GET /images", + "responses": { + "200": { + "description": "can return an object with an attribute of empty array", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + } + } + }, + "required": [ + "name", + "tags" + ] + } + }, + "example": [ + { + "name": "file.png", + "tags": [ + + ] + } + ] + } + } + } + } + } + }, + "/images/1": { + "get": { + "summary": "GET /images/1", + "responses": { + "200": { + "description": "returns a image payload", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/upload": { + "post": { + "summary": "POST /images/upload", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "image" + ] + }, + "example": { + "image": "test.png" + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/upload_multiple": { + "post": { + "summary": "POST /images/upload_multiple", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "images": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + } + } + }, + "required": [ + "images" + ] + }, + "example": { + "images": [ + "test.png", + "test.png" + ] + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload multiple", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/upload_multiple_nested": { + "post": { + "summary": "POST /images/upload_multiple_nested", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + } + }, + "required": [ + "image" + ] + } + } + }, + "required": [ + "images" + ] + }, + "example": { + "images": [ + { + "image": "test.png" + }, + { + "image": "test.png" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload multiple nested", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/images/upload_nested": { + "post": { + "summary": "POST /images/upload_nested", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "nested_image": { + "type": "object", + "properties": { + "image": { + "type": "string", + "format": "binary" + }, + "caption": { + "type": "string" + } + }, + "required": [ + "image", + "caption" + ] + } + }, + "required": [ + "nested_image" + ] + }, + "example": { + "nested_image": { + "image": "test.png", + "caption": "Some caption" + } + } + } + } + }, + "responses": { + "200": { + "description": "returns a image payload with upload nested", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, + "/my_engine/eng/example": { + "get": { + "summary": "GET /my_engine/eng/example", + "responses": { + "200": { + "description": "returns the block content", + "content": { + "text/plain": { + "schema": { + "type": "string" + }, + "example": "AN ENGINE TEST" + } + } + } + } + } + }, + "/my_engine/test": { + "get": { + "summary": "GET /my_engine/test", + "responses": { + "200": { + "description": "returns some content from the engine", + "content": { + "text/plain": { + "schema": { + "type": "string" + }, + "example": "ANOTHER TEST" + } + } + } + } + } + }, + "/secret_items": { + "get": { + "summary": "GET /secret_items", + "security": [ + { + "SecretApiKeyAuth": [ + + ] + } + ], + "responses": { + "200": { + "description": "authorizes with secret key", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "items" + ] + }, + "example": { + "items": [ + "secrets" + ] + } + } + } + } + } + } + }, + "/tables": { + "get": { + "summary": "GET /tables", + "responses": { + "200": { + "description": "with different deep query parameters", + "headers": { + "X-Cursor": { + "schema": { + "type": "integer" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "database": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + }, + "null_sample": { + "nullable": true + }, + "storage_size": { + "type": "number", + "format": "float" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "database", + "null_sample", + "storage_size", + "created_at", + "updated_at" + ] + } + }, + "example": [ + { + "id": 1, + "name": "access", + "description": "logs", + "database": { + "id": 2, + "name": "production" + }, + "null_sample": null, + "storage_size": 12.3, + "created_at": "2020-07-17T00:00:00+00:00", + "updated_at": "2020-07-17T00:00:00+00:00" + } + ] + } + } + }, + "401": { + "description": "does not return tables if unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + }, + "example": { + "message": "Unauthorized" + } + } + } + } + }, + "parameters": [ + { + "name": "X-Authorization-Token", + "in": "header", + "required": true, + "schema": { + "type": "string" + }, + "example": "token" + }, + { + "name": "filter[name]", + "in": "query", + "required": false, + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "example": { + "name": "Example Table" + } + }, + { + "name": "filter[price]", + "in": "query", + "required": false, + "schema": { + "type": "object", + "properties": { + "price": { + "type": "string" + } + }, + "required": [ + "price" + ] + }, + "example": { + "price": "0" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "example": 1 + }, + { + "name": "per", + "in": "query", + "required": false, + "schema": { + "type": "integer" + }, + "example": 10 + } + ] + }, + "post": { + "summary": "POST /tables", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "database_id": { + "type": "integer" + } + }, + "required": [ + "name", + "description", + "database_id" + ] + }, + "example": { + "name": "k0kubun", + "description": "description", + "database_id": 2 + } + } + } + }, + "responses": { + "201": { + "description": "returns a table", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "database": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + }, + "null_sample": { + "nullable": true + }, + "storage_size": { + "type": "number", + "format": "float" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "database", + "null_sample", + "storage_size", + "created_at", + "updated_at" + ] + }, + "example": { + "id": 1, + "name": "access", + "description": "logs", + "database": { + "id": 2, + "name": "production" + }, + "null_sample": null, + "storage_size": 12.3, + "created_at": "2020-07-17T00:00:00+00:00", + "updated_at": "2020-07-17T00:00:00+00:00" + } + } + } + }, + "422": { + "description": "fails to create a table (2)", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + }, + "example": { + "error": "invalid name parameter" + } + } + } + } + } + } + }, + "/tables/1": { + "delete": { + "summary": "DELETE /tables/1", + "responses": { + "200": { + "description": "returns a table", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "database": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + }, + "null_sample": { + "nullable": true + }, + "storage_size": { + "type": "number", + "format": "float" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "database", + "null_sample", + "storage_size", + "created_at", + "updated_at" + ] + }, + "example": { + "id": 1, + "name": "access", + "description": "logs", + "database": { + "id": 2, + "name": "production" + }, + "null_sample": null, + "storage_size": 12.3, + "created_at": "2020-07-17T00:00:00+00:00", + "updated_at": "2020-07-17T00:00:00+00:00" + } + } + } + }, + "202": { + "description": "returns no content if specified" + } + }, + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "no_content": { + "type": "string" + } + } + }, + "example": { + "no_content": "true" + } + } + } + } + }, + "get": { + "summary": "GET /tables/1", + "responses": { + "200": { + "description": "returns a table", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "database": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + }, + "null_sample": { + "nullable": true + }, + "storage_size": { + "type": "number", + "format": "float" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "database", + "null_sample", + "storage_size", + "created_at", + "updated_at" + ] + }, + "example": { + "id": 1, + "name": "access", + "description": "logs", + "database": { + "id": 2, + "name": "production" + }, + "null_sample": null, + "storage_size": 12.3, + "created_at": "2020-07-17T00:00:00+00:00", + "updated_at": "2020-07-17T00:00:00+00:00" + } + } + } + }, + "401": { + "description": "does not return a table if unauthorized", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + }, + "example": { + "message": "Unauthorized" + } + } + } + } + } + }, + "patch": { + "summary": "PATCH /tables/1", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "example": { + "name": "test" + } + } + } + }, + "responses": { + "200": { + "description": "returns a table", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "database": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + }, + "null_sample": { + "nullable": true + }, + "storage_size": { + "type": "number", + "format": "float" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "database", + "null_sample", + "storage_size", + "created_at", + "updated_at" + ] + }, + "example": { + "id": 1, + "name": "access", + "description": "logs", + "database": { + "id": 2, + "name": "production" + }, + "null_sample": null, + "storage_size": 12.3, + "created_at": "2020-07-17T00:00:00+00:00", + "updated_at": "2020-07-17T00:00:00+00:00" + } + } + } + } + } + } + }, + "/tables/2": { + "get": { + "summary": "GET /tables/2", + "responses": { + "404": { + "description": "does not return a table if not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + }, + "example": { + "message": "not found" + } + } + } + } + } + } + }, + "/test_block": { + "get": { + "summary": "GET /test_block", + "responses": { + "200": { + "description": "returns the block content", + "content": { + "text/plain": { + "schema": { + "type": "string" + }, + "example": "A TEST" + } + } + } + }, + "deprecated": true + } + } + }, + "components": { + "securitySchemes": { + "SecretApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "Secret-Key" + } + } + } +} \ No newline at end of file diff --git a/spec/apps/hanami/doc/openapi.yaml b/spec/apps/hanami/doc/openapi.yaml new file mode 100644 index 00000000..f26243ac --- /dev/null +++ b/spec/apps/hanami/doc/openapi.yaml @@ -0,0 +1,665 @@ +# This file is auto-generated by rspec-openapi https://github.com/k0kubun/rspec-openapi +# +# When you write a spec in spec/requests, running the spec with `OPENAPI=1 rspec` will +# update this file automatically. You can also manually edit this file. +--- +openapi: 3.0.3 +info: + title: OpenAPI Documentation + version: 1.0.0 + description: My beautiful hanami API + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +servers: +- url: http://localhost:3000 +paths: + "/images": + get: + summary: GET /images + responses: + '200': + description: can return an object with an attribute of empty array + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + tags: + type: array + items: {} + required: + - name + - tags + example: + - name: file.png + tags: [] + "/images/1": + get: + summary: GET /images/1 + responses: + '200': + description: returns a image payload + content: + image/png: + schema: + type: string + format: binary + "/images/upload": + post: + summary: POST /images/upload + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + image: + type: string + format: binary + required: + - image + example: + image: test.png + responses: + '200': + description: returns a image payload with upload + content: + image/png: + schema: + type: string + format: binary + "/images/upload_multiple": + post: + summary: POST /images/upload_multiple + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + images: + type: array + items: + type: string + format: binary + required: + - images + example: + images: + - test.png + - test.png + responses: + '200': + description: returns a image payload with upload multiple + content: + image/png: + schema: + type: string + format: binary + "/images/upload_multiple_nested": + post: + summary: POST /images/upload_multiple_nested + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + images: + type: array + items: + type: object + properties: + image: + type: string + format: binary + required: + - image + required: + - images + example: + images: + - image: test.png + - image: test.png + responses: + '200': + description: returns a image payload with upload multiple nested + content: + image/png: + schema: + type: string + format: binary + "/images/upload_nested": + post: + summary: POST /images/upload_nested + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + nested_image: + type: object + properties: + image: + type: string + format: binary + caption: + type: string + required: + - image + - caption + required: + - nested_image + example: + nested_image: + image: test.png + caption: Some caption + responses: + '200': + description: returns a image payload with upload nested + content: + image/png: + schema: + type: string + format: binary + "/my_engine/eng/example": + get: + summary: GET /my_engine/eng/example + responses: + '200': + description: returns the block content + content: + text/plain: + schema: + type: string + example: AN ENGINE TEST + "/my_engine/test": + get: + summary: GET /my_engine/test + responses: + '200': + description: returns some content from the engine + content: + text/plain: + schema: + type: string + example: ANOTHER TEST + "/secret_items": + get: + summary: GET /secret_items + security: + - SecretApiKeyAuth: [] + responses: + '200': + description: authorizes with secret key + content: + application/json: + schema: + type: object + properties: + items: + type: array + items: + type: string + required: + - items + example: + items: + - secrets + '401': + description: authorizes with secret key + content: + text/html: + schema: + type: string + example: '' + "/tables": + get: + summary: GET /tables + parameters: + - name: X-Authorization-Token + in: header + required: true + schema: + type: string + example: token + - name: filter[name] + in: query + required: false + schema: + type: object + properties: + name: + type: string + required: + - name + example: + name: Example Table + - name: filter[price] + in: query + required: false + schema: + type: object + properties: + price: + type: string + required: + - price + example: + price: '0' + - name: page + in: query + required: false + schema: + type: integer + example: 1 + - name: per + in: query + required: false + schema: + type: integer + example: 10 + responses: + '200': + description: with different deep query parameters + headers: + X-Cursor: + schema: + type: integer + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: integer + name: + type: string + description: + type: string + database: + type: object + properties: + id: + type: integer + name: + type: string + required: + - id + - name + null_sample: + nullable: true + storage_size: + type: number + format: float + created_at: + type: string + updated_at: + type: string + required: + - id + - name + - description + - database + - null_sample + - storage_size + - created_at + - updated_at + example: + - id: 1 + name: access + description: logs + database: + id: 2 + name: production + null_sample: + storage_size: 12.3 + created_at: '2020-07-17T00:00:00+00:00' + updated_at: '2020-07-17T00:00:00+00:00' + '401': + description: does not return tables if unauthorized + content: + application/json: + schema: + type: object + properties: + message: + type: string + required: + - message + example: + message: Unauthorized + post: + summary: POST /tables + responses: + '201': + description: returns a table + content: + application/json: + schema: + type: object + properties: + id: + type: integer + name: + type: string + description: + type: string + database: + type: object + properties: + id: + type: integer + name: + type: string + required: + - id + - name + null_sample: + nullable: true + storage_size: + type: number + format: float + created_at: + type: string + updated_at: + type: string + required: + - id + - name + - description + - database + - null_sample + - storage_size + - created_at + - updated_at + example: + id: 1 + name: access + description: logs + database: + id: 2 + name: production + null_sample: + storage_size: 12.3 + created_at: '2020-07-17T00:00:00+00:00' + updated_at: '2020-07-17T00:00:00+00:00' + '422': + description: fails to create a table (2) + content: + application/json: + schema: + type: object + properties: + error: + type: string + required: + - error + example: + error: invalid name parameter + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: + type: string + database_id: + type: integer + required: + - name + - description + - database_id + example: + name: k0kubun + description: description + database_id: 2 + "/tables/1": + delete: + summary: DELETE /tables/1 + responses: + '200': + description: returns a table + content: + application/json: + schema: + type: object + properties: + id: + type: integer + name: + type: string + description: + type: string + database: + type: object + properties: + id: + type: integer + name: + type: string + required: + - id + - name + null_sample: + nullable: true + storage_size: + type: number + format: float + created_at: + type: string + updated_at: + type: string + required: + - id + - name + - description + - database + - null_sample + - storage_size + - created_at + - updated_at + example: + id: 1 + name: access + description: logs + database: + id: 2 + name: production + null_sample: + storage_size: 12.3 + created_at: '2020-07-17T00:00:00+00:00' + updated_at: '2020-07-17T00:00:00+00:00' + '202': + description: returns no content if specified + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + no_content: + type: string + example: + no_content: 'true' + get: + summary: GET /tables/1 + responses: + '200': + description: returns a table + content: + application/json: + schema: + type: object + properties: + id: + type: integer + name: + type: string + description: + type: string + database: + type: object + properties: + id: + type: integer + name: + type: string + required: + - id + - name + null_sample: + nullable: true + storage_size: + type: number + format: float + created_at: + type: string + updated_at: + type: string + required: + - id + - name + - description + - database + - null_sample + - storage_size + - created_at + - updated_at + example: + id: 1 + name: access + description: logs + database: + id: 2 + name: production + null_sample: + storage_size: 12.3 + created_at: '2020-07-17T00:00:00+00:00' + updated_at: '2020-07-17T00:00:00+00:00' + '401': + description: does not return a table if unauthorized + content: + application/json: + schema: + type: object + properties: + message: + type: string + required: + - message + example: + message: Unauthorized + patch: + summary: PATCH /tables/1 + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + name: + type: string + required: + - name + example: + name: test + responses: + '200': + description: returns a table + content: + application/json: + schema: + type: object + properties: + id: + type: integer + name: + type: string + description: + type: string + database: + type: object + properties: + id: + type: integer + name: + type: string + required: + - id + - name + null_sample: + nullable: true + storage_size: + type: number + format: float + created_at: + type: string + updated_at: + type: string + required: + - id + - name + - description + - database + - null_sample + - storage_size + - created_at + - updated_at + example: + id: 1 + name: access + description: logs + database: + id: 2 + name: production + null_sample: + storage_size: 12.3 + created_at: '2020-07-17T00:00:00+00:00' + updated_at: '2020-07-17T00:00:00+00:00' + "/tables/2": + get: + summary: GET /tables/2 + responses: + '404': + description: does not return a table if not found + content: + application/json: + schema: + type: object + properties: + message: + type: string + required: + - message + example: + message: not found + "/test_block": + get: + summary: GET /test_block + responses: + '200': + description: returns the block content + content: + text/plain: + schema: + type: string + example: A TEST + deprecated: true +components: + securitySchemes: + SecretApiKeyAuth: + type: apiKey + in: header + name: Secret-Key diff --git a/spec/apps/hanami/lib/hanami_test/types.rb b/spec/apps/hanami/lib/hanami_test/types.rb new file mode 100644 index 00000000..a5c02023 --- /dev/null +++ b/spec/apps/hanami/lib/hanami_test/types.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require "dry/types" + +module HanamiTest + Types = Dry.Types + + module Types + # Define your custom types here + end +end diff --git a/spec/rails/app/controllers/concerns/.keep b/spec/apps/hanami/lib/tasks/.keep similarity index 100% rename from spec/rails/app/controllers/concerns/.keep rename to spec/apps/hanami/lib/tasks/.keep diff --git a/spec/apps/hanami/public/404.html b/spec/apps/hanami/public/404.html new file mode 100644 index 00000000..51242009 --- /dev/null +++ b/spec/apps/hanami/public/404.html @@ -0,0 +1,82 @@ + + + + + + The page you were looking for doesn’t exist (404) + + + + +
+
+

404

+

The page you were looking for doesn’t exist.

+
+
+ + diff --git a/spec/apps/hanami/public/500.html b/spec/apps/hanami/public/500.html new file mode 100644 index 00000000..1f8ff3cc --- /dev/null +++ b/spec/apps/hanami/public/500.html @@ -0,0 +1,82 @@ + + + + + + We’re sorry, but something went wrong (500) + + + + +
+
+

500

+

We’re sorry, but something went wrong.

+
+
+ + diff --git a/spec/apps/hanami/slices/my_engine/action.rb b/spec/apps/hanami/slices/my_engine/action.rb new file mode 100644 index 00000000..8b8a350a --- /dev/null +++ b/spec/apps/hanami/slices/my_engine/action.rb @@ -0,0 +1,7 @@ +# auto_register: false +# frozen_string_literal: true + +module MyEngine + class Action < HanamiTest::Action + end +end diff --git a/spec/rails/app/models/concerns/.keep b/spec/apps/hanami/slices/my_engine/actions/.keep similarity index 100% rename from spec/rails/app/models/concerns/.keep rename to spec/apps/hanami/slices/my_engine/actions/.keep diff --git a/spec/apps/hanami/slices/my_engine/actions/eng/example.rb b/spec/apps/hanami/slices/my_engine/actions/eng/example.rb new file mode 100644 index 00000000..2ad29ef0 --- /dev/null +++ b/spec/apps/hanami/slices/my_engine/actions/eng/example.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module MyEngine + module Actions + module Eng + class Example < MyEngine::Action + def handle(_request, response) + response.headers['Content-Type'] = 'text/plain' + response.body = 'AN ENGINE TEST' + end + end + end + end +end diff --git a/spec/apps/hanami/test.png b/spec/apps/hanami/test.png new file mode 100644 index 00000000..4579a006 Binary files /dev/null and b/spec/apps/hanami/test.png differ diff --git a/spec/rails/.gitignore b/spec/apps/rails/.gitignore similarity index 98% rename from spec/rails/.gitignore rename to spec/apps/rails/.gitignore index a7c1e90f..9e94df15 100644 --- a/spec/rails/.gitignore +++ b/spec/apps/rails/.gitignore @@ -13,7 +13,7 @@ /db/*.sqlite3-* # Ignore all logfiles and tempfiles. -/log/* +/log/.keep /tmp/* !/log/.keep !/tmp/.keep diff --git a/spec/rails/Rakefile b/spec/apps/rails/Rakefile similarity index 100% rename from spec/rails/Rakefile rename to spec/apps/rails/Rakefile diff --git a/spec/rails/app/assets/config/manifest.js b/spec/apps/rails/app/assets/config/manifest.js similarity index 100% rename from spec/rails/app/assets/config/manifest.js rename to spec/apps/rails/app/assets/config/manifest.js diff --git a/spec/rails/lib/assets/.keep b/spec/apps/rails/app/assets/images/.keep similarity index 100% rename from spec/rails/lib/assets/.keep rename to spec/apps/rails/app/assets/images/.keep diff --git a/spec/rails/app/assets/stylesheets/application.css b/spec/apps/rails/app/assets/stylesheets/application.css similarity index 100% rename from spec/rails/app/assets/stylesheets/application.css rename to spec/apps/rails/app/assets/stylesheets/application.css diff --git a/spec/rails/app/channels/application_cable/channel.rb b/spec/apps/rails/app/channels/application_cable/channel.rb similarity index 100% rename from spec/rails/app/channels/application_cable/channel.rb rename to spec/apps/rails/app/channels/application_cable/channel.rb diff --git a/spec/rails/app/channels/application_cable/connection.rb b/spec/apps/rails/app/channels/application_cable/connection.rb similarity index 100% rename from spec/rails/app/channels/application_cable/connection.rb rename to spec/apps/rails/app/channels/application_cable/connection.rb diff --git a/spec/rails/app/controllers/additional_properties_controller.rb b/spec/apps/rails/app/controllers/additional_properties_controller.rb similarity index 100% rename from spec/rails/app/controllers/additional_properties_controller.rb rename to spec/apps/rails/app/controllers/additional_properties_controller.rb diff --git a/spec/rails/app/controllers/application_controller.rb b/spec/apps/rails/app/controllers/application_controller.rb similarity index 100% rename from spec/rails/app/controllers/application_controller.rb rename to spec/apps/rails/app/controllers/application_controller.rb diff --git a/spec/rails/lib/tasks/.keep b/spec/apps/rails/app/controllers/concerns/.keep similarity index 100% rename from spec/rails/lib/tasks/.keep rename to spec/apps/rails/app/controllers/concerns/.keep diff --git a/spec/rails/app/controllers/images_controller.rb b/spec/apps/rails/app/controllers/images_controller.rb similarity index 100% rename from spec/rails/app/controllers/images_controller.rb rename to spec/apps/rails/app/controllers/images_controller.rb diff --git a/spec/rails/app/controllers/masters/extensions_controller.rb b/spec/apps/rails/app/controllers/masters/extensions_controller.rb similarity index 100% rename from spec/rails/app/controllers/masters/extensions_controller.rb rename to spec/apps/rails/app/controllers/masters/extensions_controller.rb diff --git a/spec/rails/app/controllers/pages_controller.rb b/spec/apps/rails/app/controllers/pages_controller.rb similarity index 100% rename from spec/rails/app/controllers/pages_controller.rb rename to spec/apps/rails/app/controllers/pages_controller.rb diff --git a/spec/rails/app/controllers/secret_items_controller.rb b/spec/apps/rails/app/controllers/secret_items_controller.rb similarity index 100% rename from spec/rails/app/controllers/secret_items_controller.rb rename to spec/apps/rails/app/controllers/secret_items_controller.rb diff --git a/spec/rails/app/controllers/tables_controller.rb b/spec/apps/rails/app/controllers/tables_controller.rb similarity index 100% rename from spec/rails/app/controllers/tables_controller.rb rename to spec/apps/rails/app/controllers/tables_controller.rb diff --git a/spec/rails/app/controllers/users_controller.rb b/spec/apps/rails/app/controllers/users_controller.rb similarity index 98% rename from spec/rails/app/controllers/users_controller.rb rename to spec/apps/rails/app/controllers/users_controller.rb index 2a66a2a2..e25e4e1d 100644 --- a/spec/rails/app/controllers/users_controller.rb +++ b/spec/apps/rails/app/controllers/users_controller.rb @@ -37,8 +37,7 @@ def find_user(id = nil) }, } when '2' - { - } + {} else raise NotFoundError end diff --git a/spec/rails/app/helpers/application_helper.rb b/spec/apps/rails/app/helpers/application_helper.rb similarity index 100% rename from spec/rails/app/helpers/application_helper.rb rename to spec/apps/rails/app/helpers/application_helper.rb diff --git a/spec/rails/app/javascript/channels/consumer.js b/spec/apps/rails/app/javascript/channels/consumer.js similarity index 100% rename from spec/rails/app/javascript/channels/consumer.js rename to spec/apps/rails/app/javascript/channels/consumer.js diff --git a/spec/rails/app/javascript/channels/index.js b/spec/apps/rails/app/javascript/channels/index.js similarity index 100% rename from spec/rails/app/javascript/channels/index.js rename to spec/apps/rails/app/javascript/channels/index.js diff --git a/spec/rails/app/javascript/packs/application.js b/spec/apps/rails/app/javascript/packs/application.js similarity index 100% rename from spec/rails/app/javascript/packs/application.js rename to spec/apps/rails/app/javascript/packs/application.js diff --git a/spec/rails/app/jobs/application_job.rb b/spec/apps/rails/app/jobs/application_job.rb similarity index 100% rename from spec/rails/app/jobs/application_job.rb rename to spec/apps/rails/app/jobs/application_job.rb diff --git a/spec/rails/app/mailers/application_mailer.rb b/spec/apps/rails/app/mailers/application_mailer.rb similarity index 100% rename from spec/rails/app/mailers/application_mailer.rb rename to spec/apps/rails/app/mailers/application_mailer.rb diff --git a/spec/rails/app/models/application_record.rb b/spec/apps/rails/app/models/application_record.rb similarity index 100% rename from spec/rails/app/models/application_record.rb rename to spec/apps/rails/app/models/application_record.rb diff --git a/spec/rails/log/.keep b/spec/apps/rails/app/models/concerns/.keep similarity index 100% rename from spec/rails/log/.keep rename to spec/apps/rails/app/models/concerns/.keep diff --git a/spec/rails/app/views/layouts/application.html.erb b/spec/apps/rails/app/views/layouts/application.html.erb similarity index 100% rename from spec/rails/app/views/layouts/application.html.erb rename to spec/apps/rails/app/views/layouts/application.html.erb diff --git a/spec/rails/app/views/layouts/mailer.html.erb b/spec/apps/rails/app/views/layouts/mailer.html.erb similarity index 100% rename from spec/rails/app/views/layouts/mailer.html.erb rename to spec/apps/rails/app/views/layouts/mailer.html.erb diff --git a/spec/rails/app/views/layouts/mailer.text.erb b/spec/apps/rails/app/views/layouts/mailer.text.erb similarity index 100% rename from spec/rails/app/views/layouts/mailer.text.erb rename to spec/apps/rails/app/views/layouts/mailer.text.erb diff --git a/spec/rails/bin/rails b/spec/apps/rails/bin/rails similarity index 100% rename from spec/rails/bin/rails rename to spec/apps/rails/bin/rails diff --git a/spec/rails/bin/rake b/spec/apps/rails/bin/rake similarity index 100% rename from spec/rails/bin/rake rename to spec/apps/rails/bin/rake diff --git a/spec/rails/bin/setup b/spec/apps/rails/bin/setup similarity index 100% rename from spec/rails/bin/setup rename to spec/apps/rails/bin/setup diff --git a/spec/rails/bin/yarn b/spec/apps/rails/bin/yarn similarity index 100% rename from spec/rails/bin/yarn rename to spec/apps/rails/bin/yarn diff --git a/spec/rails/config.ru b/spec/apps/rails/config.ru similarity index 100% rename from spec/rails/config.ru rename to spec/apps/rails/config.ru diff --git a/spec/rails/config/application.rb b/spec/apps/rails/config/application.rb similarity index 100% rename from spec/rails/config/application.rb rename to spec/apps/rails/config/application.rb diff --git a/spec/rails/config/boot.rb b/spec/apps/rails/config/boot.rb similarity index 100% rename from spec/rails/config/boot.rb rename to spec/apps/rails/config/boot.rb diff --git a/spec/rails/config/cable.yml b/spec/apps/rails/config/cable.yml similarity index 100% rename from spec/rails/config/cable.yml rename to spec/apps/rails/config/cable.yml diff --git a/spec/rails/config/credentials.yml.enc b/spec/apps/rails/config/credentials.yml.enc similarity index 100% rename from spec/rails/config/credentials.yml.enc rename to spec/apps/rails/config/credentials.yml.enc diff --git a/spec/rails/config/database.yml b/spec/apps/rails/config/database.yml similarity index 100% rename from spec/rails/config/database.yml rename to spec/apps/rails/config/database.yml diff --git a/spec/rails/config/environment.rb b/spec/apps/rails/config/environment.rb similarity index 100% rename from spec/rails/config/environment.rb rename to spec/apps/rails/config/environment.rb diff --git a/spec/rails/config/environments/development.rb b/spec/apps/rails/config/environments/development.rb similarity index 100% rename from spec/rails/config/environments/development.rb rename to spec/apps/rails/config/environments/development.rb diff --git a/spec/rails/config/environments/production.rb b/spec/apps/rails/config/environments/production.rb similarity index 99% rename from spec/rails/config/environments/production.rb rename to spec/apps/rails/config/environments/production.rb index 37a5e950..0a9905f5 100644 --- a/spec/rails/config/environments/production.rb +++ b/spec/apps/rails/config/environments/production.rb @@ -51,7 +51,7 @@ config.log_level = :debug # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store diff --git a/spec/rails/config/environments/test.rb b/spec/apps/rails/config/environments/test.rb similarity index 100% rename from spec/rails/config/environments/test.rb rename to spec/apps/rails/config/environments/test.rb diff --git a/spec/rails/config/initializers/application_controller_renderer.rb b/spec/apps/rails/config/initializers/application_controller_renderer.rb similarity index 100% rename from spec/rails/config/initializers/application_controller_renderer.rb rename to spec/apps/rails/config/initializers/application_controller_renderer.rb diff --git a/spec/rails/config/initializers/backtrace_silencers.rb b/spec/apps/rails/config/initializers/backtrace_silencers.rb similarity index 100% rename from spec/rails/config/initializers/backtrace_silencers.rb rename to spec/apps/rails/config/initializers/backtrace_silencers.rb diff --git a/spec/rails/config/initializers/content_security_policy.rb b/spec/apps/rails/config/initializers/content_security_policy.rb similarity index 100% rename from spec/rails/config/initializers/content_security_policy.rb rename to spec/apps/rails/config/initializers/content_security_policy.rb diff --git a/spec/rails/config/initializers/cookies_serializer.rb b/spec/apps/rails/config/initializers/cookies_serializer.rb similarity index 100% rename from spec/rails/config/initializers/cookies_serializer.rb rename to spec/apps/rails/config/initializers/cookies_serializer.rb diff --git a/spec/rails/config/initializers/filter_parameter_logging.rb b/spec/apps/rails/config/initializers/filter_parameter_logging.rb similarity index 100% rename from spec/rails/config/initializers/filter_parameter_logging.rb rename to spec/apps/rails/config/initializers/filter_parameter_logging.rb diff --git a/spec/rails/config/initializers/inflections.rb b/spec/apps/rails/config/initializers/inflections.rb similarity index 100% rename from spec/rails/config/initializers/inflections.rb rename to spec/apps/rails/config/initializers/inflections.rb diff --git a/spec/rails/config/initializers/mime_types.rb b/spec/apps/rails/config/initializers/mime_types.rb similarity index 100% rename from spec/rails/config/initializers/mime_types.rb rename to spec/apps/rails/config/initializers/mime_types.rb diff --git a/spec/rails/config/initializers/wrap_parameters.rb b/spec/apps/rails/config/initializers/wrap_parameters.rb similarity index 100% rename from spec/rails/config/initializers/wrap_parameters.rb rename to spec/apps/rails/config/initializers/wrap_parameters.rb diff --git a/spec/rails/config/locales/en.yml b/spec/apps/rails/config/locales/en.yml similarity index 100% rename from spec/rails/config/locales/en.yml rename to spec/apps/rails/config/locales/en.yml diff --git a/spec/rails/config/puma.rb b/spec/apps/rails/config/puma.rb similarity index 100% rename from spec/rails/config/puma.rb rename to spec/apps/rails/config/puma.rb diff --git a/spec/rails/config/routes.rb b/spec/apps/rails/config/routes.rb similarity index 94% rename from spec/rails/config/routes.rb rename to spec/apps/rails/config/routes.rb index 224d5ddb..889043dd 100644 --- a/spec/rails/config/routes.rb +++ b/spec/apps/rails/config/routes.rb @@ -15,7 +15,7 @@ post 'upload_multiple_nested' end end - resources :users, only: [:show, :create] do + resources :users, only: [:show, :create] do get 'active' end diff --git a/spec/rails/config/spring.rb b/spec/apps/rails/config/spring.rb similarity index 100% rename from spec/rails/config/spring.rb rename to spec/apps/rails/config/spring.rb diff --git a/spec/rails/config/storage.yml b/spec/apps/rails/config/storage.yml similarity index 100% rename from spec/rails/config/storage.yml rename to spec/apps/rails/config/storage.yml diff --git a/spec/rails/db/seeds.rb b/spec/apps/rails/db/seeds.rb similarity index 100% rename from spec/rails/db/seeds.rb rename to spec/apps/rails/db/seeds.rb diff --git a/spec/rails/doc/openapi.json b/spec/apps/rails/doc/openapi.json similarity index 100% rename from spec/rails/doc/openapi.json rename to spec/apps/rails/doc/openapi.json diff --git a/spec/rails/doc/openapi.yaml b/spec/apps/rails/doc/openapi.yaml similarity index 100% rename from spec/rails/doc/openapi.yaml rename to spec/apps/rails/doc/openapi.yaml diff --git a/spec/rails/doc/screenshot.png b/spec/apps/rails/doc/screenshot.png similarity index 100% rename from spec/rails/doc/screenshot.png rename to spec/apps/rails/doc/screenshot.png diff --git a/spec/rails/doc/smart/expected.yaml b/spec/apps/rails/doc/smart/expected.yaml similarity index 100% rename from spec/rails/doc/smart/expected.yaml rename to spec/apps/rails/doc/smart/expected.yaml diff --git a/spec/rails/doc/smart/openapi.yaml b/spec/apps/rails/doc/smart/openapi.yaml similarity index 100% rename from spec/rails/doc/smart/openapi.yaml rename to spec/apps/rails/doc/smart/openapi.yaml diff --git a/spec/rails/storage/.keep b/spec/apps/rails/lib/assets/.keep similarity index 100% rename from spec/rails/storage/.keep rename to spec/apps/rails/lib/assets/.keep diff --git a/spec/rails/tmp/.keep b/spec/apps/rails/lib/tasks/.keep similarity index 100% rename from spec/rails/tmp/.keep rename to spec/apps/rails/lib/tasks/.keep diff --git a/spec/rails/tmp/pids/.keep b/spec/apps/rails/log/.keep similarity index 100% rename from spec/rails/tmp/pids/.keep rename to spec/apps/rails/log/.keep diff --git a/spec/rails/package.json b/spec/apps/rails/package.json similarity index 100% rename from spec/rails/package.json rename to spec/apps/rails/package.json diff --git a/spec/rails/public/404.html b/spec/apps/rails/public/404.html similarity index 100% rename from spec/rails/public/404.html rename to spec/apps/rails/public/404.html diff --git a/spec/rails/public/422.html b/spec/apps/rails/public/422.html similarity index 100% rename from spec/rails/public/422.html rename to spec/apps/rails/public/422.html diff --git a/spec/rails/public/500.html b/spec/apps/rails/public/500.html similarity index 100% rename from spec/rails/public/500.html rename to spec/apps/rails/public/500.html diff --git a/spec/rails/public/apple-touch-icon-precomposed.png b/spec/apps/rails/public/apple-touch-icon-precomposed.png similarity index 100% rename from spec/rails/public/apple-touch-icon-precomposed.png rename to spec/apps/rails/public/apple-touch-icon-precomposed.png diff --git a/spec/rails/public/apple-touch-icon.png b/spec/apps/rails/public/apple-touch-icon.png similarity index 100% rename from spec/rails/public/apple-touch-icon.png rename to spec/apps/rails/public/apple-touch-icon.png diff --git a/spec/rails/public/favicon.ico b/spec/apps/rails/public/favicon.ico similarity index 100% rename from spec/rails/public/favicon.ico rename to spec/apps/rails/public/favicon.ico diff --git a/spec/rails/public/robots.txt b/spec/apps/rails/public/robots.txt similarity index 100% rename from spec/rails/public/robots.txt rename to spec/apps/rails/public/robots.txt diff --git a/spec/rails/vendor/.keep b/spec/apps/rails/storage/.keep similarity index 100% rename from spec/rails/vendor/.keep rename to spec/apps/rails/storage/.keep diff --git a/spec/apps/rails/tmp/.keep b/spec/apps/rails/tmp/.keep new file mode 100644 index 00000000..e69de29b diff --git a/spec/apps/rails/tmp/pids/.keep b/spec/apps/rails/tmp/pids/.keep new file mode 100644 index 00000000..e69de29b diff --git a/spec/apps/rails/vendor/.keep b/spec/apps/rails/vendor/.keep new file mode 100644 index 00000000..e69de29b diff --git a/spec/rails/vendor/my_engine/config/routes.rb b/spec/apps/rails/vendor/my_engine/config/routes.rb similarity index 100% rename from spec/rails/vendor/my_engine/config/routes.rb rename to spec/apps/rails/vendor/my_engine/config/routes.rb diff --git a/spec/rails/vendor/my_engine/lib/my_engine/engine.rb b/spec/apps/rails/vendor/my_engine/lib/my_engine/engine.rb similarity index 100% rename from spec/rails/vendor/my_engine/lib/my_engine/engine.rb rename to spec/apps/rails/vendor/my_engine/lib/my_engine/engine.rb diff --git a/spec/roda/doc/openapi.json b/spec/apps/roda/doc/openapi.json similarity index 100% rename from spec/roda/doc/openapi.json rename to spec/apps/roda/doc/openapi.json diff --git a/spec/roda/doc/openapi.yaml b/spec/apps/roda/doc/openapi.yaml similarity index 100% rename from spec/roda/doc/openapi.yaml rename to spec/apps/roda/doc/openapi.yaml diff --git a/spec/roda/doc/rspec_openapi.rb b/spec/apps/roda/doc/rspec_openapi.rb similarity index 100% rename from spec/roda/doc/rspec_openapi.rb rename to spec/apps/roda/doc/rspec_openapi.rb diff --git a/spec/roda/roda_app.rb b/spec/apps/roda/roda_app.rb similarity index 100% rename from spec/roda/roda_app.rb rename to spec/apps/roda/roda_app.rb diff --git a/spec/integration_tests/rails_test.rb b/spec/integration_tests/rails_test.rb index 70dd104a..b7a8dc92 100644 --- a/spec/integration_tests/rails_test.rb +++ b/spec/integration_tests/rails_test.rb @@ -5,12 +5,12 @@ ENV['OPENAPI_OUTPUT'] ||= 'yaml' require 'minitest/autorun' -require File.expand_path('../rails/config/environment', __dir__) +require File.expand_path('../apps/rails/config/environment', __dir__) RSpec::OpenAPI.title = 'OpenAPI Documentation' RSpec::OpenAPI.request_headers = %w[X-Authorization-Token] RSpec::OpenAPI.response_headers = %w[X-Cursor] -RSpec::OpenAPI.path = File.expand_path("../rails/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) +RSpec::OpenAPI.path = File.expand_path("../apps/rails/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) RSpec::OpenAPI.ignored_paths = [Regexp.new('/admin/.*$')] RSpec::OpenAPI.comment = <<~COMMENT This file is auto-generated by rspec-openapi https://github.com/k0kubun/rspec-openapi diff --git a/spec/integration_tests/roda_test.rb b/spec/integration_tests/roda_test.rb index 07ceabe4..2771216d 100644 --- a/spec/integration_tests/roda_test.rb +++ b/spec/integration_tests/roda_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative '../roda/roda_app' +require_relative '../apps/roda/roda_app' require 'json' require 'rack/test' require 'minitest/autorun' @@ -9,7 +9,7 @@ ENV['OPENAPI_OUTPUT'] ||= 'yaml' RSpec::OpenAPI.title = 'OpenAPI Documentation' -RSpec::OpenAPI.path = File.expand_path("../roda/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) +RSpec::OpenAPI.path = File.expand_path("../apps/roda/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) RSpec::OpenAPI.ignored_paths = ['/admin/masters/extensions'] class RodaTest < Minitest::Test diff --git a/spec/minitest/rack_test_spec.rb b/spec/minitest/rack_test_spec.rb index 20c8d424..3ea15c3f 100644 --- a/spec/minitest/rack_test_spec.rb +++ b/spec/minitest/rack_test_spec.rb @@ -9,10 +9,10 @@ describe 'yaml output' do let(:openapi_path) do - File.expand_path('spec/roda/doc/openapi.yaml', repo_root) + File.expand_path('spec/apps/roda/doc/openapi.yaml', repo_root) end - it 'generates the same spec/roda/doc/openapi.yaml' do + it 'generates the same spec/apps/roda/doc/openapi.yaml' do org_yaml = YAML.safe_load(File.read(openapi_path)) minitest 'spec/integration_tests/roda_test.rb', openapi: true new_yaml = YAML.safe_load(File.read(openapi_path)) @@ -22,10 +22,10 @@ describe 'json output' do let(:openapi_path) do - File.expand_path('spec/roda/doc/openapi.json', repo_root) + File.expand_path('spec/apps/roda/doc/openapi.json', repo_root) end - it 'generates the same spec/roda/doc/openapi.json' do + it 'generates the same spec/apps/roda/doc/openapi.json' do org_yaml = YAML.safe_load(File.read(openapi_path)) minitest 'spec/integration_tests/roda_test.rb', openapi: true, output: :json new_yaml = YAML.safe_load(File.read(openapi_path)) diff --git a/spec/minitest/rails_spec.rb b/spec/minitest/rails_spec.rb index d67a3cab..eee10628 100644 --- a/spec/minitest/rails_spec.rb +++ b/spec/minitest/rails_spec.rb @@ -10,10 +10,10 @@ describe 'yaml output' do let(:openapi_path) do - File.expand_path('spec/rails/doc/openapi.yaml', repo_root) + File.expand_path('spec/apps/rails/doc/openapi.yaml', repo_root) end - it 'generates the same spec/rails/doc/openapi.yaml' do + it 'generates the same spec/apps/rails/doc/openapi.yaml' do org_yaml = YAML.safe_load(File.read(openapi_path)) minitest 'spec/integration_tests/rails_test.rb', openapi: true, output: :yaml new_yaml = YAML.safe_load(File.read(openapi_path)) @@ -23,10 +23,10 @@ describe 'json' do let(:openapi_path) do - File.expand_path('spec/rails/doc/openapi.json', repo_root) + File.expand_path('spec/apps/rails/doc/openapi.json', repo_root) end - it 'generates the same spec/rails/doc/openapi.json' do + it 'generates the same spec/apps/rails/doc/openapi.json' do org_yaml = JSON.parse(File.read(openapi_path)) minitest 'spec/integration_tests/rails_test.rb', openapi: true, output: :json new_yaml = JSON.parse(File.read(openapi_path)) diff --git a/spec/requests/hanami_spec.rb b/spec/requests/hanami_spec.rb new file mode 100644 index 00000000..1dadb4b4 --- /dev/null +++ b/spec/requests/hanami_spec.rb @@ -0,0 +1,284 @@ +# frozen_string_literal: true + +return unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') + +ENV['TZ'] ||= 'UTC' +ENV['HANAMI_ENV'] ||= 'test' +ENV['OPENAPI_OUTPUT'] ||= 'yaml' + +require 'json' +require 'rack/test' + +Dir.chdir('spec/apps/hanami') # HACK: for load hanami success +require 'hanami/prepare' + +RSpec::OpenAPI.title = 'OpenAPI Documentation' +RSpec::OpenAPI.request_headers = %w[X-Authorization-Token Secret-Key] +RSpec::OpenAPI.response_headers = %w[X-Cursor] +RSpec::OpenAPI.path = File.expand_path("../apps/hanami/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) +RSpec::OpenAPI.ignored_paths = ['/admin/masters/extensions'] +RSpec::OpenAPI.comment = <<~COMMENT + This file is auto-generated by rspec-openapi https://github.com/k0kubun/rspec-openapi + + When you write a spec in spec/requests, running the spec with `OPENAPI=1 rspec` will + update this file automatically. You can also manually edit this file. +COMMENT +RSpec::OpenAPI.servers = [{ url: 'http://localhost:3000' }] +RSpec::OpenAPI.info = { + description: 'My beautiful hanami API', + license: { + name: 'Apache 2.0', + url: 'https://www.apache.org/licenses/LICENSE-2.0.html', + }, +} + +RSpec::OpenAPI.security_schemes = { + SecretApiKeyAuth: { + type: 'apiKey', + in: 'header', + name: 'Secret-Key', + }, +} + +RSpec.shared_context 'Hanami app' do + let(:app) { Hanami.app } +end + +RSpec.configure do |config| + config.include Rack::Test::Methods, type: :request + config.include_context 'Hanami app', type: :request +end + +RSpec.describe 'Tables', type: :request do + describe '#index' do + context 'returns a list of tables' do + it 'with flat query parameters' do + get '/tables', { page: '1', per: '10' }, { 'AUTHORIZATION' => 'k0kubun', 'X_AUTHORIZATION_TOKEN' => 'token' } + # binding.irb + expect(last_response.status).to eq(200) + end + + it 'with deep query parameters' do + get '/tables', { filter: { 'name' => 'Example Table' } }, { 'AUTHORIZATION' => 'k0kubun' } + expect(last_response.status).to eq(200) + end + + it 'with different deep query parameters' do + get '/tables', { filter: { 'price' => 0 } }, { 'AUTHORIZATION' => 'k0kubun' } + expect(last_response.status).to eq(200) + end + end + + # it 'has a request spec which does not make any request' do + # expect(last_request).to eq(nil) + # end + # Rack::Test::Error: + # No request yet. Request a page first. + + it 'does not return tables if unauthorized' do + get '/tables' + expect(last_response.status).to eq(401) + end + end + + describe '#show' do + it 'returns a table' do + get '/tables/1', nil, { 'AUTHORIZATION' => 'k0kubun' } + expect(last_response.status).to eq(200) + end + + it 'does not return a table if unauthorized' do + get '/tables/1' + expect(last_response.status).to eq(401) + end + + it 'does not return a table if not found' do + get '/tables/2', nil, { 'AUTHORIZATION' => 'k0kubun' } + expect(last_response.status).to eq(404) + end + + it 'does not return a table if not found (openapi: false)', openapi: false do + get '/tables/3', nil, { 'AUTHORIZATION' => 'k0kubun' } + expect(last_response.status).to eq(404) + end + end + + describe '#create' do + it 'returns a table' do + post '/tables', { + name: 'k0kubun', + description: 'description', + database_id: 2, + }.to_json, + { 'AUTHORIZATION' => 'k0kubun', 'CONTENT_TYPE' => 'application/json' } + expect(last_response.status).to eq(201) + end + + it 'fails to create a table' do + post '/tables', { + description: 'description', + database_id: 2, + }.to_json, + { 'AUTHORIZATION' => 'k0kubun', 'CONTENT_TYPE' => 'application/json' } + expect(last_response.status).to eq(422) + end + + it 'fails to create a table (2)' do + post '/tables', { + name: 'some_invalid_name', + description: 'description', + database_id: 2, + }.to_json, + { 'AUTHORIZATION' => 'k0kubun', 'CONTENT_TYPE' => 'application/json' } + expect(last_response.status).to eq(422) + end + end + + describe '#update' do + it 'returns a table' do + patch '/tables/1', { name: 'test' }, + { 'AUTHORIZATION' => 'k0kubun', 'CONTENT_TYPE' => 'application/x-www-form-urlencoded' } + expect(last_response.status).to eq(200) + end + end + + describe '#destroy' do + it 'returns a table' do + delete '/tables/1', nil, { 'AUTHORIZATION' => 'k0kubun' } + expect(last_response.status).to eq(200) + end + + it 'returns no content if specified' do + delete '/tables/1', { no_content: true }, + { 'AUTHORIZATION' => 'k0kubun', 'CONTENT_TYPE' => 'application/x-www-form-urlencoded' } + expect(last_response.status).to eq(202) + end + end +end + +RSpec.describe 'Images', type: :request do + describe '#payload' do + it 'returns a image payload' do + get '/images/1' + expect(last_response.status).to eq(200) + end + end + + describe '#index' do + it 'can return an object with an attribute of empty array' do + get '/images' + expect(last_response.status).to eq(200) + end + end + + describe '#upload' do + before do + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP + QBoAAAAASUVORK5CYII='.unpack1('m') + File.binwrite('test.png', png) + end + let(:image) { Rack::Test::UploadedFile.new('test.png', 'image/png') } + + it 'returns a image payload with upload' do + post '/images/upload', { 'image' => image } + expect(last_response.status).to eq(200) + end + end + + describe '#upload_nested' do + before do + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP + QBoAAAAASUVORK5CYII='.unpack1('m') + File.binwrite('test.png', png) + end + let(:image) { Rack::Test::UploadedFile.new('test.png', 'image/png') } + + it 'returns a image payload with upload nested' do + post '/images/upload_nested', { nested_image: { image: image, caption: 'Some caption' } } + expect(last_response.status).to eq(200) + end + end + + describe '#upload_multiple' do + before do + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP + QBoAAAAASUVORK5CYII='.unpack1('m') + File.binwrite('test.png', png) + end + let(:image) { Rack::Test::UploadedFile.new('test.png', 'image/png') } + + it 'returns a image payload with upload multiple' do + post '/images/upload_multiple', { images: [image, image] } + expect(last_response.status).to eq(200) + end + end + + describe '#upload_multiple_nested' do + before do + png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP + QBoAAAAASUVORK5CYII='.unpack1('m') + File.binwrite('test.png', png) + end + let(:image) { Rack::Test::UploadedFile.new('test.png', 'image/png') } + + it 'returns a image payload with upload multiple nested' do + post '/images/upload_multiple_nested', { images: [{ image: image }, { image: image }] } + expect(last_response.status).to eq(200) + end + end +end + +RSpec.describe 'SecretKey securityScheme', + type: :request, + openapi: { security: [{ 'SecretApiKeyAuth' => [] }] } do + describe '#secret_items' do + it 'authorizes with secret key' do + get '/secret_items', nil, + { + 'Secret-Key' => '42', + } + expect(last_response.status).to eq(200) + end + end +end + +RSpec.describe 'Extra routes', type: :request do + describe '#test_block', openapi: { deprecated: true } do + it 'returns the block content' do + get '/test_block' + expect(last_response.status).to eq(200) + end + end +end + +RSpec.describe 'Engine test', type: :request do + describe 'engine routes' do + it 'returns some content from the engine' do + get '/my_engine/test' + expect(last_response.status).to eq(200) + end + end +end + +RSpec.describe 'Engine extra routes', type: :request do + describe '#test' do + it 'returns the block content' do + get '/my_engine/eng/example' + expect(last_response.status).to eq(200) + end + end +end + +RSpec.describe 'Namespace test', type: :request do + describe '/admin/masters/extensions' do + it 'returns some content' do + get '/admin/masters/extensions' + expect(last_response.status).to eq(200) + end + + it 'creates a content' do + post '/admin/masters/extensions' + expect(last_response.status).to eq(200) + end + end +end diff --git a/spec/requests/rails_smart_merge_spec.rb b/spec/requests/rails_smart_merge_spec.rb index 4b1da2a1..c65b81bc 100644 --- a/spec/requests/rails_smart_merge_spec.rb +++ b/spec/requests/rails_smart_merge_spec.rb @@ -4,12 +4,12 @@ ENV['RAILS_ENV'] ||= 'test' ENV['OPENAPI_OUTPUT'] ||= 'json' -require File.expand_path('../rails/config/environment', __dir__) +require File.expand_path('../apps/rails/config/environment', __dir__) require 'rspec/rails' RSpec::OpenAPI.request_headers = %w[X-Authorization-Token] RSpec::OpenAPI.response_headers = %w[X-Cursor] -RSpec::OpenAPI.path = File.expand_path("../rails/doc/smart/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) +RSpec::OpenAPI.path = File.expand_path("../apps/rails/doc/smart/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) RSpec::OpenAPI.comment = <<~COMMENT This file is auto-generated by rspec-openapi https://github.com/k0kubun/rspec-openapi diff --git a/spec/requests/rails_spec.rb b/spec/requests/rails_spec.rb index ed90b7be..c4e9ca5e 100644 --- a/spec/requests/rails_spec.rb +++ b/spec/requests/rails_spec.rb @@ -4,13 +4,13 @@ ENV['RAILS_ENV'] ||= 'test' ENV['OPENAPI_OUTPUT'] ||= 'yaml' -require File.expand_path('../rails/config/environment', __dir__) +require File.expand_path('../apps/rails/config/environment', __dir__) require 'rspec/rails' RSpec::OpenAPI.title = 'OpenAPI Documentation' RSpec::OpenAPI.request_headers = %w[X-Authorization-Token Secret-Key] RSpec::OpenAPI.response_headers = %w[X-Cursor] -RSpec::OpenAPI.path = File.expand_path("../rails/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) +RSpec::OpenAPI.path = File.expand_path("../apps/rails/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) RSpec::OpenAPI.ignored_paths = ['/admin/masters/extensions'] RSpec::OpenAPI.comment = <<~COMMENT This file is auto-generated by rspec-openapi https://github.com/k0kubun/rspec-openapi diff --git a/spec/requests/roda_spec.rb b/spec/requests/roda_spec.rb index 48b47e07..c434cac8 100644 --- a/spec/requests/roda_spec.rb +++ b/spec/requests/roda_spec.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -require_relative '../roda/roda_app' +require_relative '../apps/roda/roda_app' require 'json' require 'rack/test' ENV['OPENAPI_OUTPUT'] ||= 'yaml' RSpec::OpenAPI.title = 'OpenAPI Documentation' -RSpec::OpenAPI.path = File.expand_path("../roda/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) +RSpec::OpenAPI.path = File.expand_path("../apps/roda/doc/openapi.#{ENV.fetch('OPENAPI_OUTPUT', nil)}", __dir__) RSpec::OpenAPI.ignored_paths = ['/admin/masters/extensions'] RSpec::OpenAPI.description_builder = lambda do |example| diff --git a/spec/rspec/hanami_spec.rb b/spec/rspec/hanami_spec.rb new file mode 100644 index 00000000..57cdbd4e --- /dev/null +++ b/spec/rspec/hanami_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +return unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') + +require 'spec_helper' +require 'yaml' +require 'json' +require 'pry' + +RSpec.describe 'hanami request spec' do + include SpecHelper + + describe 'yaml output' do + let(:openapi_path) do + File.expand_path('spec/apps/hanami/doc/openapi.yaml', repo_root) + end + + it 'generates the same spec/apps/hanami/doc/openapi.yaml' do + org_yaml = YAML.safe_load(File.read(openapi_path)) + rspec 'spec/requests/hanami_spec.rb', openapi: true, output: :yaml + new_yaml = YAML.safe_load(File.read(openapi_path)) + expect(new_yaml).to eq org_yaml + end + end + + describe 'json' do + let(:openapi_path) do + File.expand_path('spec/apps/hanami/doc/openapi.json', repo_root) + end + + it 'generates the same spec/apps/hanami/doc/openapi.json' do + org_json = JSON.parse(File.read(openapi_path)) + rspec 'spec/requests/hanami_spec.rb', openapi: true, output: :json + new_json = JSON.parse(File.read(openapi_path)) + expect(new_json).to eq org_json + end + end +end diff --git a/spec/rspec/rack_test_spec.rb b/spec/rspec/rack_test_spec.rb index 40866ec2..c8735398 100644 --- a/spec/rspec/rack_test_spec.rb +++ b/spec/rspec/rack_test_spec.rb @@ -9,10 +9,10 @@ describe 'yaml output' do let(:openapi_path) do - File.expand_path('spec/roda/doc/openapi.yaml', repo_root) + File.expand_path('spec/apps/roda/doc/openapi.yaml', repo_root) end - it 'generates the same spec/roda/doc/openapi.yaml' do + it 'generates the same spec/apps/roda/doc/openapi.yaml' do org_yaml = YAML.safe_load(File.read(openapi_path)) rspec 'spec/requests/roda_spec.rb', openapi: true new_yaml = YAML.safe_load(File.read(openapi_path)) @@ -22,10 +22,10 @@ describe 'json output' do let(:openapi_path) do - File.expand_path('spec/roda/doc/openapi.json', repo_root) + File.expand_path('spec/apps/roda/doc/openapi.json', repo_root) end - it 'generates the same spec/roda/doc/openapi.json' do + it 'generates the same spec/apps/roda/doc/openapi.json' do org_yaml = YAML.safe_load(File.read(openapi_path)) rspec 'spec/requests/roda_spec.rb', openapi: true, output: :json new_yaml = YAML.safe_load(File.read(openapi_path)) diff --git a/spec/rspec/rails_spec.rb b/spec/rspec/rails_spec.rb index 7e310b64..aa15a33e 100644 --- a/spec/rspec/rails_spec.rb +++ b/spec/rspec/rails_spec.rb @@ -10,10 +10,10 @@ describe 'yaml output' do let(:openapi_path) do - File.expand_path('spec/rails/doc/openapi.yaml', repo_root) + File.expand_path('spec/apps/rails/doc/openapi.yaml', repo_root) end - it 'generates the same spec/rails/doc/openapi.yaml' do + it 'generates the same spec/apps/rails/doc/openapi.yaml' do org_yaml = YAML.safe_load(File.read(openapi_path)) rspec 'spec/requests/rails_spec.rb', openapi: true, output: :yaml new_yaml = YAML.safe_load(File.read(openapi_path)) @@ -23,10 +23,10 @@ describe 'json' do let(:openapi_path) do - File.expand_path('spec/rails/doc/openapi.json', repo_root) + File.expand_path('spec/apps/rails/doc/openapi.json', repo_root) end - it 'generates the same spec/rails/doc/openapi.json' do + it 'generates the same spec/apps/rails/doc/openapi.json' do org_json = JSON.parse(File.read(openapi_path)) rspec 'spec/requests/rails_spec.rb', openapi: true, output: :json new_json = JSON.parse(File.read(openapi_path)) @@ -36,14 +36,14 @@ describe 'smart merge' do let(:openapi_path) do - File.expand_path('spec/rails/doc/smart/openapi.yaml', repo_root) + File.expand_path('spec/apps/rails/doc/smart/openapi.yaml', repo_root) end let(:expected_path) do - File.expand_path('spec/rails/doc/smart/expected.yaml', repo_root) + File.expand_path('spec/apps/rails/doc/smart/expected.yaml', repo_root) end - it 'updates the spec/rails/doc/smart/openapi.yaml as same as in expected.yaml' do + it 'updates the spec/apps/rails/doc/smart/openapi.yaml as same as in expected.yaml' do original_source = File.read(openapi_path) begin rspec 'spec/requests/rails_smart_merge_spec.rb', openapi: true, output: :yaml