Add specs and JSON schemas

Include test helpers
This commit is contained in:
Matt-Yorkley
2021-10-11 11:28:51 +01:00
committed by Maikel Linke
parent 39f4feed4a
commit 76f14a03c6
9 changed files with 308 additions and 1 deletions

View File

@@ -27,11 +27,16 @@ Metrics/BlockLength:
"class_eval",
"collection",
"context",
"delete",
"describe",
"feature",
"get",
"it",
"member",
"namespace",
"path",
"post",
"put",
"resource",
"resources",
"scenario",

View File

@@ -466,6 +466,7 @@ Metrics/BlockLength:
- 'spec/lib/open_food_network/group_buy_report_spec.rb'
- 'spec/requests/api/orders_spec.rb'
- 'spec/spec_helper.rb'
- 'spec/swagger_helper.rb'
- 'spec/support/cancan_helper.rb'
- 'spec/support/matchers/select2_matchers.rb'
- 'spec/support/matchers/table_matchers.rb'

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
class CustomerSchema < JsonApiSchema
def self.object_name
"customer"
end
def self.attributes
{
id: { type: :integer, example: 1 },
enterprise_id: { type: :integer, example: 2 },
first_name: { type: :string, nullable: true, example: "Alice" },
last_name: { type: :string, nullable: true, example: "Springs" },
code: { type: :string, nullable: true, example: "BUYER1" },
email: { type: :string, example: "alice@example.com" }
}
end
def self.required_attributes
[:enterprise_id, :email]
end
end

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
class ErrorsSchema
def self.schema
{
type: :object,
properties: {
errors: {
type: :array,
items: {
type: :object,
properties: {
title: { type: :string },
detail: { type: :string },
source: { type: :object }
},
required: [:detail]
}
}
},
required: [:errors]
}
end
end

View File

@@ -0,0 +1,65 @@
# frozen_string_literal: true
class JsonApiSchema
class << self
def attributes
{}
end
def required_attributes
[]
end
def all_attributes
attributes.keys
end
def schema(options = {})
{
type: :object,
properties: {
data: {
type: :object,
properties: data_properties(**options)
},
meta: { type: :object }
},
required: [:data]
}
end
def collection(options)
{
type: :object,
properties: {
data: {
type: :array,
items: {
type: :object,
properties: data_properties(**options)
}
},
meta: { type: :object }
},
required: [:data]
}
end
private
def data_properties(require_all: false)
required = require_all ? all_attributes : required_attributes
{
id: { type: :string, example: "1" },
type: { type: :string, example: object_name },
attributes: {
type: :object,
properties: attributes,
required: required
},
relationships: { type: :object }
}
end
end
end

View File

@@ -5,7 +5,7 @@ module Api
class CustomerSerializer
include JSONAPI::Serializer
attributes :id, :enterprise_id, :name, :code, :email
attributes :id, :enterprise_id, :first_name, :last_name, :code, :email
belongs_to :enterprise, record_type: :enterprise, serializer: :id
end

View File

@@ -0,0 +1,166 @@
# frozen_string_literal: true
require "swagger_helper"
describe "Customers", type: :request do
let!(:enterprise1) { create(:enterprise) }
let!(:enterprise2) { create(:enterprise) }
let!(:customer1) { create(:customer, enterprise: enterprise1) }
let!(:customer2) { create(:customer, enterprise: enterprise1) }
let!(:customer3) { create(:customer, enterprise: enterprise2) }
before { login_as enterprise1.owner }
path "/api/v1/customers" do
get "List customers" do
tags "Customers"
parameter name: :enterprise_id, in: :query, type: :string
produces "application/json"
response "200", "Customers list" do
param(:enterprise_id) { enterprise1.id }
schema CustomerSchema.collection(require_all: true)
run_test!
end
end
describe "returning results based on permissions" do
context "as an enterprise owner" do
before { login_as enterprise1.owner }
it "returns customers of enterprises the user manages" do
get "/api/v1/customers"
expect(json_response_ids).to eq [customer1.id.to_s, customer2.id.to_s]
end
end
context "as another enterprise owner" do
before { login_as enterprise2.owner }
it "returns customers of enterprises the user manages" do
get "/api/v1/customers"
expect(json_response_ids).to eq [customer3.id.to_s]
end
end
end
post "Create customer" do
tags "Customers"
consumes "application/json"
produces "application/json"
parameter name: :customer, in: :body, schema: {
type: :object,
properties: CustomerSchema.attributes.except(:id),
required: CustomerSchema.required_attributes
}
response "201", "Customer created" do
param(:customer) do
{
email: "test@example.com",
enterprise_id: enterprise1.id.to_s
}
end
schema CustomerSchema.schema(require_all: true)
run_test!
end
response "422", "Unprocessable entity" do
param(:customer) { {} }
schema ErrorsSchema.schema
run_test!
end
end
end
path "/api/v1/customers/{id}" do
get "Show customer" do
tags "Customers"
parameter name: :id, in: :path, type: :string
produces "application/json"
response "200", "Customer" do
param(:id) { customer1.id }
schema CustomerSchema.schema(require_all: true)
run_test!
end
response "404", "Not found" do
param(:id) { 0 }
schema ErrorsSchema.schema
run_test! do
expect(json_error_detail).to eq "The resource you were looking for could not be found."
end
end
end
put "Update customer" do
tags "Customers"
parameter name: :id, in: :path, type: :string
consumes "application/json"
produces "application/json"
parameter name: :customer, in: :body, schema: {
type: :object,
properties: CustomerSchema.attributes,
required: CustomerSchema.required_attributes
}
response "200", "Customer updated" do
param(:id) { customer1.id }
param(:customer) do
{
id: customer1.id.to_s,
email: "test@example.com",
enterprise_id: enterprise1.id.to_s
}
end
schema CustomerSchema.schema(require_all: true)
run_test!
end
response "422", "Unprocessable entity" do
param(:id) { customer1.id }
param(:customer) { {} }
schema ErrorsSchema.schema
run_test!
end
end
delete "Delete customer" do
tags "Customers"
parameter name: :id, in: :path, type: :string
produces "application/json"
response "200", "Customer deleted" do
param(:id) { customer1.id }
schema CustomerSchema.schema(require_all: true)
run_test!
end
end
end
path "/api/v1/enterprises/{enterprise_id}/customers" do
get "List customers of an enterprise" do
tags "Customers", "Enterprises"
parameter name: :enterprise_id, in: :path, type: :string, required: true
produces "application/json"
response "200", "Customers list" do
param(:enterprise_id) { enterprise1.id }
schema CustomerSchema.collection(require_all: true)
run_test!
end
end
end
end

View File

@@ -14,6 +14,14 @@ module OpenFoodNetwork
end
end
def json_response_ids
json_response[:data].map{ |item| item["id"] }
end
def json_error_detail
json_response[:errors][0][:detail]
end
def assert_unauthorized!
expect(json_response).to eq("error" => "You are not authorized to perform that action.")
expect(response.status).to eq 401

View File

@@ -3,6 +3,9 @@
require 'spec_helper'
RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers, type: :request
config.include OpenFoodNetwork::ApiHelper, type: :request
# Specify a root folder where Swagger JSON files are generated
# NOTE: If you're using the rswag-api to serve API descriptions, you'll need
# to ensure that it's configured to serve Swagger from the same folder
@@ -21,6 +24,12 @@ RSpec.configure do |config|
title: 'API V1',
version: 'v1'
},
components: {
schemas: {
error_response: ErrorsSchema.schema,
customer: CustomerSchema.schema
}
},
paths: {},
servers: [
{ url: "/" }
@@ -41,3 +50,10 @@ RSpec.configure do |config|
# Defaults to json. Accepts ':json' and ':yaml'.
config.swagger_format = :yaml
end
module RswagExtension
def param(args, &block)
public_send(:let, args) { instance_eval(&block) }
end
end
Rswag::Specs::ExampleGroupHelpers.prepend RswagExtension