scorpio 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/test/blog.rb DELETED
@@ -1,117 +0,0 @@
1
- require 'sinatra'
2
- require 'api_hammer'
3
- require 'rack/accept'
4
- require 'logger'
5
-
6
- # app
7
-
8
- class Blog < Sinatra::Base
9
- include ApiHammer::Sinatra
10
- self.supported_media_types = ['application/json']
11
- set :static, false
12
- disable :protection
13
- logpath = Pathname.new('log/test.log')
14
- FileUtils.mkdir_p(logpath.dirname)
15
- set :logger, ::Logger.new(logpath)
16
- logger.level = ::Logger::INFO
17
- define_method(:logger) { self.class.logger }
18
- use_with_lint ApiHammer::RequestLogger, logger
19
-
20
- # prevent sinatra from using Sinatra::ShowExceptions so we can use ShowTextExceptions instead
21
- set :show_exceptions, false
22
- # allow errors to bubble past sinatra up to ShowTextExceptions
23
- set :raise_errors, true
24
- # ShowTextExceptions rescues ruby exceptions and gives a response of 500 with text/plain
25
- use_with_lint ApiHammer::ShowTextExceptions, :full_error => true, :logger => logger
26
- end
27
-
28
- # models
29
-
30
- require 'active_record'
31
- ActiveRecord::Base.logger = Blog.logger
32
- dbpath = Pathname.new('tmp/blog.sqlite3')
33
- FileUtils.mkdir_p(dbpath.dirname)
34
- dbpath.unlink if dbpath.exist?
35
- ActiveRecord::Base.establish_connection(
36
- :adapter => "sqlite3",
37
- :database => dbpath
38
- )
39
-
40
- ActiveRecord::Schema.define do
41
- create_table :articles do |table|
42
- table.column :title, :string
43
- table.column :author_id, :integer
44
- end
45
-
46
- create_table :authors do |table|
47
- table.column :name, :string
48
- end
49
- end
50
-
51
- # we will namespace the models under Blog so that the top-level namespace
52
- # can be used by the scorpio model classes
53
- class Blog
54
- class Article < ActiveRecord::Base
55
- # validates_enthusiasm_of :title
56
- validate { errors.add(:title, "with gusto!") if title && !title[/!\z/] }
57
- end
58
- class Author < ActiveRecord::Base
59
- end
60
- end
61
-
62
- # controllers
63
-
64
- class Blog
65
- get '/v1/articles' do
66
- check_accept
67
-
68
- articles = Blog::Article.all
69
- format_response(200, articles.map(&:serializable_hash))
70
- end
71
- get '/v1/articles_with_root' do
72
- check_accept
73
-
74
- articles = Blog::Article.all
75
- body = {
76
- # this is on the response schema, an array with items whose id indicates they are articles
77
- 'articles' => articles.map(&:serializable_hash),
78
- # in the response schema, a single article
79
- 'best_article' => articles.last.serializable_hash,
80
- # this is on the response schema, not indicating it is an article
81
- 'version' => 'v1',
82
- # this is not in the response schema at all
83
- 'note' => 'hi!',
84
- }
85
- format_response(200, body)
86
- end
87
- get '/v1/articles/:id' do |id|
88
- article = find_or_halt(Blog::Article, id: id)
89
- format_response(200, article.serializable_hash)
90
- end
91
- post '/v1/articles' do
92
- article = Blog::Article.create(parsed_body)
93
- if article.persisted?
94
- format_response(200, article.serializable_hash)
95
- else
96
- halt_unprocessable_entity(article.errors.messages)
97
- end
98
- end
99
- patch '/v1/articles/:id' do |id|
100
- article_attrs = parsed_body
101
- article = find_or_halt(Blog::Article, id: id)
102
-
103
- article.assign_attributes(article_attrs)
104
- saved = article.save
105
- if saved
106
- format_response(200, article.serializable_hash)
107
- else
108
- halt_unprocessable_entity(article.errors.messages)
109
- end
110
- end
111
-
112
- require 'database_cleaner'
113
- post '/v1/clean' do
114
- DatabaseCleaner.clean_with(:truncation)
115
- format_response(200, nil)
116
- end
117
- end
@@ -1,67 +0,0 @@
1
- discoveryVersion: v1
2
- name: blog
3
- title: "Scorpio Blog"
4
- description: "REST service for the Scorpio Blog"
5
- documentationLink: https://github.com/notEthan/scorpio
6
- servicePath: /v1
7
- resources:
8
- articles:
9
- methods:
10
- index:
11
- path: articles
12
- httpMethod: GET
13
- response:
14
- type: array
15
- items:
16
- $ref: https://blog.example.com/schemas/articles/v1.0.0
17
- index_with_root:
18
- path: articles_with_root
19
- httpMethod: GET
20
- response:
21
- type: object
22
- properties:
23
- articles:
24
- type: array
25
- items:
26
- $ref: https://blog.example.com/schemas/articles/v1.0.0
27
- best_article:
28
- $ref: https://blog.example.com/schemas/articles/v1.0.0
29
- version:
30
- type: string
31
- read:
32
- path: articles/{id}
33
- httpMethod: GET
34
- response:
35
- $ref: https://blog.example.com/schemas/articles/v1.0.0
36
- post:
37
- path: articles
38
- httpMethod: POST
39
- request:
40
- $ref: https://blog.example.com/schemas/articles/v1.0.0
41
- response:
42
- $ref: https://blog.example.com/schemas/articles/v1.0.0
43
- patch:
44
- path: articles/{id}
45
- httpMethod: PATCH
46
- request:
47
- $ref: https://blog.example.com/schemas/articles/v1.0.0
48
- response:
49
- $ref: https://blog.example.com/schemas/articles/v1.0.0
50
- clean:
51
- methods:
52
- clean:
53
- path: clean
54
- httpMethod: POST
55
- response:
56
- {}
57
- schemas:
58
- articles:
59
- id: https://blog.example.com/schemas/articles/v1.0.0
60
- type: object
61
- properties:
62
- id:
63
- type: integer
64
- title:
65
- type: string
66
- author_id:
67
- type: integer
@@ -1,49 +0,0 @@
1
- require 'logger'
2
- require 'api_hammer'
3
-
4
- # this is a virtual model to parent models representing resources of the blog. it sets
5
- # up connection information including base url, custom middleware or adapter for faraday.
6
- # it describes the API by setting the API document, but this class itself represents no
7
- # resources - it sets no resource_name and defines no schema_keys.
8
- class BlogModel < Scorpio::ResourceBase
9
- define_inheritable_accessor(:logger)
10
- logpath = Pathname.new('log/test.log')
11
- FileUtils.mkdir_p(logpath.dirname)
12
- self.logger = ::Logger.new(logpath)
13
-
14
- if ENV['SCORPIO_API_DESCRIPTION_FORMAT'] == 'rest_description'
15
- self.openapi_document = Scorpio::Google::RestDescription.new_jsi(YAML.load_file('test/blog.rest_description.yml')).to_openapi_document
16
- self.base_url = File.join("http://localhost:#{$blog_port || raise(Bug)}/", openapi_document.basePath)
17
- elsif ENV['SCORPIO_API_DESCRIPTION_FORMAT'] == 'openapi2'
18
- self.openapi_document = YAML.load_file('test/blog.openapi2.yml')
19
- self.base_url = File.join("http://localhost:#{$blog_port || raise(Bug)}/", openapi_document.basePath)
20
- elsif ENV['SCORPIO_API_DESCRIPTION_FORMAT'] == 'openapi3' || ENV['SCORPIO_API_DESCRIPTION_FORMAT'].nil?
21
- self.openapi_document = YAML.load_file('test/blog.openapi3.yml')
22
- self.server_variables = {
23
- 'scheme' => 'http',
24
- 'host' => 'localhost',
25
- 'port' => $blog_port || raise(Bug, '$blog_port is nil'),
26
- }
27
- else
28
- abort("bad SCORPIO_API_DESCRIPTION_FORMAT")
29
- end
30
- self.faraday_builder = -> (conn) {
31
- conn.request(:api_hammer_request_logger, logger)
32
- }
33
- end
34
-
35
- # this is a model of Article, a resource of the blog API. it sets the resource_name
36
- # to the key of the 'resources' section of the API (described by the api document
37
- # specified to BlogModel)
38
- class Article < BlogModel
39
- self.tag_name = 'articles'
40
- if openapi_document.v2?
41
- self.represented_schemas = [openapi_document.definitions['articles']]
42
- else
43
- self.represented_schemas = [openapi_document.components.schemas['articles']]
44
- end
45
- end
46
-
47
- class BlogClean < BlogModel
48
- self.tag_name = 'clean'
49
- end
data/test/scorpio_test.rb DELETED
@@ -1,105 +0,0 @@
1
- require_relative 'test_helper'
2
-
3
- class ScorpioTest < Minitest::Test
4
- def test_that_it_has_a_version_number
5
- refute_nil ::Scorpio::VERSION
6
- end
7
- end
8
-
9
- describe 'blog' do
10
- let(:blog_article) { Article.post('title' => "sports!") }
11
-
12
- it 'indexes articles' do
13
- blog_article
14
-
15
- articles = Article.index
16
-
17
- assert_equal(1, articles.size)
18
- article = articles[0]
19
- assert_equal(1, article['id'])
20
- assert(article.is_a?(Article))
21
- assert_equal('sports!', article['title'])
22
- assert_equal('sports!', article.title)
23
- end
24
- it 'indexes articles with root' do
25
- blog_article
26
-
27
- articles = Article.index_with_root
28
- assert_respond_to(articles, :to_hash)
29
- assert_equal('v1', articles['version'])
30
- assert_equal('hi!', articles['note'])
31
- assert_instance_of(Article, articles['best_article'])
32
- assert_equal(articles['articles'].last, articles['best_article'])
33
- assert_equal(1, articles['articles'].size)
34
- article = articles['articles'][0]
35
- assert_equal(1, article['id'])
36
- assert(article.is_a?(Article))
37
- assert_equal('sports!', article['title'])
38
- assert_equal('sports!', article.title)
39
- end
40
- it 'reads an article' do
41
- blog_article
42
- article = Article.read(id: blog_article.id)
43
- assert(article.is_a?(Article))
44
- assert_equal('sports!', article['title'])
45
- assert_equal('sports!', article.title)
46
- end
47
- it 'tries to read an article without a required path variable' do
48
- blog_article
49
- e = assert_raises(ArgumentError) do
50
- Article.read({})
51
- end
52
- assert_equal('path /articles/{id} for operation articles.read requires path_params which were missing: ["id"]',
53
- e.message)
54
- e = assert_raises(ArgumentError) do
55
- Article.read({id: ''})
56
- end
57
- assert_equal('path /articles/{id} for operation articles.read requires path_params which were empty: ["id"]',
58
- e.message)
59
- end
60
- it 'tries to read a nonexistent article' do
61
- err = assert_raises(Scorpio::NotFound404Error) do
62
- Article.read(id: 99)
63
- end
64
- assert_equal({"article" => ["Unknown article! id: 99"]}, JSI::Typelike.as_json(err.response_object['errors']))
65
- assert_match(/Unknown article! id: 99/, err.message)
66
- end
67
- it 'updates an article on the class' do
68
- blog_article
69
- Article.patch({id: blog_article.id, title: 'politics!'})
70
- assert_equal('politics!', Article.read(id: blog_article.id).title)
71
- end
72
- it 'updates an article on the instance' do
73
- blog_article
74
- article = Article.read(id: blog_article.id)
75
- article.title = 'politics!'
76
- article.patch
77
- assert_equal('politics!', Article.read(id: blog_article.id).title)
78
- end
79
- it 'updates an article with an unsuccessful response' do
80
- blog_article
81
- err = assert_raises(Scorpio::UnprocessableEntity422Error) do
82
- Article.patch({id: blog_article.id, title: 'politics?'})
83
- end
84
- assert_equal({"title" => ["with gusto!"]}, JSI::Typelike.as_json(err.response_object['errors']))
85
- assert_match(/with gusto!/, err.message)
86
- assert_equal('sports!', Article.read(id: blog_article.id).title)
87
- end
88
- it 'instantiates an article with bad argument' do
89
- assert_raises(ArgumentError) { Article.new("foo") }
90
- end
91
- it 'reports schema failure when the request does not match the request schema' do
92
- # TODO handle blame
93
- assert_raises(Scorpio::HTTPErrors::UnprocessableEntity422Error) do
94
- # title is supposed to be a string
95
- Article.post('title' => {'music' => '!'})
96
- end
97
- end
98
- it 'checks equality' do
99
- assert_equal(Article.read(id: blog_article.id), Article.read(id: blog_article.id))
100
- end
101
- it 'consistently keys a hash' do
102
- hash = {Article.read(id: blog_article.id) => 0}
103
- assert_equal(0, hash[Article.read(id: blog_article.id)])
104
- end
105
- end
data/test/test_helper.rb DELETED
@@ -1,86 +0,0 @@
1
- require 'coveralls'
2
- if Coveralls.will_run?
3
- Coveralls.wear!
4
- end
5
-
6
- require 'simplecov'
7
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
8
- require 'scorpio'
9
-
10
- require 'bundler/setup'
11
-
12
- # NO EXPECTATIONS
13
- ENV["MT_NO_EXPECTATIONS"] = ''
14
-
15
- require 'minitest/autorun'
16
- require 'minitest/around/spec'
17
- require 'minitest/reporters'
18
-
19
- Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
20
-
21
- require 'byebug'
22
-
23
- class ScorpioSpec < Minitest::Spec
24
- if ENV['SCORPIO_TEST_ALPHA']
25
- # :nocov:
26
- define_singleton_method(:test_order) { :alpha }
27
- # :nocov:
28
- end
29
-
30
- around do |test|
31
- test.call
32
- BlogClean.clean
33
- end
34
-
35
- def assert_equal exp, act, msg = nil
36
- msg = message(msg, E) { diff exp, act }
37
- assert exp == act, msg
38
- end
39
- end
40
-
41
- # register this to be the base class for specs instead of Minitest::Spec
42
- Minitest::Spec.register_spec_type(//, ScorpioSpec)
43
-
44
- # boot the blog application in a different process
45
-
46
- # find a free port
47
- server = TCPServer.new(0)
48
- $blog_port = server.addr[1]
49
- server.close
50
-
51
- $blog_pid = fork do
52
- require_relative 'blog'
53
-
54
- STDOUT.reopen(Scorpio.root.join('log/blog_webrick_stdout.log').open('a'))
55
- STDERR.reopen(Scorpio.root.join('log/blog_webrick_stderr.log').open('a'))
56
-
57
- trap('INT') { ::Rack::Handler::WEBrick.shutdown }
58
-
59
- ::Rack::Handler::WEBrick.run(::Blog, Port: $blog_port)
60
- end
61
-
62
- # wait for the server to become responsive
63
- running = false
64
- started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
65
- timeout = 30
66
- while !running
67
- require 'socket'
68
- begin
69
- sock=TCPSocket.new('localhost', $blog_port)
70
- running = true
71
- sock.close
72
- rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE
73
- if Process.clock_gettime(Process::CLOCK_MONOTONIC) > started + timeout
74
- raise $!.class, "Failed to connect to the server on port #{$blog_port} after #{timeout} seconds.\n\n#{$!.message}", $!.backtrace
75
- end
76
- sleep 2**-2
77
- STDOUT.write('.')
78
- end
79
- end
80
-
81
- Minitest.after_run do
82
- Process.kill('INT', $blog_pid)
83
- Process.waitpid
84
- end
85
-
86
- require_relative 'blog_scorpio_models'