| @@ -46,6 +46,8 @@ group :development, :test do | |||||
| # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] | # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] | ||||
| gem "rubocop-rails-omakase", require: false | gem "rubocop-rails-omakase", require: false | ||||
| gem 'factory_bot_rails' | |||||
| end | end | ||||
| @@ -99,6 +99,11 @@ GEM | |||||
| drb (2.2.1) | drb (2.2.1) | ||||
| ed25519 (1.4.0) | ed25519 (1.4.0) | ||||
| erubi (1.13.1) | erubi (1.13.1) | ||||
| factory_bot (6.5.6) | |||||
| activesupport (>= 6.1.0) | |||||
| factory_bot_rails (6.5.1) | |||||
| factory_bot (~> 6.5) | |||||
| railties (>= 6.1.0) | |||||
| ffi (1.17.2-aarch64-linux-gnu) | ffi (1.17.2-aarch64-linux-gnu) | ||||
| ffi (1.17.2-aarch64-linux-musl) | ffi (1.17.2-aarch64-linux-musl) | ||||
| ffi (1.17.2-arm-linux-gnu) | ffi (1.17.2-arm-linux-gnu) | ||||
| @@ -441,6 +446,7 @@ DEPENDENCIES | |||||
| diff-lcs | diff-lcs | ||||
| discard | discard | ||||
| dotenv-rails | dotenv-rails | ||||
| factory_bot_rails | |||||
| gollum | gollum | ||||
| image_processing (~> 1.14) | image_processing (~> 1.14) | ||||
| jwt | jwt | ||||
| @@ -18,6 +18,8 @@ class UsersController < ApplicationController | |||||
| end | end | ||||
| def renew | def renew | ||||
| return head :unauthorized unless current_user | |||||
| user = current_user | user = current_user | ||||
| user.inheritance_code = SecureRandom.uuid | user.inheritance_code = SecureRandom.uuid | ||||
| user.save! | user.save! | ||||
| @@ -11,6 +11,8 @@ module Backend | |||||
| # Initialize configuration defaults for originally generated Rails version. | # Initialize configuration defaults for originally generated Rails version. | ||||
| config.load_defaults 8.0 | config.load_defaults 8.0 | ||||
| config.i18n.default_locale = :ja | |||||
| # Please, add to the `ignore` list any other `lib` subdirectories that do | # Please, add to the `ignore` list any other `lib` subdirectories that do | ||||
| # not contain `.rb` files, or that should not be reloaded or eager loaded. | # not contain `.rb` files, or that should not be reloaded or eager loaded. | ||||
| # Common ones are `templates`, `generators`, or `middleware`, for example. | # Common ones are `templates`, `generators`, or `middleware`, for example. | ||||
| @@ -0,0 +1,5 @@ | |||||
| FactoryBot.define do | |||||
| factory :tag_name do | |||||
| name { "tag-#{SecureRandom.hex(4)}" } | |||||
| end | |||||
| end | |||||
| @@ -0,0 +1,12 @@ | |||||
| FactoryBot.define do | |||||
| factory :tag do | |||||
| category { 'general' } | |||||
| post_count { 0 } | |||||
| association :tag_name | |||||
| trait :nico do | |||||
| category { 'nico' } | |||||
| tag_name { association(:tag_name, name: "nico:#{ SecureRandom.hex(4) }") } | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -0,0 +1,11 @@ | |||||
| FactoryBot.define do | |||||
| factory :user do | |||||
| name { "test-user" } | |||||
| inheritance_code { SecureRandom.uuid } | |||||
| role { "guest" } | |||||
| trait :member do | |||||
| role { "member" } | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -8,6 +8,10 @@ abort("The Rails environment is running in production mode!") if Rails.env.produ | |||||
| # that will avoid rails generators crashing because migrations haven't been run yet | # that will avoid rails generators crashing because migrations haven't been run yet | ||||
| # return unless Rails.env.test? | # return unless Rails.env.test? | ||||
| require 'rspec/rails' | require 'rspec/rails' | ||||
| Dir[Rails.root.join('spec/support/**/*.rb')].each do |f| | |||||
| require f | |||||
| end | |||||
| # Add additional requires below this line. Rails is not loaded until this point! | # Add additional requires below this line. Rails is not loaded until this point! | ||||
| # Requires supporting ruby files with custom matchers and macros, etc, in | # Requires supporting ruby files with custom matchers and macros, etc, in | ||||
| @@ -34,12 +38,16 @@ begin | |||||
| rescue ActiveRecord::PendingMigrationError => e | rescue ActiveRecord::PendingMigrationError => e | ||||
| abort e.to_s.strip | abort e.to_s.strip | ||||
| end | end | ||||
| Dir[Rails.root.join('spec/support/**/*.rb')].each do |f| | |||||
| require f | |||||
| end | |||||
| RSpec.configure do |config| | RSpec.configure do |config| | ||||
| config.include TestRecords | config.include TestRecords | ||||
| # FactoryBot の create / create_list を使へるやぅにする | |||||
| config.include FactoryBot::Syntax::Methods | |||||
| # request spec で helper 使へるやぅにする | |||||
| config.include AuthHelper, type: :request | |||||
| config.include JsonHelper, type: :request | |||||
| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures | ||||
| config.fixture_paths = [ | config.fixture_paths = [ | ||||
| Rails.root.join('spec/fixtures') | Rails.root.join('spec/fixtures') | ||||
| @@ -0,0 +1,38 @@ | |||||
| require 'rails_helper' | |||||
| RSpec.describe 'NicoTags', type: :request do | |||||
| describe 'GET /tags/nico' do | |||||
| it 'returns tags and next_cursor when overflowing limit' do | |||||
| create_list(:tag, 21, :nico) | |||||
| get '/tags/nico', params: { limit: 20 } | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json['tags'].size).to eq(20) | |||||
| expect(json['next_cursor']).to be_present | |||||
| end | |||||
| end | |||||
| describe 'PATCH /tags/nico/:id' do | |||||
| let(:member) { create(:user, :member) } | |||||
| let(:nico_tag) { create(:tag, :nico) } | |||||
| it '401 when not logged in' do | |||||
| sign_out | |||||
| patch "/tags/nico/#{nico_tag.id}", params: { tags: 'a b' } | |||||
| expect(response).to have_http_status(:unauthorized) | |||||
| end | |||||
| it '403 when not member' do | |||||
| sign_in_as(create(:user, role: 'guest')) | |||||
| patch "/tags/nico/#{nico_tag.id}", params: { tags: 'a b' } | |||||
| expect(response).to have_http_status(:forbidden) | |||||
| end | |||||
| it '400 when target is not nico category' do | |||||
| sign_in_as(member) | |||||
| non_nico = create(:tag, :general) | |||||
| patch "/tags/nico/#{non_nico.id}", params: { tags: 'a b' } | |||||
| expect(response).to have_http_status(:bad_request) | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -4,7 +4,7 @@ require 'rails_helper' | |||||
| RSpec.describe 'Posts API', type: :request do | RSpec.describe 'Posts API', type: :request do | ||||
| describe 'GET /posts' do | describe 'GET /posts' do | ||||
| it 'returns tags with name in JSON' do | it 'returns tags with name in JSON' do | ||||
| tn = TagName.create!(name: 'gen:spec_tag') | |||||
| tn = TagName.create!(name: 'spec_tag') | |||||
| tag = Tag.create!(tag_name: tn, category: 'general') | tag = Tag.create!(tag_name: tn, category: 'general') | ||||
| post = Post.create!(title: 'spec post', url: 'https://example.com/spec') | post = Post.create!(title: 'spec post', url: 'https://example.com/spec') | ||||
| @@ -14,7 +14,6 @@ RSpec.describe 'Posts API', type: :request do | |||||
| expect(response).to have_http_status(:ok) | expect(response).to have_http_status(:ok) | ||||
| json = JSON.parse(response.body) | |||||
| expect(json).to have_key('posts') | expect(json).to have_key('posts') | ||||
| expect(json['posts']).to be_a(Array) | expect(json['posts']).to be_a(Array) | ||||
| expect(json['posts']).not_to be_empty | expect(json['posts']).not_to be_empty | ||||
| @@ -24,7 +23,10 @@ RSpec.describe 'Posts API', type: :request do | |||||
| expect(tags).not_to be_empty | expect(tags).not_to be_empty | ||||
| expect(tags[0]).to have_key('name') | expect(tags[0]).to have_key('name') | ||||
| expect(tags[0]['name']).to eq('gen:spec_tag') | |||||
| expect(tags[0]['name']).to eq('spec_tag') | |||||
| expect(tags.map { |t| t['name'] }).to include('spec_tag') | |||||
| expect(tags[0]).to include('category') | |||||
| end | end | ||||
| end | end | ||||
| end | end | ||||
| @@ -0,0 +1,28 @@ | |||||
| require "rails_helper" | |||||
| RSpec.describe "Preview", type: :request do | |||||
| describe "GET /preview/title" do | |||||
| it "401 unless logged in" do | |||||
| sign_out | |||||
| get "/preview/title", params: { url: "example.com" } | |||||
| expect(response).to have_http_status(:unauthorized) | |||||
| end | |||||
| it "400 when url blank" do | |||||
| sign_in_as(create(:user)) | |||||
| get "/preview/title", params: { url: "" } | |||||
| expect(response).to have_http_status(:bad_request) | |||||
| end | |||||
| it "returns parsed title (stubbing URI.open)" do | |||||
| sign_in_as(create(:user)) | |||||
| fake_html = "<html><head><title> Hello </title></head></html>" | |||||
| allow(URI).to receive(:open).and_return(StringIO.new(fake_html)) | |||||
| get "/preview/title", params: { url: "example.com" } | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json["title"]).to eq("Hello") | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -44,5 +44,10 @@ RSpec.describe 'Tags API', type: :request do | |||||
| expect(json['id']).to eq(tag.id) | expect(json['id']).to eq(tag.id) | ||||
| expect(json['name']).to eq('spec_tag') | expect(json['name']).to eq('spec_tag') | ||||
| end | end | ||||
| it 'returns 404 when not found' do | |||||
| get "/tags/name/#{ CGI.escape('nope') }" | |||||
| expect(response).to have_http_status(:not_found) | |||||
| end | |||||
| end | end | ||||
| end | end | ||||
| @@ -0,0 +1,21 @@ | |||||
| require "rails_helper" | |||||
| RSpec.describe "Users", type: :request do | |||||
| describe "POST /users" do | |||||
| it "creates guest user and returns code" do | |||||
| post "/users" | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json["code"]).to be_present | |||||
| expect(json["user"]["role"]).to eq("guest") | |||||
| end | |||||
| end | |||||
| describe "POST /users/code/renew" do | |||||
| it "should be 401 when not logged in (recommended behavior)" do | |||||
| sign_out | |||||
| post "/users/code/renew" | |||||
| expect(response).to have_http_status(:unauthorized) | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -4,7 +4,7 @@ require 'securerandom' | |||||
| RSpec.describe 'Wiki API', type: :request do | RSpec.describe 'Wiki API', type: :request do | ||||
| let!(:user) { create_user_for_wiki! } | |||||
| let!(:user) { create_member_user! } | |||||
| let!(:tn) { TagName.create!(name: 'spec_wiki_title') } | let!(:tn) { TagName.create!(name: 'spec_wiki_title') } | ||||
| let!(:page) do | let!(:page) do | ||||
| @@ -38,5 +38,10 @@ RSpec.describe 'Wiki API', type: :request do | |||||
| expect(json['id']).to eq(page.id) | expect(json['id']).to eq(page.id) | ||||
| expect(json['title']).to eq('spec_wiki_title') | expect(json['title']).to eq('spec_wiki_title') | ||||
| end | end | ||||
| it 'returns 404 when not found' do | |||||
| get "/wiki/title/#{ CGI.escape('nope') }" | |||||
| expect(response).to have_http_status(:not_found) | |||||
| end | |||||
| end | end | ||||
| end | end | ||||
| @@ -0,0 +1,11 @@ | |||||
| module AuthHelper | |||||
| def sign_in_as(user) | |||||
| allow_any_instance_of(ApplicationController) | |||||
| .to receive(:current_user).and_return(user) | |||||
| end | |||||
| def sign_out | |||||
| allow_any_instance_of(ApplicationController) | |||||
| .to receive(:current_user).and_return(nil) | |||||
| end | |||||
| end | |||||
| @@ -0,0 +1,5 @@ | |||||
| module JsonHelper | |||||
| def json | |||||
| JSON.parse(response.body) | |||||
| end | |||||
| end | |||||