Skip to content

Commit 63dc7a2

Browse files
author
yrlu
committed
add spec
1 parent 59b2380 commit 63dc7a2

9 files changed

Lines changed: 343 additions & 24 deletions

File tree

.github/workflows/ci.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
ruby: ['2.7', '3.0', '3.1']
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Ruby
20+
uses: ruby/setup-ruby@v1
21+
with:
22+
ruby-version: ${{ matrix.ruby }}
23+
bundler-cache: true
24+
25+
- name: Run unit + integration tests
26+
run: bundle exec rake spec

.github/workflows/gempush.yml

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,54 @@
1-
name: jsonapi-swagger
1+
name: Release
22

33
on:
4-
pull_request:
5-
branches:
6-
- master
74
push:
8-
branches:
9-
- master
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
108

119
jobs:
12-
build:
13-
name: Build + Publish
10+
test:
11+
name: Test (Ruby ${{ matrix.ruby }})
1412
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
ruby: ['2.7', '3.0', '3.1']
1517

1618
steps:
17-
- uses: actions/checkout@master
18-
- name: Set up Ruby 2.6
19-
uses: actions/setup-ruby@v1
20-
with:
21-
version: 2.6.x
22-
23-
- name: Publish to RubyGems
24-
run: |
25-
mkdir -p $HOME/.gem
26-
touch $HOME/.gem/credentials
27-
chmod 0600 $HOME/.gem/credentials
28-
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
29-
gem build *.gemspec
30-
gem push *.gem
31-
env:
32-
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
19+
- uses: actions/checkout@v4
20+
21+
- name: Set up Ruby
22+
uses: ruby/setup-ruby@v1
23+
with:
24+
ruby-version: ${{ matrix.ruby }}
25+
bundler-cache: true
26+
27+
- name: Run unit + integration tests
28+
run: bundle exec rake spec
29+
30+
publish:
31+
name: Publish to RubyGems
32+
runs-on: ubuntu-latest
33+
needs: test
34+
35+
steps:
36+
- uses: actions/checkout@v4
37+
38+
- name: Set up Ruby
39+
uses: ruby/setup-ruby@v1
40+
with:
41+
ruby-version: '3.1'
42+
43+
- name: Build gem
44+
run: gem build jsonapi-swagger.gemspec
45+
46+
- name: Publish gem
47+
run: |
48+
mkdir -p $HOME/.gem
49+
touch $HOME/.gem/credentials
50+
chmod 0600 $HOME/.gem/credentials
51+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
52+
gem push jsonapi-swagger-*.gem
53+
env:
54+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}

Rakefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
# frozen_string_literal: true
22

33
require 'bundler/gem_tasks'
4+
5+
begin
6+
require 'rspec/core/rake_task'
7+
RSpec::Core::RakeTask.new(:spec)
8+
rescue LoadError
9+
# RSpec not available in all environments.
10+
end
411
task default: :spec

jsonapi-swagger.gemspec

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ Gem::Specification.new do |spec|
2222

2323
spec.add_development_dependency 'bundler', '~> 2.0'
2424
spec.add_development_dependency 'rake', '>= 12.3.3'
25+
spec.add_development_dependency 'rspec', '~> 3.0'
26+
spec.add_development_dependency 'activesupport', '>= 5.2', '< 7.0'
27+
spec.add_development_dependency 'i18n', '>= 0.7', '< 2.0'
2528
spec.add_development_dependency 'rubocop', '~> 0.67'
2629
spec.add_development_dependency 'rswag', '~>2.0'
2730
end
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
require 'erb'
6+
require 'fileutils'
7+
require 'ostruct'
8+
require 'tmpdir'
9+
10+
module Rails
11+
module Generators
12+
class NamedBase
13+
class << self
14+
attr_accessor :_source_root, :_destination_root
15+
16+
def desc(*) = nil
17+
18+
def source_root(path = nil)
19+
self._source_root = path if path
20+
_source_root
21+
end
22+
23+
def destination_root(path = nil)
24+
self._destination_root = path if path
25+
_destination_root
26+
end
27+
end
28+
29+
def initialize(name)
30+
@name = name
31+
end
32+
33+
def file_name
34+
@name.to_s.underscore
35+
end
36+
37+
def class_path
38+
[]
39+
end
40+
41+
def template(src, dest)
42+
src_path = File.join(self.class.source_root, src)
43+
out_path = File.join(self.class.destination_root, dest)
44+
FileUtils.mkdir_p(File.dirname(out_path))
45+
File.write(out_path, ERB.new(File.read(src_path), trim_mode: '-').result(binding))
46+
end
47+
end
48+
end
49+
end
50+
51+
require 'jsonapi/swagger/json'
52+
require 'jsonapi/swagger/resource'
53+
require 'jsonapi/swagger/resources/jsonapi_resource'
54+
require 'generators/jsonapi/swagger/swagger_generator'
55+
56+
RSpec.describe Jsonapi::SwaggerGenerator do
57+
around do |example|
58+
Dir.mktmpdir('jsonapi-swagger-spec') do |dir|
59+
described_class.destination_root(dir)
60+
example.run
61+
end
62+
end
63+
64+
before do
65+
stub_const('JSONAPI', Module.new)
66+
stub_const('JSONAPI::Resource', Class.new)
67+
68+
stub_const('User', Class.new)
69+
User.define_singleton_method(:columns) do
70+
[
71+
OpenStruct.new(name: 'id', type: :integer, null: false, comment: 'ID'),
72+
OpenStruct.new(name: 'name', type: :string, null: true, comment: 'Name')
73+
]
74+
end
75+
76+
relation = OpenStruct.new(class_name: 'Company', table_name: 'companies')
77+
relation.define_singleton_method(:belongs_to?) { true }
78+
79+
resource = Class.new(JSONAPI::Resource) do
80+
def self._attributes = { id: nil, name: nil }
81+
def self._relationships = { company: OpenStruct.new(class_name: 'Company', table_name: 'companies') }
82+
def self.sortable_fields = [:name]
83+
def self.creatable_fields = [:name]
84+
def self.updatable_fields = [:name]
85+
def self.filters = { name: {} }
86+
def self.mutable? = true
87+
end
88+
89+
# Ensure relationship object has belongs_to? and table_name.
90+
resource._relationships[:company].define_singleton_method(:belongs_to?) { true }
91+
92+
stub_const('UserResource', resource)
93+
94+
Jsonapi::Swagger.config do |c|
95+
c.use_rswag = false
96+
c.base_path = '/api'
97+
c.file_path = 'v1/swagger.json'
98+
end
99+
end
100+
101+
it 'generates swagger JSON via template' do
102+
gen = described_class.new('User')
103+
gen.create_swagger_file
104+
105+
out = File.join(described_class.destination_root, 'swagger', 'v1', 'swagger.json')
106+
expect(File.exist?(out)).to eq(true)
107+
108+
json = JSON.parse(File.read(out))
109+
expect(json['swagger']).to eq('2.0')
110+
expect(json['basePath']).to eq('/api')
111+
expect(json['paths']).to include('/users')
112+
expect(json['paths']).to include('/users/{id}')
113+
expect(json['paths']['/users']).to include('post')
114+
end
115+
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe Jsonapi::Swagger do
6+
it 'provides defaults' do
7+
expect(described_class.version).to eq('2.0')
8+
expect(described_class.info).to eq({ title: 'API V1', version: 'V1' })
9+
expect(described_class.file_path).to eq('v1/swagger.json')
10+
expect(described_class.use_rswag).to eq(false)
11+
expect(described_class.attribute_default).to eq({ type: :string, nullable: true, comment: nil })
12+
end
13+
14+
it 'allows configuration via block' do
15+
described_class.config do |c|
16+
c.version = '2.1'
17+
c.info = { title: 'API', version: 'V2' }
18+
c.file_path = 'v2/swagger.json'
19+
c.base_path = '/api'
20+
c.use_rswag = true
21+
end
22+
23+
expect(described_class.version).to eq('2.1')
24+
expect(described_class.info).to eq({ title: 'API', version: 'V2' })
25+
expect(described_class.file_path).to eq('v2/swagger.json')
26+
expect(described_class.base_path).to eq('/api')
27+
expect(described_class.use_rswag).to eq(true)
28+
end
29+
end

spec/jsonapi/swagger/json_spec.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
require 'tempfile'
5+
6+
RSpec.describe Jsonapi::Swagger::Json do
7+
describe '#parse_doc' do
8+
it 'returns an empty hash if file does not exist' do
9+
json = described_class.new('nope/swagger.json')
10+
expect(json.parse_doc).to eq({})
11+
end
12+
13+
it 'returns an empty hash if content is not JSON' do
14+
file = Tempfile.new(['swagger', '.json'])
15+
file.write('not-json')
16+
file.flush
17+
18+
json = described_class.new(file.path)
19+
expect(json.parse_doc).to eq({})
20+
ensure
21+
file.close!
22+
end
23+
24+
it 'parses valid JSON' do
25+
file = Tempfile.new(['swagger', '.json'])
26+
file.write('{"paths":{}}')
27+
file.flush
28+
29+
json = described_class.new(file.path)
30+
expect(json.parse_doc).to eq({ 'paths' => {} })
31+
ensure
32+
file.close!
33+
end
34+
end
35+
end
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe Jsonapi::Swagger::Resource do
6+
before do
7+
stub_const('JSONAPI', Module.new)
8+
stub_const('JSONAPI::Resource', Class.new)
9+
stub_const('JSONAPI::Serializable', Module.new)
10+
stub_const('JSONAPI::Serializable::Resource', Class.new)
11+
stub_const('FastJsonapi', Module.new)
12+
stub_const('FastJsonapi::ObjectSerializer', Class.new)
13+
end
14+
15+
it 'wraps JSONAPI::Resource subclasses' do
16+
klass = Class.new(JSONAPI::Resource) do
17+
def self._attributes = { id: {}, name: {} }
18+
def self._relationships = {}
19+
def self.sortable_fields = []
20+
def self.creatable_fields = []
21+
def self.updatable_fields = []
22+
def self.filters = {}
23+
def self.mutable? = false
24+
end
25+
stub_const('UserResource', klass)
26+
27+
wrapper = described_class.with('User')
28+
expect(wrapper).to be_a(Jsonapi::Swagger::JsonapiResource)
29+
expect(wrapper.attributes).to eq({ id: {}, name: {} })
30+
end
31+
32+
it 'wraps JSONAPI::Serializable::Resource subclasses' do
33+
klass = Class.new(JSONAPI::Serializable::Resource) do
34+
def self.type_val = :users
35+
def self.attribute_blocks = { id: nil, name: nil }
36+
def self.relationship_blocks = { company: nil }
37+
def self.link_blocks = {}
38+
end
39+
stub_const('SerializableUser', klass)
40+
41+
wrapper = described_class.with('User')
42+
expect(wrapper).to be_a(Jsonapi::Swagger::SerializableResource)
43+
expect(wrapper.attributes).to eq({ id: nil, name: nil })
44+
expect(wrapper.relationships.keys).to eq([:company])
45+
end
46+
47+
it 'wraps FastJsonapi::ObjectSerializer subclasses' do
48+
klass = Class.new(FastJsonapi::ObjectSerializer) do
49+
def self.attributes_to_serialize = { id: {}, name: {} }
50+
def self.relationships_to_serialize = {}
51+
end
52+
stub_const('UserSerializer', klass)
53+
54+
wrapper = described_class.with('User')
55+
expect(wrapper).to be_a(Jsonapi::Swagger::FastJsonapiResource)
56+
expect(wrapper.attributes).to eq({ id: {}, name: {} })
57+
end
58+
59+
it 'raises when no supported resource is found' do
60+
expect { described_class.with('User') }.to raise_error(Jsonapi::Swagger::Error, /not support/i)
61+
end
62+
end

spec/spec_helper.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# frozen_string_literal: true
2+
3+
require 'json'
4+
5+
require 'active_support'
6+
require 'active_support/core_ext/hash/except'
7+
require 'active_support/core_ext/object/blank'
8+
require 'active_support/core_ext/object/try'
9+
require 'active_support/core_ext/string/inflections'
10+
11+
require 'i18n'
12+
I18n.enforce_available_locales = false if I18n.respond_to?(:enforce_available_locales=)
13+
14+
require 'jsonapi/swagger'
15+
16+
RSpec.configure do |config|
17+
config.disable_monkey_patching!
18+
config.order = :random
19+
Kernel.srand config.seed
20+
end

0 commit comments

Comments
 (0)