mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-25 20:46:48 +00:00
463 lines
14 KiB
Ruby
463 lines
14 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "swagger_helper"
|
|
|
|
RSpec.describe "Customers", swagger_doc: "v1.yaml", feature: :api_v1 do
|
|
let!(:enterprise1) { create(:enterprise, name: "The Farm") }
|
|
let!(:enterprise2) { create(:enterprise) }
|
|
let!(:enterprise3) { create(:enterprise) }
|
|
|
|
let!(:customer1) {
|
|
create(
|
|
:customer,
|
|
enterprise: enterprise1,
|
|
created_manually: true,
|
|
terms_and_conditions_accepted_at: Time.zone.parse("2000-01-01"),
|
|
tag_list: ["long-term"],
|
|
ship_address: create(:address),
|
|
)
|
|
}
|
|
let!(:customer2) { create(:customer, enterprise: enterprise1, created_manually: true,) }
|
|
let!(:customer3) { create(:customer, enterprise: enterprise2, created_manually: true,) }
|
|
let(:json_response_ids) { json_response[:data]&.pluck(:id) }
|
|
let(:json_error_detail) { json_response[:errors][0][:detail] }
|
|
|
|
before do
|
|
login_as enterprise1.owner
|
|
end
|
|
|
|
path "/api/v1/customers" do
|
|
get "List customers" do
|
|
tags "Customers"
|
|
parameter name: :enterprise_id, in: :query, type: :string
|
|
parameter name: "extra_fields[customer]", in: :query, type: :string, example: :balance,
|
|
description: "Add extra fields to each customer"
|
|
produces "application/json"
|
|
|
|
response "200", "Customers list" do
|
|
let(:enterprise_id) { enterprise1.id }
|
|
let("extra_fields[customer]") { :balance }
|
|
schema '$ref': "#/components/schemas/customers_collection"
|
|
|
|
run_test!
|
|
end
|
|
end
|
|
|
|
describe "returning results based on permissions" do
|
|
context "as guest user" do
|
|
before { login_as nil }
|
|
|
|
it "returns no customers" do
|
|
get "/api/v1/customers"
|
|
expect(json_response_ids).to eq []
|
|
end
|
|
|
|
it "returns not even customers without user id" do
|
|
customer3.update!(user_id: nil)
|
|
|
|
get "/api/v1/customers"
|
|
expect(json_response_ids).to eq []
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
context "as a user who manages the enterprise" do
|
|
let!(:user){ enterprise3.users.first }
|
|
before do
|
|
EnterpriseRole.create!(user:, enterprise: enterprise1)
|
|
login_as user
|
|
end
|
|
|
|
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 an enterprise that has edit profile permission" do
|
|
let!(:user){ enterprise3.users.first }
|
|
before do
|
|
EnterpriseRelationship.create!(parent: enterprise1, child: enterprise3,
|
|
permissions_list: [:edit_profile])
|
|
login_as user
|
|
end
|
|
|
|
it "shoult not return customers of the managed enterprise" do
|
|
get "/api/v1/customers"
|
|
expect(json_response_ids).to eq []
|
|
end
|
|
end
|
|
|
|
context "with ransack params searching for specific customers" do
|
|
before { login_as enterprise2.owner }
|
|
|
|
it "does not show results the user doesn't have permissions to view" do
|
|
get "/api/v1/customers", params: { q: { id_eq: customer2.id } }
|
|
|
|
expect(json_response_ids).to eq []
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "pagination" do
|
|
it "renders the first page" do
|
|
get "/api/v1/customers", params: { page: "1" }
|
|
expect(json_response_ids).to eq [customer1.id.to_s, customer2.id.to_s]
|
|
end
|
|
|
|
it "renders the second page" do
|
|
get "/api/v1/customers", params: { page: "2", per_page: "1" }
|
|
expect(json_response_ids).to eq [customer2.id.to_s]
|
|
end
|
|
|
|
it "renders beyond the available pages" do
|
|
get "/api/v1/customers", params: { page: "2" }
|
|
expect(json_response_ids).to eq []
|
|
end
|
|
|
|
it "informs about invalid pages" do
|
|
get "/api/v1/customers", params: { page: "0" }
|
|
expect(json_response_ids).to eq nil
|
|
expect(json_error_detail).to eq "expected :page >= 1; got 0"
|
|
end
|
|
end
|
|
|
|
describe "query parameters" do
|
|
describe "extra_fields[customer]" do
|
|
context "with balance" do
|
|
it "adds balance to each customer" do
|
|
get "/api/v1/customers", params: { extra_fields: { customer: :balance } }
|
|
balances = json_response[:data].map{ |c| c[:attributes][:balance] }
|
|
expect(balances.all?{ |b| b.is_a? Numeric }).to eq(true)
|
|
end
|
|
end
|
|
|
|
context "with unknown field" do
|
|
it "returns unprocessable entity" do
|
|
get "/api/v1/customers", params: { extra_fields: { customer: :unknown } }
|
|
expect([response.status, json_error_detail]).to eq [422, "Unsupported fields: unknown"]
|
|
end
|
|
end
|
|
|
|
context "when not recevied" do
|
|
it "does not add balances" do
|
|
get "/api/v1/customers"
|
|
balances = json_response[:data].map{ |c| c[:attributes][:balance] }
|
|
expect([response.status, balances.compact]).to eq [200, []]
|
|
end
|
|
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.writable_attributes,
|
|
required: CustomerSchema.required_attributes
|
|
}
|
|
|
|
response "201", "Minimal customer created" do
|
|
let(:customer) do
|
|
{
|
|
email: "test@example.com",
|
|
enterprise_id: enterprise1.id.to_s
|
|
}
|
|
end
|
|
schema '$ref': "#/components/schemas/customer"
|
|
|
|
run_test! do
|
|
expect(json_response[:data][:attributes]).to include(
|
|
allow_charges: false,
|
|
terms_and_conditions_accepted_at: nil,
|
|
)
|
|
|
|
customer = Customer.find(json_response[:data][:attributes][:id])
|
|
expect(customer.created_manually).to eq true
|
|
end
|
|
end
|
|
|
|
response "201", "Example customer created" do
|
|
let(:customer) do
|
|
CustomerSchema.writable_attributes.transform_values do |attribute|
|
|
attribute[:example]
|
|
end.merge(
|
|
enterprise_id: enterprise1.id,
|
|
)
|
|
end
|
|
schema '$ref': "#/components/schemas/customer"
|
|
|
|
run_test! do
|
|
expect(json_response[:data][:attributes]).to include(
|
|
first_name: "Alice",
|
|
last_name: "Springs",
|
|
code: "BUYER1",
|
|
email: "alice@example.com",
|
|
enterprise_id: enterprise1.id,
|
|
tags: ["staff", "discount"],
|
|
shipping_address: CustomerSchema.address_example
|
|
)
|
|
end
|
|
end
|
|
|
|
response "422", "Unpermitted parameter" do
|
|
let(:customer) do
|
|
{
|
|
email: "test@example.com",
|
|
enterprise_id: enterprise1.id.to_s,
|
|
allow_charges: true,
|
|
}
|
|
end
|
|
schema '$ref': "#/components/schemas/error_response"
|
|
|
|
run_test! do
|
|
expect(json_error_detail).to eq "Parameters not allowed in this request: allow_charges"
|
|
end
|
|
end
|
|
|
|
response "422", "Unprocessable entity" do
|
|
let(:customer) { {} }
|
|
schema '$ref': "#/components/schemas/error_response"
|
|
|
|
run_test! do
|
|
expect(json_error_detail).to eq "A required parameter is missing or empty: customer"
|
|
expect(json_response[:meta]).to eq nil
|
|
end
|
|
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
|
|
let(:id) { customer1.id }
|
|
schema CustomerSchema.schema(
|
|
require_all: true,
|
|
extra_fields: { name: :balance, required: true }
|
|
)
|
|
|
|
run_test! do
|
|
date_time_string =
|
|
json_response[:data][:attributes][:terms_and_conditions_accepted_at]
|
|
expect(date_time_string).to match /^2000-01-01T00:00:00.000[Z+-].*$/
|
|
expect(DateTime.parse(date_time_string)).to eq(
|
|
customer1.terms_and_conditions_accepted_at
|
|
)
|
|
end
|
|
end
|
|
|
|
response "404", "Not found" do
|
|
let(:id) { 0 }
|
|
schema '$ref': "#/components/schemas/error_response"
|
|
|
|
run_test! do
|
|
expect(json_error_detail).to eq "The resource you were looking for could not be found."
|
|
end
|
|
end
|
|
|
|
context "without authentication" do
|
|
before { logout }
|
|
|
|
response "401", "Unauthorized" do
|
|
let(:id) { customer1.id }
|
|
schema '$ref': "#/components/schemas/error_response"
|
|
|
|
run_test! do
|
|
expect(json_error_detail).to eq "You are not authorized to perform that action."
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "related records" do
|
|
it "contains exactly the defined relationships" do
|
|
get "/api/v1/customers/#{customer1.id}"
|
|
|
|
relationships = json_response[:data][:relationships].keys
|
|
expect(relationships).to match_array CustomerSchema.relationships.map(&:to_s)
|
|
end
|
|
|
|
it "serializes the enterprise relationship" do
|
|
expected_enterprise_data = {
|
|
"data" => {
|
|
"id" => customer1.enterprise_id.to_s,
|
|
"type" => "enterprise"
|
|
},
|
|
"links" => {
|
|
"related" => "http://test.host/api/v1/enterprises/#{customer1.enterprise_id}"
|
|
}
|
|
}
|
|
|
|
get "/api/v1/customers/#{customer1.id}"
|
|
expect(json_response[:data][:relationships][:enterprise]).to eq(expected_enterprise_data)
|
|
end
|
|
end
|
|
|
|
describe "including related records" do
|
|
it "doesn't include other records by default" do
|
|
get "/api/v1/customers/#{customer1.id}"
|
|
|
|
expect(json_response[:included]).to eq nil
|
|
end
|
|
|
|
it "includes enterprise data on request" do
|
|
get "/api/v1/customers/#{customer1.id}?include=enterprise"
|
|
|
|
expect(json_response[:included].size).to eq 1
|
|
expect(json_response[:included].first).to include(
|
|
id: enterprise1.id.to_s,
|
|
type: "enterprise",
|
|
attributes: {
|
|
id: enterprise1.id,
|
|
name: "The Farm",
|
|
}
|
|
)
|
|
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.writable_attributes,
|
|
required: CustomerSchema.required_attributes
|
|
}
|
|
|
|
response "200", "Customer updated" do
|
|
let(:id) { customer1.id }
|
|
let(:customer) do
|
|
{
|
|
email: "test@example.com",
|
|
enterprise_id: enterprise1.id.to_s
|
|
}
|
|
end
|
|
schema '$ref': "#/components/schemas/customer"
|
|
|
|
run_test! do
|
|
# Tags should not be overridden when the param is missing:
|
|
expect(json_response[:data][:attributes]).to include(
|
|
tags: ["long-term"],
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "address" do
|
|
it "matches by country and state code" do
|
|
put "/api/v1/customers/#{customer1.id}", params: {
|
|
customer: {
|
|
shipping_address: CustomerSchema.address_example.merge(
|
|
region: { code: "Vic" },
|
|
country: { code: "AU" },
|
|
),
|
|
}
|
|
}
|
|
|
|
expect(response).to have_http_status :ok
|
|
expect(json_response.dig(:data, :attributes, :shipping_address)).to include(
|
|
region: { code: "Vic", name: "Victoria" },
|
|
country: { code: "AU", name: "Australia" },
|
|
)
|
|
end
|
|
|
|
it "matches by country and state name" do
|
|
put "/api/v1/customers/#{customer1.id}", params: {
|
|
customer: {
|
|
shipping_address: CustomerSchema.address_example.merge(
|
|
region: { name: "Victoria" },
|
|
country: { name: "Australia" },
|
|
),
|
|
}
|
|
}
|
|
|
|
expect(response).to have_http_status :ok
|
|
expect(json_response.dig(:data, :attributes, :shipping_address)).to include(
|
|
region: { code: "Vic", name: "Victoria" },
|
|
country: { code: "AU", name: "Australia" },
|
|
)
|
|
end
|
|
|
|
it "matches country and state case-insensitive" do
|
|
put "/api/v1/customers/#{customer1.id}", params: {
|
|
customer: {
|
|
shipping_address: CustomerSchema.address_example.merge(
|
|
region: { code: "VIC" },
|
|
country: { name: "AUSTRALIA" },
|
|
),
|
|
}
|
|
}
|
|
|
|
expect(response).to have_http_status :ok
|
|
expect(json_response.dig(:data, :attributes, :shipping_address)).to include(
|
|
region: { code: "Vic", name: "Victoria" },
|
|
country: { code: "AU", name: "Australia" },
|
|
)
|
|
end
|
|
end
|
|
|
|
response "422", "Unprocessable entity" do
|
|
let(:id) { customer1.id }
|
|
let(:customer) { {} }
|
|
schema '$ref': "#/components/schemas/error_response"
|
|
|
|
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
|
|
let(:id) { customer1.id }
|
|
schema '$ref': "#/components/schemas/customer"
|
|
|
|
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
|
|
let(:enterprise_id) { enterprise1.id }
|
|
schema '$ref': "#/components/schemas/customers_collection"
|
|
|
|
run_test!
|
|
end
|
|
end
|
|
end
|
|
end
|