From ba5fd614150d8315ce8068dc32dc82ce18d3655b Mon Sep 17 00:00:00 2001 From: Gideon Kimutai Date: Sat, 21 Jan 2023 06:36:48 +0300 Subject: [PATCH] feat: add payment methods and delivery addresses(#12) --- app/controllers/application_controller.rb | 6 ++ app/controllers/orders_controller.rb | 13 +--- .../orders_payment_methods_controller.rb | 24 +++++++ app/controllers/payment_methods_controller.rb | 2 +- app/controllers/payments_controller.rb | 27 ++++++++ .../shipping_addresses_controller.rb | 48 ++++++++++++++ app/models/county.rb | 8 +++ app/models/order.rb | 12 ++-- app/models/payment.rb | 7 ++ app/models/payment_method.rb | 5 +- app/models/payments_payment_method.rb | 8 +++ app/models/shipping_address.rb | 14 ++++ app/models/town.rb | 9 +++ app/models/user.rb | 1 + ...alizer.rb => payment_method_serializer.rb} | 2 +- app/serializers/payment_serializer.rb | 9 +++ .../shipping_address_serializer.rb | 10 +++ app/serializers/town_serializer.rb | 9 +++ config/routes.rb | 11 +++- db/data/ke/county_towns.json | 1 + ...119030417_create_orders_payment_methods.rb | 4 +- db/migrate/20230119041901_create_payments.rb | 9 +++ ...9042455_create_payments_payment_methods.rb | 10 +++ db/migrate/20230120041655_create_counties.rb | 10 +++ db/migrate/20230120041718_create_towns.rb | 10 +++ ...0230120041929_create_shipping_addresses.rb | 15 +++++ ...20141624_add_shipping_address_to_orders.rb | 5 ++ .../20230121042740_add_order_to_payments.rb | 5 ++ db/schema.rb | 65 ++++++++++++++++++- db/seeds.rb | 33 +++++++++- spec/factories/counties.rb | 7 ++ spec/factories/orders.rb | 1 + spec/factories/payments.rb | 6 ++ spec/factories/payments_payment_methods.rb | 6 ++ spec/factories/shipping_addresses.rb | 13 ++++ spec/factories/towns.rb | 8 +++ spec/models/county_spec.rb | 12 ++++ spec/models/order_spec.rb | 9 +++ spec/models/payment_spec.rb | 10 +++ spec/models/payments_payment_method_spec.rb | 10 +++ spec/models/product_spec.rb | 2 +- spec/models/shipping_address_spec.rb | 18 +++++ spec/models/town_spec.rb | 12 ++++ spec/rails_helper.rb | 2 + spec/requests/orders/create_spec.rb | 3 +- .../orders/payment_methods/create_spec.rb | 65 +++++++++++++++++++ spec/requests/orders/payments/create_spec.rb | 47 ++++++++++++++ spec/requests/payments_spec.rb | 12 ++++ spec/requests/shipping_addresses/show_spec.rb | 21 ++++++ .../shipping_addresses/update_spec.rb | 46 +++++++++++++ spec/requests/users/create_spec.rb | 6 +- .../users/shipping_addresses/create_spec.rb | 62 ++++++++++++++++++ .../users/shipping_addresses/index_spec.rb | 24 +++++++ 53 files changed, 762 insertions(+), 32 deletions(-) create mode 100644 app/controllers/orders_payment_methods_controller.rb create mode 100644 app/controllers/payments_controller.rb create mode 100644 app/controllers/shipping_addresses_controller.rb create mode 100644 app/models/county.rb create mode 100644 app/models/payment.rb create mode 100644 app/models/payments_payment_method.rb create mode 100644 app/models/shipping_address.rb create mode 100644 app/models/town.rb rename app/serializers/{payment_methods_serializer.rb => payment_method_serializer.rb} (76%) create mode 100644 app/serializers/payment_serializer.rb create mode 100644 app/serializers/shipping_address_serializer.rb create mode 100644 app/serializers/town_serializer.rb create mode 100644 db/data/ke/county_towns.json create mode 100644 db/migrate/20230119041901_create_payments.rb create mode 100644 db/migrate/20230119042455_create_payments_payment_methods.rb create mode 100644 db/migrate/20230120041655_create_counties.rb create mode 100644 db/migrate/20230120041718_create_towns.rb create mode 100644 db/migrate/20230120041929_create_shipping_addresses.rb create mode 100644 db/migrate/20230120141624_add_shipping_address_to_orders.rb create mode 100644 db/migrate/20230121042740_add_order_to_payments.rb create mode 100644 spec/factories/counties.rb create mode 100644 spec/factories/payments.rb create mode 100644 spec/factories/payments_payment_methods.rb create mode 100644 spec/factories/shipping_addresses.rb create mode 100644 spec/factories/towns.rb create mode 100644 spec/models/county_spec.rb create mode 100644 spec/models/payment_spec.rb create mode 100644 spec/models/payments_payment_method_spec.rb create mode 100644 spec/models/shipping_address_spec.rb create mode 100644 spec/models/town_spec.rb create mode 100644 spec/requests/orders/payment_methods/create_spec.rb create mode 100644 spec/requests/orders/payments/create_spec.rb create mode 100644 spec/requests/payments_spec.rb create mode 100644 spec/requests/shipping_addresses/show_spec.rb create mode 100644 spec/requests/shipping_addresses/update_spec.rb create mode 100644 spec/requests/users/shipping_addresses/create_spec.rb create mode 100644 spec/requests/users/shipping_addresses/index_spec.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b6e615d..2ce28f4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -31,6 +31,12 @@ def ensure_cart_owner! @cart = cart end + def ensure_order_owner! + id = params[:order_id] || params[:id] + + @order = @current_user.orders.find id + end + attr_reader :current_user def record_not_found(exception) diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index a35fa71..4133f5b 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -10,8 +10,7 @@ def show end def create - @order = @cart.build_order - @order.payment_method = PaymentMethod.find_by id: create_params[:payment_method_id] + @order = @cart.build_order order_params order_item_attrs = @order.cart.items.select('id as cart_item_id, amount').map(&:attributes) @order.items.build order_item_attrs @@ -31,13 +30,7 @@ def update end end - private - - def create_params - params.require(:order).permit(:payment_method_id, :status).except(:status) - end - - def ensure_order_owner! - @order = @current_user.orders.find(params[:id]) + def order_params + params.require(:order).permit(:shipping_address_id) end end diff --git a/app/controllers/orders_payment_methods_controller.rb b/app/controllers/orders_payment_methods_controller.rb new file mode 100644 index 0000000..7253cbb --- /dev/null +++ b/app/controllers/orders_payment_methods_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class OrdersPaymentMethodsController < ApplicationController + before_action :authenticate + before_action :ensure_order_owner! + + def create + @order_payment_method = @order.build_orders_payment_methods create_params + + if @order_payment_method.valid? + @order_payment_method.save! + + render json: @order_payment_method, status: :created + else + render json: @order_payment_method.errors, status: :unprocessable_entity + end + end + + private + + def create_params + params.require(:order_payment_method).permit(:payment_method_id) + end +end diff --git a/app/controllers/payment_methods_controller.rb b/app/controllers/payment_methods_controller.rb index 2b183d4..27d707a 100644 --- a/app/controllers/payment_methods_controller.rb +++ b/app/controllers/payment_methods_controller.rb @@ -5,7 +5,7 @@ def index @payment_methods = PaymentMethod.page(pagination_params[:page]).per(pagination_params[:limit]) with_pagination_options @payment_methods do |options| - render json: PaymentMethodsSerializer.new(@payment_methods, options), status: :ok + render json: PaymentMethodSerializer.new(@payment_methods, options), status: :ok end end end diff --git a/app/controllers/payments_controller.rb b/app/controllers/payments_controller.rb new file mode 100644 index 0000000..96ef004 --- /dev/null +++ b/app/controllers/payments_controller.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class PaymentsController < ApplicationController + before_action :authenticate + + def create + @order = Order.find params[:order_id] + @payment = @order.build_payment create_params + + if @payment.valid? + @payment.save! + @order.reload + @order.process_payment! + + render json: PaymentSerializer.new(@payment), status: :created + + else + render json: @payment.errors, status: :unprocessable_entity + end + end + + private + + def create_params + params.require(:payment).permit(:amount) + end +end diff --git a/app/controllers/shipping_addresses_controller.rb b/app/controllers/shipping_addresses_controller.rb new file mode 100644 index 0000000..9f9e3dc --- /dev/null +++ b/app/controllers/shipping_addresses_controller.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +class ShippingAddressesController < ApplicationController + before_action :authenticate + + def index + @shipping_addresses = ShippingAddress.page(pagination_params[:page]).per(pagination_params[:limit]) + + with_pagination_options @shipping_addresses do |options| + render json: ShippingAddressSerializer.new(@shipping_addresses, options) + end + end + + def show + @shipping_address = ShippingAddress.find params[:id] + + render json: ShippingAddressSerializer.new(@shipping_address) + end + + def create + @user = User.find(params[:user_id]) + @shipping_address = @user.shipping_addresses.build create_params + + if @shipping_address.valid? + @shipping_address.save! + + render json: ShippingAddressSerializer.new(@shipping_address), status: :created + else + render json: @shipping_address.errors, status: :unprocessable_entity + end + end + + def update + @shipping_address = ShippingAddress.find params[:id] + + if @shipping_address.update create_params + render json: ShippingAddressSerializer.new(@shipping_address), status: :ok + else + render json: @shipping_address.errors, status: :unprocessable_entity + end + end + + private + + def create_params + params.require(:shipping_address).permit(:first_name, :last_name, :phone, :description, :town_id) + end +end diff --git a/app/models/county.rb b/app/models/county.rb new file mode 100644 index 0000000..7ba6f81 --- /dev/null +++ b/app/models/county.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class County < ApplicationRecord + has_many :towns, dependent: :destroy + + validates :name, presence: true + validates :name, uniqueness: true +end diff --git a/app/models/order.rb b/app/models/order.rb index eea296c..dd36192 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -11,12 +11,14 @@ class Order < ApplicationRecord acts_as_paranoid belongs_to :cart + belongs_to :shipping_address has_one :orders_payment_methods, class_name: 'OrdersPaymentMethod', dependent: :destroy has_one :payment_method, through: :orders_payment_methods + has_one :payment, dependent: :destroy has_many :items, class_name: 'OrderItem', dependent: :destroy - validates :cart, cart_items: true + validates :cart, cart_items: true, if: proc { |order| order.cart.present? } aasm :status do state :pending, initial: true @@ -34,7 +36,7 @@ class Order < ApplicationRecord state :void event :process_payment do - transitions from: :pending, to: :payment_processing, guard: :verify_payment? + transitions from: :delivered, to: :payment_processing end event :confirm_payment do @@ -54,11 +56,11 @@ def pay_on_delivery? payment_method.name == PAYMENT_METHODS[:pay_on_delivery] end - def verify_payment? - return true if pay_on_delivery? + def payment_received? + pay_on_delivery? && payment.present? end def payment_confirmed? - return true if pay_on_delivery? + false end end diff --git a/app/models/payment.rb b/app/models/payment.rb new file mode 100644 index 0000000..4bc209d --- /dev/null +++ b/app/models/payment.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Payment < ApplicationRecord + belongs_to :order + has_one :payments_payment_methods, class_name: 'PaymentsPaymentMethod', dependent: :destroy + has_one :payment_method, through: :payments_payment_methods +end diff --git a/app/models/payment_method.rb b/app/models/payment_method.rb index 9c062e2..9fb67e5 100644 --- a/app/models/payment_method.rb +++ b/app/models/payment_method.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true class PaymentMethod < ApplicationRecord - has_many :orders_payment_methods + has_many :orders_payment_methods, dependent: :destroy has_many :orders, through: :orders_payment_methods + + has_many :payments_payment_methods, dependent: :destroy + has_many :payments, through: :payments_payment_methods end diff --git a/app/models/payments_payment_method.rb b/app/models/payments_payment_method.rb new file mode 100644 index 0000000..8aa6783 --- /dev/null +++ b/app/models/payments_payment_method.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class PaymentsPaymentMethod < ApplicationRecord + self.table_name = 'payments_payment_methods' + + belongs_to :payment + belongs_to :payment_method +end diff --git a/app/models/shipping_address.rb b/app/models/shipping_address.rb new file mode 100644 index 0000000..099c393 --- /dev/null +++ b/app/models/shipping_address.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class ShippingAddress < ApplicationRecord + belongs_to :town + belongs_to :user + has_many :orders, dependent: :restrict_with_error + + with_options presence: true do + validates :first_name + validates :last_name + validates :phone + validates :description + end +end diff --git a/app/models/town.rb b/app/models/town.rb new file mode 100644 index 0000000..b3ed6c3 --- /dev/null +++ b/app/models/town.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Town < ApplicationRecord + belongs_to :county + has_many :shipping_addresses, dependent: :destroy + + validates :name, presence: true + validates :name, uniqueness: true +end diff --git a/app/models/user.rb b/app/models/user.rb index 9100c43..a85a8ca 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,6 +7,7 @@ class User < ApplicationRecord has_many :products, foreign_key: :created_by_id, dependent: :destroy, inverse_of: :created_by has_many :carts, inverse_of: :user, dependent: :destroy + has_many :shipping_addresses, dependent: :destroy with_options presence: true do validates :first_name diff --git a/app/serializers/payment_methods_serializer.rb b/app/serializers/payment_method_serializer.rb similarity index 76% rename from app/serializers/payment_methods_serializer.rb rename to app/serializers/payment_method_serializer.rb index 7fe4efb..9af2f51 100644 --- a/app/serializers/payment_methods_serializer.rb +++ b/app/serializers/payment_method_serializer.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class PaymentMethodsSerializer +class PaymentMethodSerializer include JSONAPI::Serializer attributes :name, :description diff --git a/app/serializers/payment_serializer.rb b/app/serializers/payment_serializer.rb new file mode 100644 index 0000000..2630f2e --- /dev/null +++ b/app/serializers/payment_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class PaymentSerializer + include JSONAPI::Serializer + + attributes :amount, :order_id + + has_one :payment_method +end diff --git a/app/serializers/shipping_address_serializer.rb b/app/serializers/shipping_address_serializer.rb new file mode 100644 index 0000000..0beadeb --- /dev/null +++ b/app/serializers/shipping_address_serializer.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class ShippingAddressSerializer + include JSONAPI::Serializer + + attributes :first_name, :last_name, :phone, :description, :user_id, :town_id + + belongs_to :user + belongs_to :town +end diff --git a/app/serializers/town_serializer.rb b/app/serializers/town_serializer.rb new file mode 100644 index 0000000..a63616e --- /dev/null +++ b/app/serializers/town_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class TownSerializer + include JSONAPI::Serializer + + attributes :name, :county_id + + belongs_to :county +end diff --git a/config/routes.rb b/config/routes.rb index 7a963c5..8e9cc65 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,10 @@ Rails.application.routes.draw do + get 'payments/create' post 'auth', to: 'auth#create' - resources :users + resources :users do + resources :shipping_addresses, only: %i[create index] + end + resources :shipping_addresses, only: %i[update show] resources :brands, only: %i[show create index] resources :categories, only: %i[show create index] resources :products, only: %i[show index create] @@ -11,5 +15,8 @@ resources :variants, only: %i[index] resources :sku, only: %i[index create], as: :sku resources :payment_methods, only: :index - resources :orders, only: :show + resources :orders, only: :show do + resources :orders_payment_methods, only: :create, as: :payment_methods + resources :payments, only: :create + end end diff --git a/db/data/ke/county_towns.json b/db/data/ke/county_towns.json new file mode 100644 index 0000000..56d4d78 --- /dev/null +++ b/db/data/ke/county_towns.json @@ -0,0 +1 @@ +{"Baringo":["Eldama Ravine","Kabarnet","Koriema","Marigat","Mogotio"],"Bomet":["Bomet Town","Chepalungu","Chepilat","Chorwet","Konoin","Longisa","Mulot","Silibwet","Sotik"],"Bungoma":["Bumula","Bungoma Town","Chwele","Kabuchai","Kamukuywa","Kanduyi","Kimilili","Mount Elgon","Myanga","Nabing'eng'e","Sirisia","Tongaren","Webuye"],"Busia":["Amagoro","Bumala","Bundalangi","Busia Town","Butula","Funyula","Malaba","Matayos","Nambale","Tangakona","Teso"],"Elgeyo-Marakwet":["Iten","Kapsowar","Keiyo","Marakwet"],"Embu":["Embu Town","Mbeere","Mikimbi","Runyenjes"],"Garissa":["Garissa Town","Madogo"],"Homa Bay":["Gwasi","Homa Bay Town","Kabondo Kasipul","Karachuonyo","Kasipul","Kendu Bay","Mawego","Mbita","Ndhiwa","Nyakwere","Oyugis","Rangwe","Suba"],"Isiolo":["Isiolo"],"Kajiado":["Embulbul","Ewaso","Isinet","Isinya","Kajiado Town","Kerarapon","Kimana","Kiserian","Kitengela","Loitoktok","Matasia","Namanga","Ngong","Olkeri","Olooloitikosh","Oloolua","Ongata Rongai","Tuala"],"Kakamega":["Bulimbo","Butali","Butere","Furfaral","Ikoli","Ikolomani","Imanga","Kakamega Town","Khwisero","Kongoni","Likuyani","Luandeti","Lugari","Lurambi","Malava","Malimili","Matunda","Matungu","Mukumu","Mumias","Musoli","Navakholo","Sabatia","Sango","Shianda","Shinyalu","Sigalagala"],"Kericho":["Ainamoi","Belgut","Bureti","Chepseon","Kabianga","Kapkaket","Kericho Town","Kipkelion","Litein","Sigowet Soin"],"Kiambu":["Banana Hill","Gachie","Gachie","Gatundu","Gitaru","Githunguri","Ikinu","Juja","Kabete","Kahuho","Kiambaa","Kiambu","Kiambu01","Kikuyu","Kimbo","Kimende","Kinoo","Lari","Limuru","Ruaka","Ruiru","Thika","Thindigua","Tinganga","Uthiru","Wangige"],"Kilifi":["Bamba","Ganze","Gede","Gongoni","Kakokeni","Kaloleni","Kanamai","Kibaoni","Kibaoni","Kilifi Town","Magarini","Malindi","Mariakani","Matsangoni","Mazeras","Mkapuni","Mtwapa","Rabai","Singwaya","Watamu"],"Kirinyaga":["Gichugu","Kagio","Kerugoya","Kutus","Mukinduri","Mururi","Mwea","Ndia","Piai","Sagana"],"Kisii":["Bobasi","Bomachoge Borabu","Bomachoge Chache","Bonchari","Kisii Town","Kitutu Chache","Mongonga","Mosocho","Nyaribari","Ogembo Town","South Mugirango"],"Kisumu":["Ahero","Aminelbi","Awasi","Kanyakwar","Kisian","Kisumu Town","Kondele","Kong'ou","Manyatta","Maseno","Muhoroni","Nyakach","Nyalenda","Nyando","Seme","Sondu"],"Kitui":["Ikanga","Kitui","Mutha","Mutomo","Mwingi","Nuu"],"Kwale":["Diani","Kinango","Kombani","Kwale town","Lunga Lunga","Mackinnon Road","Matuga","Msambweni","Mtaa","Samburu","Ukunda"],"Laikipia":["Githiga","Karaba","Laikipia","Mia Moja","Nanyuki","Nyahururu","Rumuruti"],"Lamu":["Lamu","Mpeketoni","Mpeketoni","Witu"],"Machakos":["Athi River","Kangundo","Kathiani","Kinanie","Kithimani","Kivaa","Koma","Machakos Town","Malaa","Malili","Matungulu","Matuu","Mavoko","Mlolongo","Mwala","Syokimau","Tala","Wamunyu","Yatta"],"Makueni":["Emali","Kaiti","Kalawani","Kalongo","Kambu","Kibwezi","Kilome","Makindu","Makueni Town","Matiliku","Mbooni","Mtito Andei","Salama Town","Tawa","Wote Town"],"Mandera":["Mandera Town"],"Marsabit":["Moyale","Saku"],"Meru":["Athiru Ruujine","Buuri","Igembe","Imenti","Kariene","Kianjai","Maua","Meru Town","Mikinduri","Mugeene","Ngucici","Nkubu","Ntharene","Tigania","Timau"],"Migori":["Awendo","Isibania","Kuria","Migori Town","Nyatike","Rongo","Suna","Uriri"],"Mombasa":["Bamburi","Bombolulu","Changamwe","Jomvu","Kisauni","Kongowea","Likoni","Mikindani","Miritini","Mombasa Town","Mvita","Nyali","Shanzu"],"Muranga":["Gatanga","Kambirwa","Kangema","Kangema","Kenol","Kigumo","Kiharu","Kiriaini","Maragua","Muranga Town","Mwitingiri"],"Nairobi":["Buruburu","Central Business District","Chokaa","Dagoretti","Donholm","Eastleigh","Embakasi","Gigiri","Githurai","Githurai 44","Githurai 45","Highrise","Imara Daima","Industrial Area","Jogoo road","Joska","Kahawa","Kamukunji","Kamulu","Kangemi","Karen","Kariobangi","Kasarani","Kawangware","Kayole","Kibra","Kileleshwa","Kilimani","Komarack","Langata","Lavington","Madaraka","Makadara","Mathare","Muthaiga","Ngara","Ngong Road","Ngumo","Pangani","Parklands","Ridgeways","Roysambu","Ruai","Ruaraka","Runda","South B","South C","Starehe","Syokimau","Umoja","Upper Hill","Utawala","Westlands"],"Nakuru":["Bahati","Egerton University","Gilgil","Kabarak","Kikopey","Kuresoi","Mau Narok","Menengai","Milimani","Molo","Naivasha","Nakuru Town","Njambini","Njoro","Rongai","Subukia"],"Nandi":["Aldai","Chesumei","Emgwen","Kapsabet","Mosop","Nandi Hills","Tinderet"],"Narok":["Emurua Dikirr","Enengetia","Kilgoris","Narok Town","Olulung'a","Talek"],"Nyamira":["Kebirigo","Keroka Town","Kitutu Masaba","Mugirango","Nyamira Town","Rigoma"],"Nyandarua":["Kinangop","Kipipiri","Ndaragwa","Ol Jorok","Ol Kalou"],"Nyeri":["Chaka","Kamakwa","Karatina","Kieni","Mathira","Muiga","Mukurweini","Naromoru","Nyeri Town","Othaya","Tetu"],"Samburu":["Baragoi","Maralal","Samburu","Wamba"],"Siaya":["Alego Usonga","Bondo","Gem","Nyamonye","Rarieda","Sega","Siaya Town","SIGOMERE","Ugenya","Ugunja","Ukwala","Usenge","Yala"],"Taita-Taveta":["Mwatate","Taita-Taveta","Taveta","Voi","Wundanyi"],"Tana River":["Bura","Chakamba","Garsen","Hola","Minjila"],"Tharaka-Nithi":["Chogoria","Chuka","Gatunga","Igambangombe","Kaanwa","Maara","Marimanti","Nkarini"],"Trans-Nzoia":["Cherangany","Endebess","Kiminini","Kitale","Kwanza","Moisbridge","Saboti"],"Turkana":["Kainuk","Kakuma","Lodwar","Lokichogio"],"Uasin Gishu":["Ainabkoi","Burnt Forest","Eldoret Town","Kapseret","Kesses","Moi University Kesses","Moiben","Mois bridge","Soy","Turbo"],"Vihiga":["Chavakali","Emuhaya","Hamisi","Kabinjari","Luanda","Mahanga","Majengo","Mbale","Sabatia","Vihiga Town"],"Wajir":["Eldas","Tarbaj","Wajir"],"West Pokot":["Kacheliba","Kapenguria","Ortum","Pokot","Sigor"]} \ No newline at end of file diff --git a/db/migrate/20230119030417_create_orders_payment_methods.rb b/db/migrate/20230119030417_create_orders_payment_methods.rb index a832b2f..21d8d60 100644 --- a/db/migrate/20230119030417_create_orders_payment_methods.rb +++ b/db/migrate/20230119030417_create_orders_payment_methods.rb @@ -1,8 +1,8 @@ class CreateOrdersPaymentMethods < ActiveRecord::Migration[6.0] def change create_table :orders_payment_methods do |t| - t.bigint :order_id - t.bigint :payment_method_id + t.references :order, null: false, foreign_key: true + t.references :payment_method, null: false, foreign_key: true t.timestamps end diff --git a/db/migrate/20230119041901_create_payments.rb b/db/migrate/20230119041901_create_payments.rb new file mode 100644 index 0000000..57343c7 --- /dev/null +++ b/db/migrate/20230119041901_create_payments.rb @@ -0,0 +1,9 @@ +class CreatePayments < ActiveRecord::Migration[6.0] + def change + create_table :payments do |t| + t.decimal :amount, precision: 8, scale: 2 + + t.timestamps + end + end +end diff --git a/db/migrate/20230119042455_create_payments_payment_methods.rb b/db/migrate/20230119042455_create_payments_payment_methods.rb new file mode 100644 index 0000000..3df36b4 --- /dev/null +++ b/db/migrate/20230119042455_create_payments_payment_methods.rb @@ -0,0 +1,10 @@ +class CreatePaymentsPaymentMethods < ActiveRecord::Migration[6.0] + def change + create_table :payments_payment_methods do |t| + t.references :payment, null: false, foreign_key: true + t.references :payment_method, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20230120041655_create_counties.rb b/db/migrate/20230120041655_create_counties.rb new file mode 100644 index 0000000..2fe1933 --- /dev/null +++ b/db/migrate/20230120041655_create_counties.rb @@ -0,0 +1,10 @@ +class CreateCounties < ActiveRecord::Migration[6.0] + def change + create_table :counties do |t| + t.string :name + t.integer :code + + t.timestamps + end + end +end diff --git a/db/migrate/20230120041718_create_towns.rb b/db/migrate/20230120041718_create_towns.rb new file mode 100644 index 0000000..a1e1a0f --- /dev/null +++ b/db/migrate/20230120041718_create_towns.rb @@ -0,0 +1,10 @@ +class CreateTowns < ActiveRecord::Migration[6.0] + def change + create_table :towns do |t| + t.string :name + t.references :county, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20230120041929_create_shipping_addresses.rb b/db/migrate/20230120041929_create_shipping_addresses.rb new file mode 100644 index 0000000..c6caf29 --- /dev/null +++ b/db/migrate/20230120041929_create_shipping_addresses.rb @@ -0,0 +1,15 @@ +class CreateShippingAddresses < ActiveRecord::Migration[6.0] + def change + create_table :shipping_addresses do |t| + t.string :first_name + t.string :last_name + t.string :phone + t.references :user, null: false, foreign_key: true + t.references :town, null: false, foreign_key: true + t.text :description + t.boolean :is_default, default: false + + t.timestamps + end + end +end diff --git a/db/migrate/20230120141624_add_shipping_address_to_orders.rb b/db/migrate/20230120141624_add_shipping_address_to_orders.rb new file mode 100644 index 0000000..8aade6f --- /dev/null +++ b/db/migrate/20230120141624_add_shipping_address_to_orders.rb @@ -0,0 +1,5 @@ +class AddShippingAddressToOrders < ActiveRecord::Migration[6.0] + def change + add_reference :orders, :shipping_address, null: false, foreign_key: true + end +end diff --git a/db/migrate/20230121042740_add_order_to_payments.rb b/db/migrate/20230121042740_add_order_to_payments.rb new file mode 100644 index 0000000..9b7ce59 --- /dev/null +++ b/db/migrate/20230121042740_add_order_to_payments.rb @@ -0,0 +1,5 @@ +class AddOrderToPayments < ActiveRecord::Migration[6.0] + def change + add_reference :payments, :order, null: false, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 196f355..3d383fb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_01_19_031404) do +ActiveRecord::Schema.define(version: 2023_01_21_042740) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -62,6 +62,13 @@ t.index ["deleted_at"], name: "index_categories_on_deleted_at" end + create_table "counties", force: :cascade do |t| + t.string "name" + t.integer "code" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + create_table "friendly_id_slugs", force: :cascade do |t| t.string "slug", null: false t.integer "sluggable_id", null: false @@ -131,15 +138,19 @@ t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.string "status" + t.bigint "shipping_address_id", null: false t.index ["cart_id"], name: "index_orders_on_cart_id" t.index ["deleted_at"], name: "index_orders_on_deleted_at" + t.index ["shipping_address_id"], name: "index_orders_on_shipping_address_id" end create_table "orders_payment_methods", force: :cascade do |t| - t.bigint "order_id" - t.bigint "payment_method_id" + t.bigint "order_id", null: false + t.bigint "payment_method_id", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.index ["order_id"], name: "index_orders_payment_methods_on_order_id" + t.index ["payment_method_id"], name: "index_orders_payment_methods_on_payment_method_id" end create_table "payment_methods", force: :cascade do |t| @@ -149,6 +160,23 @@ t.datetime "updated_at", precision: 6, null: false end + create_table "payments", force: :cascade do |t| + t.decimal "amount", precision: 8, scale: 2 + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.bigint "order_id", null: false + t.index ["order_id"], name: "index_payments_on_order_id" + end + + create_table "payments_payment_methods", force: :cascade do |t| + t.bigint "payment_id", null: false + t.bigint "payment_method_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["payment_id"], name: "index_payments_payment_methods_on_payment_id" + t.index ["payment_method_id"], name: "index_payments_payment_methods_on_payment_method_id" + end + create_table "products", force: :cascade do |t| t.string "slug" t.string "name" @@ -175,6 +203,20 @@ t.index ["product_id"], name: "index_products_options_on_product_id" end + create_table "shipping_addresses", force: :cascade do |t| + t.string "first_name" + t.string "last_name" + t.string "phone" + t.bigint "user_id", null: false + t.bigint "town_id", null: false + t.text "description" + t.boolean "is_default", default: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["town_id"], name: "index_shipping_addresses_on_town_id" + t.index ["user_id"], name: "index_shipping_addresses_on_user_id" + end + create_table "skus", force: :cascade do |t| t.string "value", null: false t.datetime "created_at", precision: 6, null: false @@ -184,6 +226,14 @@ t.index ["value"], name: "index_skus_on_value", unique: true end + create_table "towns", force: :cascade do |t| + t.string "name" + t.bigint "county_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["county_id"], name: "index_towns_on_county_id" + end + create_table "users", force: :cascade do |t| t.string "first_name" t.string "last_name" @@ -223,10 +273,19 @@ add_foreign_key "order_items", "cart_items" add_foreign_key "order_items", "orders" add_foreign_key "orders", "carts" + add_foreign_key "orders", "shipping_addresses" + add_foreign_key "orders_payment_methods", "orders" + add_foreign_key "orders_payment_methods", "payment_methods" + add_foreign_key "payments", "orders" + add_foreign_key "payments_payment_methods", "payment_methods" + add_foreign_key "payments_payment_methods", "payments" add_foreign_key "products", "brands" add_foreign_key "products", "categories" add_foreign_key "products_options", "options" add_foreign_key "products_options", "products" + add_foreign_key "shipping_addresses", "towns" + add_foreign_key "shipping_addresses", "users" + add_foreign_key "towns", "counties" add_foreign_key "variants", "products" add_foreign_key "variants", "skus" end diff --git a/db/seeds.rb b/db/seeds.rb index e6c083d..c66902d 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file should contain all the record creation needed to seed the database with its default values. # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). # @@ -7,7 +9,24 @@ # Character.create(name: 'Luke', movie: movies.first) ActiveRecord::Base.transaction do - user = User.create( + OrderItem.delete_all! + Order.delete_all! + + CartItem.delete_all! + Cart.delete_all! + + Variant.delete_all! + Sku.delete_all! + + User.delete_all! + Product.delete_all! + Category.delete_all! + Brand.delete_all! + Order.delete_all! + Town.destroy_all + County.destroy_all + + user = User.create!( first_name: 'test', last_name: 'user', email: 'test@user.com', @@ -15,6 +34,7 @@ password: 'testpassword' ) + Rails.logger.debug user 10.times do Category.create name: Faker::Lorem.word @@ -39,6 +59,15 @@ end ['Pay on Delivery', 'Credit Card'].each do |payment_method| - PaymentMethod.create name: payment_method.to_s, description: "For #{payment_method.to_s} orders" + PaymentMethod.create name: payment_method.to_s, description: "For #{payment_method} orders" + end + + counties = JSON.parse File.read('./db/data/ke/county_towns.json') + counties.each_key do |county| + county = County.create name: county + + counties[county.name].each do |town| + Town.create name: town, county: county + end end end diff --git a/spec/factories/counties.rb b/spec/factories/counties.rb new file mode 100644 index 0000000..93c3bf7 --- /dev/null +++ b/spec/factories/counties.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :county do + name { Faker::Address.city } + end +end diff --git a/spec/factories/orders.rb b/spec/factories/orders.rb index 0bb4500..d916b50 100644 --- a/spec/factories/orders.rb +++ b/spec/factories/orders.rb @@ -3,5 +3,6 @@ FactoryBot.define do factory :order do cart { nil } + shipping_address end end diff --git a/spec/factories/payments.rb b/spec/factories/payments.rb new file mode 100644 index 0000000..c96086a --- /dev/null +++ b/spec/factories/payments.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :payment do + end +end diff --git a/spec/factories/payments_payment_methods.rb b/spec/factories/payments_payment_methods.rb new file mode 100644 index 0000000..a249489 --- /dev/null +++ b/spec/factories/payments_payment_methods.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :payments_payment_method do + end +end diff --git a/spec/factories/shipping_addresses.rb b/spec/factories/shipping_addresses.rb new file mode 100644 index 0000000..7d3177f --- /dev/null +++ b/spec/factories/shipping_addresses.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :shipping_address do + first_name { Faker::Name.first_name } + last_name { Faker::Name.last_name } + phone { Faker::PhoneNumber.phone_number } + town + user + description { Faker::Lorem.paragraph } + is_default { false } + end +end diff --git a/spec/factories/towns.rb b/spec/factories/towns.rb new file mode 100644 index 0000000..0e49a1e --- /dev/null +++ b/spec/factories/towns.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :town do + name { Faker::Address.city } + county + end +end diff --git a/spec/models/county_spec.rb b/spec/models/county_spec.rb new file mode 100644 index 0000000..eff834b --- /dev/null +++ b/spec/models/county_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe County, type: :model do + before { create :county } + + context 'Validations' do + specify { should validate_presence_of(:name) } + specify { should validate_uniqueness_of(:name) } + end +end diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index 6cd64d9..38c595e 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -3,7 +3,16 @@ require 'rails_helper' RSpec.describe Order, type: :model do + let(:user) { create :user } + let(:cart) { create :cart, :with_items, items_count: 1 } + + subject do + create :order, cart: cart + end + context 'Associations' do specify { should have_one(:payment_method).through :orders_payment_methods } + specify { should have_one(:payment) } + specify { should belong_to(:shipping_address) } end end diff --git a/spec/models/payment_spec.rb b/spec/models/payment_spec.rb new file mode 100644 index 0000000..b2198a0 --- /dev/null +++ b/spec/models/payment_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Payment, type: :model do + context 'Associations' do + specify { should have_one(:payment_method).through :payments_payment_methods } + specify { should belong_to(:order) } + end +end diff --git a/spec/models/payments_payment_method_spec.rb b/spec/models/payments_payment_method_spec.rb new file mode 100644 index 0000000..b78b7df --- /dev/null +++ b/spec/models/payments_payment_method_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe PaymentsPaymentMethod, type: :model do + context 'Associations' do + specify { should belong_to(:payment) } + specify { should belong_to(:payment_method) } + end +end diff --git a/spec/models/product_spec.rb b/spec/models/product_spec.rb index b73b5e8..7c25b60 100644 --- a/spec/models/product_spec.rb +++ b/spec/models/product_spec.rb @@ -9,7 +9,7 @@ specify { should validate_presence_of(:description) } end - describe 'association' do + describe 'associations' do specify { should belong_to(:created_by).class_name('User') } specify { should belong_to(:brand).required } diff --git a/spec/models/shipping_address_spec.rb b/spec/models/shipping_address_spec.rb new file mode 100644 index 0000000..f4b03c7 --- /dev/null +++ b/spec/models/shipping_address_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ShippingAddress, type: :model do + describe 'association' do + specify { should belong_to(:user).class_name('User') } + specify { should belong_to(:town).required } + specify { should have_many(:orders).dependent :restrict_with_error } + end + + describe 'validations' do + specify { should validate_presence_of :first_name } + specify { should validate_presence_of :last_name } + specify { should validate_presence_of :phone } + specify { should validate_presence_of :description } + end +end diff --git a/spec/models/town_spec.rb b/spec/models/town_spec.rb new file mode 100644 index 0000000..4e17a41 --- /dev/null +++ b/spec/models/town_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Town, type: :model do + before { create :town } + + context 'Validations' do + specify { should validate_presence_of(:name) } + specify { should validate_uniqueness_of(:name) } + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 3eaa089..b89f3cb 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -33,6 +33,8 @@ exit 1 end +require 'aasm/rspec' + Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |file| require file } RSpec.configure do |config| diff --git a/spec/requests/orders/create_spec.rb b/spec/requests/orders/create_spec.rb index fa8d237..c61ed11 100644 --- a/spec/requests/orders/create_spec.rb +++ b/spec/requests/orders/create_spec.rb @@ -5,6 +5,7 @@ RSpec.describe 'POST /orders', type: :request do let(:user) { create :user } let(:cart) { create :cart, user: user } + let(:shipping_address) { create :shipping_address, user: user } let(:payment_method) { create :payment_method, name: 'Pay on Delivery' } context 'When unauthorized' do @@ -41,7 +42,7 @@ before do cart.items << cart_item - post cart_orders_path(cart), params: { order: { payment_method_id: payment_method.id } }, + post cart_orders_path(cart), params: { order: { shipping_address_id: shipping_address.id } }, headers: authorization_header(user.email) end diff --git a/spec/requests/orders/payment_methods/create_spec.rb b/spec/requests/orders/payment_methods/create_spec.rb new file mode 100644 index 0000000..19230cf --- /dev/null +++ b/spec/requests/orders/payment_methods/create_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'POST /orders/:order_id/payment_methods', type: :request do + let(:user) { create :user } + let(:cart) { create :cart, :with_items, user: user, items_count: 2 } + let(:payment_method) { create :payment_method, name: 'Pay on Delivery' } + let(:order) { create :order, cart: cart } + + context 'When unauthorized' do + before { post order_payment_methods_path(order) } + + it_behaves_like 'unauthorized request' + end + + context 'With invalid params' do + let(:params) do + { + order_payment_method: { + payment_method_id: 'not-found' + } + } + end + + before { post order_payment_methods_path(order), params: params, headers: authorization_header(user.email) } + + it_behaves_like 'unprocessable request' + + it 'returns error message' do + expect(response.parsed_body['payment_method']).to match_array ['must exist'] + end + end + + context 'With valid params' do + let(:params) do + { + order_payment_method: { + payment_method_id: payment_method.id + } + } + end + + before { post order_payment_methods_path(order), params: params, headers: authorization_header(user.email) } + + it_behaves_like 'created resource request' + end + + context ' When pay on delivery' do + let(:params) do + { + order_payment_method: { + payment_method_id: payment_method.id + } + } + end + + it "transitions from 'pending' -> 'order_packaging'" do + expect do + post order_payment_methods_path(order), params: params, headers: authorization_header(user.email) + order.reload + end.to change { order.status }.from('pending').to('order_packaging') + end + end +end diff --git a/spec/requests/orders/payments/create_spec.rb b/spec/requests/orders/payments/create_spec.rb new file mode 100644 index 0000000..0ea4912 --- /dev/null +++ b/spec/requests/orders/payments/create_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'POST /orders/:order_id/payments', type: :request do + let(:user) { create :user } + let(:cart) { create :cart, :with_items, user: user, items_count: 1 } + let(:order) { create :order, cart: cart } + let(:payment_method) { create :payment_method, name: 'Pay on Delivery' } + + context 'When unauthorized' do + before { post order_payments_path(order), params: {} } + + it_should_behave_like 'unauthorized request' + end + + context 'With pay on delivery payment method' do + let(:cart) { create :cart, user: user } + let(:product) { create :product, :with_master, stock: 10 } + let(:product2) { create :product, :with_master, stock: 1 } + let(:cart_item1) { create :cart_item, product: product, sku: product.master.sku, cart: cart } + let(:cart_item2) { create :cart_item, product: product2, sku: product2.master.sku, cart: cart } + let(:amount) { cart.items.select(:amount).collect(&:amount).reduce(&:+).to_f } + + before do + cart.items << [cart_item1, cart_item2] + order.payment_method = payment_method + order.status = :delivered + order.save! + post order_payments_path(order), params: { payment: { amount: amount } }, + headers: authorization_header(user.email) + end + + it_behaves_like 'created resource request' + + it 'returns payment data' do + expect(response.parsed_body['data']['type']).to eq 'payment' + expect(response.parsed_body['data']['attributes']['order_id']).to eql order.id + expect(response.parsed_body['data']['attributes']['amount'].to_f).to eql amount.to_f + end + + it 'updates order status to payment processing' do + order.reload + expect(order.status).to eql :payment_processing.to_s + end + end +end diff --git a/spec/requests/payments_spec.rb b/spec/requests/payments_spec.rb new file mode 100644 index 0000000..2375f8b --- /dev/null +++ b/spec/requests/payments_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Payments', type: :request do + describe 'GET /create' do + it 'returns http success' do + get '/payments/create' + expect(response).to have_http_status(:success) + end + end +end diff --git a/spec/requests/shipping_addresses/show_spec.rb b/spec/requests/shipping_addresses/show_spec.rb new file mode 100644 index 0000000..ec9233e --- /dev/null +++ b/spec/requests/shipping_addresses/show_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'GET /shipping_addresses/:id', type: :request do + let(:shipping_address) { create :shipping_address } + + context 'When unauthorized' do + before { get shipping_address_path(shipping_address) } + + it_behaves_like 'unauthorized request' + end + before { get shipping_address_path(shipping_address), headers: authorization_header(shipping_address.user.email) } + + it_behaves_like 'ok request' + + it 'returns correct fields' do + expect(response.parsed_body['data']['id']).to eql shipping_address.id.to_s + expect(response.parsed_body['data']['attributes']['town_id']).to eql shipping_address.town_id + end +end diff --git a/spec/requests/shipping_addresses/update_spec.rb b/spec/requests/shipping_addresses/update_spec.rb new file mode 100644 index 0000000..67f9836 --- /dev/null +++ b/spec/requests/shipping_addresses/update_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'PUT /shipping_addresses/:id', type: :request do + let(:shipping_address) { create :shipping_address } + + context 'When unauthorized' do + before { put shipping_address_path(shipping_address) } + + it_behaves_like 'unauthorized request' + end + + context 'Validations' do + before do + put shipping_address_path(shipping_address), params: { shipping_address: { first_name: '' } }, + headers: authorization_header(shipping_address.user.email) + end + + it_behaves_like 'unprocessable request' + + it 'returns correct errors' do + expect(response.parsed_body['first_name']).to match_array ["can't be blank"] + end + end + + context 'When authorized' do + let(:town) { create :town } + let(:valid_params) do + { shipping_address: { + town_id: town.id + } } + end + + before do + put shipping_address_path(shipping_address), params: valid_params, + headers: authorization_header(shipping_address.user.email) + end + + it_behaves_like 'ok request' + + it 'returns correct fields' do + expect(response.parsed_body['data']['attributes']['town_id']).to eql valid_params[:shipping_address][:town_id] + end + end +end diff --git a/spec/requests/users/create_spec.rb b/spec/requests/users/create_spec.rb index c1c68a8..29304dc 100644 --- a/spec/requests/users/create_spec.rb +++ b/spec/requests/users/create_spec.rb @@ -7,12 +7,12 @@ let(:user_params) { {} } context 'When params are valid' do - let(:params) { attributes_for(:user) } + let(:valid_params) { attributes_for(:user) } before do - user_params[:user] = params + user_params[:user] = valid_params - post users_url, params: user_params + post users_path, params: user_params end it 'returns status code 201' do diff --git a/spec/requests/users/shipping_addresses/create_spec.rb b/spec/requests/users/shipping_addresses/create_spec.rb new file mode 100644 index 0000000..eb94c42 --- /dev/null +++ b/spec/requests/users/shipping_addresses/create_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'POST /users/:user_id/shipping_addresses', type: :request do + let(:user) { create :user } + let(:params) do + { first_name: '', + last_name: '', + phone: '', + description: '', + town_id: '' } + end + + context 'When unauthorized' do + before { post user_shipping_addresses_path(user) } + + it_behaves_like 'unauthorized request' + end + + context 'Validations' do + before do + post user_shipping_addresses_path(user), params: { shipping_address: params }, + headers: authorization_header(user.email) + end + + it_behaves_like 'unprocessable request' + + it 'returns correct errors' do + expect(response.parsed_body['first_name']).to match_array ["can't be blank"] + expect(response.parsed_body['last_name']).to match_array ["can't be blank"] + expect(response.parsed_body['phone']).to match_array ["can't be blank"] + expect(response.parsed_body['description']).to match_array ["can't be blank"] + expect(response.parsed_body['town']).to match_array ['must exist'] + end + end + + context 'When authorized' do + let(:town) { create :town } + let(:valid_params) do + { shipping_address: params.merge!( + first_name: Faker::Name.first_name, + last_name: Faker::Name.last_name, + phone: Faker::PhoneNumber.phone_number, + description: Faker::Lorem.paragraph, + town_id: town.id + ) } + end + + before { post user_shipping_addresses_path(user), params: valid_params, headers: authorization_header(user.email) } + + it_behaves_like 'created resource request' + + it 'returns correct fields' do + expect(response.parsed_body['data']['attributes']['first_name']).to eql valid_params[:shipping_address][:first_name] + expect(response.parsed_body['data']['attributes']['last_name']).to eql valid_params[:shipping_address][:last_name] + expect(response.parsed_body['data']['attributes']['phone']).to eql valid_params[:shipping_address][:phone] + expect(response.parsed_body['data']['attributes']['description']).to eql valid_params[:shipping_address][:description] + expect(response.parsed_body['data']['attributes']['town_id']).to eql valid_params[:shipping_address][:town_id] + end + end +end diff --git a/spec/requests/users/shipping_addresses/index_spec.rb b/spec/requests/users/shipping_addresses/index_spec.rb new file mode 100644 index 0000000..33d1827 --- /dev/null +++ b/spec/requests/users/shipping_addresses/index_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'GET users/:user_id/shipping_addresses', type: :request do + let(:shipping_address) { create :shipping_address } + + context 'When unauthorized' do + before { get user_shipping_addresses_path(shipping_address) } + + it_behaves_like 'unauthorized request' + end + before do + get user_shipping_addresses_path(shipping_address), headers: authorization_header(shipping_address.user.email) + end + + it_behaves_like 'ok request' + + it 'returns correct fields' do + expect(response.parsed_body['meta']['total_pages']).to eql 1 + expect(response.parsed_body['data'][0]['attributes']['town_id']).to eql shipping_address.town_id + expect(response.parsed_body['data'][0]['attributes']['user_id']).to eql shipping_address.user_id + end +end