Commit 6697ee3a by ivan Lan

Add spec for models & Make up the migration of tag

parent d6914fdb
...@@ -7,3 +7,4 @@ ...@@ -7,3 +7,4 @@
/pkg/ /pkg/
/spec/reports/ /spec/reports/
/tmp/ /tmp/
/mysql.yml
example_id | status | run_time |
------------------------------------------ | ------ | --------------- |
./spec/shotengai/models_spec.rb[1:1:1:1] | passed | 0.36278 seconds |
./spec/shotengai/models_spec.rb[1:1:1:2] | passed | 0.24106 seconds |
./spec/shotengai/models_spec.rb[1:1:2:1] | passed | 0.19389 seconds |
./spec/shotengai/models_spec.rb[1:1:2:2] | passed | 0.19885 seconds |
./spec/shotengai/models_spec.rb[1:1:2:3] | passed | 0.22058 seconds |
./spec/shotengai/models_spec.rb[1:1:3:1] | passed | 0.202 seconds |
./spec/shotengai/models_spec.rb[1:1:3:2] | passed | 0.20022 seconds |
./spec/shotengai/models_spec.rb[1:1:3:3] | passed | 0.20972 seconds |
./spec/shotengai/models_spec.rb[1:1:4:1] | passed | 0.23258 seconds |
./spec/shotengai/models_spec.rb[1:1:5:1] | passed | 0.23735 seconds |
./spec/shotengai/models_spec.rb[1:1:5:2:1] | passed | 0.23552 seconds |
./spec/shotengai/models_spec.rb[1:1:5:2:2] | passed | 0.25856 seconds |
./spec/shotengai/models_spec.rb[1:2:1:1] | passed | 0.22996 seconds |
./spec/shotengai/models_spec.rb[1:2:1:2] | passed | 0.2075 seconds |
./spec/shotengai/models_spec.rb[1:2:2:1] | passed | 0.46901 seconds |
./spec/shotengai/models_spec.rb[1:2:2:2] | passed | 0.29917 seconds |
class CreateShotengaiProductsAndOrders < ActiveRecord::Migration[5.1] class CreateShotengaiProductsAndOrders < ActiveRecord::Migration[5.1]
p 'fuck !'
def change def change
create_table :shotengai_products do |t| create_table :shotengai_products do |t|
t.string :title t.string :title
......
class CreateTagModels < ActiveRecord::Migration[5.1]
def change
create_table "taggings" do |t|
t.integer "tag_id"
t.string "taggable_type"
t.integer "taggable_id"
t.string "tagger_type"
t.integer "tagger_id"
t.string "context", limit: 128
t.datetime "created_at"
t.index ["context"], name: "index_taggings_on_context"
t.index ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
t.index ["tag_id"], name: "index_taggings_on_tag_id"
t.index ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
t.index ["taggable_id", "taggable_type", "tagger_id", "context"], name: "taggings_idy"
t.index ["taggable_id"], name: "index_taggings_on_taggable_id"
t.index ["taggable_type"], name: "index_taggings_on_taggable_type"
t.index ["tagger_id", "tagger_type"], name: "index_taggings_on_tagger_id_and_tagger_type"
t.index ["tagger_id"], name: "index_taggings_on_tagger_id"
end
create_table "tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
t.index ["name"], name: "index_tags_on_name", unique: true
end
end
end
...@@ -29,7 +29,7 @@ module Shotengai ...@@ -29,7 +29,7 @@ module Shotengai
state :unpaid, initial: true state :unpaid, initial: true
state :paid, :delivering, :received, :evaluated state :paid, :delivering, :received, :evaluated
{ {
pay: { from: :unpaid, to: :paid, after: :fill_snapshot }, pay: { from: :unpaid, to: :paid, after: [:fill_snapshot, :set_pay_time] },
cancel: { from: :unpaid, to: :canceled }, cancel: { from: :unpaid, to: :canceled },
send_out: { from: :paid, to: :delivering, after: :set_delivery_time }, send_out: { from: :paid, to: :delivering, after: :set_delivery_time },
get_it: { from: :delivering, to: :received, after: :set_receipt_time }, get_it: { from: :delivering, to: :received, after: :set_receipt_time },
...@@ -41,17 +41,22 @@ module Shotengai ...@@ -41,17 +41,22 @@ module Shotengai
end end
def fill_snapshot def fill_snapshot
p 'cmm 222'
ActiveRecord::Base.transaction { ActiveRecord::Base.transaction {
self.snapshots.each(&:copy_info) self.snapshots.each(&:copy_info)
} }
end end
def set_pay_time
self.update!(pay_time: Time.now)
end
def set_delivery_time def set_delivery_time
update!(delivery_time: Time.zone.now) update!(delivery_time: Time.now)
end end
def set_receipt_time def set_receipt_time
update!(receipt_time: Time.zone.now) update!(receipt_time: Time.now)
end end
def total_price def total_price
......
...@@ -19,6 +19,7 @@ module Shotengai ...@@ -19,6 +19,7 @@ module Shotengai
# updated_at :datetime not null # updated_at :datetime not null
class Shotengai::Product < ActiveRecord::Base class Shotengai::Product < ActiveRecord::Base
require 'acts_as_taggable_on'
self.table_name = 'shotengai_products' self.table_name = 'shotengai_products'
validate :check_spec, if: :spec validate :check_spec, if: :spec
......
mysql:
username: root
password:
\ No newline at end of file
# == Schema Information
#
# Table name: shotengai_catalogs
#
# id :integer not null, primary key
# name :string(255)
# level_type :string(255)
# image :string(255)
# type :string(255)
# super_catalog_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
FactoryGirl.define do
factory :catalog, class: 'Shotengai::Catalog' do
end
end
# == Schema Information
#
# Table name: shotengai_orders
#
# id :integer not null, primary key
# seq :integer
# address :string(255)
# pay_time :datetime
# delivery_time :datetime
# receipt_time :datetime
# delivery_way :string(255)
# delivery_cost :string(255)
# status :string(255)
# type :string(255)
# meta :json
# buyer_id :integer
# buyer_type :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
FactoryGirl.define do
factory :shotengai_order do
end
end
# == Schema Information
#
# Table name: shotengai_products
#
# id :integer not null, primary key
# title :string(255)
# status :string(255)
# spec :json
# default_series_id :integer
# need_express :boolean
# need_time_attr :boolean
# cover_image :string(255)
# banners :json
# detail :json
# type :string(255)
# meta :json
# created_at :datetime not null
# updated_at :datetime not null
#
FactoryGirl.define do
factory :test_good, class: 'TestGood' do
title 'Test Product Title'
# status
spec {
{
"颜色" => ["黑色", "红色", "白色"],
"大小" => ["S", "M", "L"],
}
}
# default_series_id ''
need_express true
# need_time_attr true
cover_image 'cover_image.image'
banners { [ 'image1', 'iamge2' ] }
detail {
{
"使用说明" => "xxxxxxxx...",
"产品参数" => "参数 参数..."
}
}
# type "
meta {
{
"meta1" => "111",
"meta2" => "222",
}
}
end
factory :other_good, class: 'OtherGood' do
title 'Test Product Title'
# status
spec {
{
"颜色" => ["黑色", "红色", "白色"],
"大小" => ["S", "M", "L"],
}
}
# default_series_id ''
need_express true
# need_time_attr true
cover_image 'cover_image.image'
banners { [ 'image1', 'iamge2' ] }
detail {
{
"使用说明" => "xxxxxxxx...",
"产品参数" => "参数 参数..."
}
}
# type "
meta {
{
"meta1" => "111",
"meta2" => "222",
}
}
end
end
# == Schema Information
#
# Table name: shotengai_series
#
# id :integer not null, primary key
# original_price :decimal(9, 2)
# price :decimal(9, 2)
# stock :integer
# spec :json
# type :string(255)
# meta :json
# shotengai_products_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
FactoryGirl.define do
factory :test_series, class: 'TestGoodSeries' do
original_price 100
price 80
stock 10
spec {
{
"颜色" => "红色",
"大小" => "S",
}
}
# type
meta {
{
"meta1" => "111",
"meta2" => "222",
}
}
end
factory :other_series, class: 'OtherGoodSeries' do
original_price 100
price 80
stock 10
spec {
{
"颜色" => "红色",
"大小" => "S",
}
}
# type
meta {
{
"series_meta1" => "111",
"series_meta2" => "222",
}
}
end
end
# == Schema Information
#
# Table name: shotengai_snapshots
#
# id :integer not null, primary key
# original_price :decimal(9, 2)
# price :decimal(9, 2)
# count :integer
# spec :json
# banners :json
# cover_image :string(255)
# detail :json
# type :string(255)
# meta :json
# shotengai_series_id :integer
# shotengai_orders_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
FactoryGirl.define do
factory :test_snapshot, class: 'TestGoodSnapshot' do
original_price 100
price 80
count 2
spec {
{
"颜色" => "红色",
"大小" => "S",
}
}
banners { ['iamge 1', 'iamge 2'] }
cover_image 'cover_image 1'
detail {
{
"使用说明" => "xxxxxxxx...",
"产品参数" => "参数 参数..."
}
}
meta {
{
"meta1" => "111",
"meta2" => "222",
}
}
end
factory :other_snapshot, class: 'OtherGoodSnapshot' do
original_price 100
price 80
count 2
spec {
{
"颜色" => "红色",
"大小" => "S",
}
}
banners { ['iamge 1', 'iamge 2'] }
cover_image 'cover_image 1'
detail {
{
"使用说明" => "xxxxxxxx...",
"产品参数" => "参数 参数..."
}
}
meta {
{
"meta1" => "111",
"meta2" => "222",
}
}
end
end
require 'spec_helper'
mysql_setting_file = '../../mysql.yml'
mysql_setting = File.exist?(mysql_setting_file) ? YAML.load(File.read(mysql_setting_file)) : { username: 'root', password: nil }
Dir['db/migrate/*'].map{ |x| File.basename(x)}.each { |f| require_relative "../../db/migrate/#{f}"}
ActiveRecord::Base.establish_connection(
adapter: 'mysql2',
username: mysql_setting[:username],
password: mysql_setting[:password] || '',
)
c = ActiveRecord::Base.connection
c.exec_update('drop database if exists shotengai_test;')
c.exec_update('create database shotengai_test')
c.exec_update('use shotengai_test')
# 如何通过activerecord 建database
RSpec.describe 'Shotengai Models' do
before do
ActiveRecord::Migration[5.1].subclasses.each do |migrate|
migrate.migrate(:up)
end
end
describe 'Product & Order' do
before do
class TestGood < Shotengai::Product; end
class OtherGood < Shotengai::Product; end
class TestOrder < Shotengai::Order
can_buy ::TestGood
end
class TestBuyer < ActiveRecord::Base
include Shotengai::Buyer
can_shopping_with ::TestOrder
ActiveRecord::Base.connection.create_table(:test_buyers) unless ActiveRecord::Base.connection.table_exists?(:test_buyers)
end
@good = create(:test_good)
@series = create(:test_series, test_good: @good)
@snapshot = create(:test_snapshot, series: @series)
@other_good = create(:other_good)
@other_series = create(:other_series, other_good: @other_good)
@other_snapshot = create(:other_snapshot, series: @other_series)
@buyer = TestBuyer.create
end
describe 'About Product' do
it 'validate' do
expect(@good.spec.class).to eq(Hash)
expect(@good.banners.class).to eq(Array)
# Do not check_spec if spec is nil
expect(@good.update!(spec: nil)).to eq(true)
end
it 'Associations' do
# has many Series
expect(@good.series.to_a).to eq([ @series ])
# has many Snapshot through Series
expect(@good.snapshots.to_a).to eq([ @snapshot ])
end
end
describe 'About Series' do
it 'validate' do
expect(@series.spec.class).to eq(Hash)
# 非法关键字
expect{
@series.update!(spec: {"颜色" => "红色", "大小" => 1111 })
}.to raise_error(Shotengai::WebError)
# 关键字缺失
expect{
@series.update!(spec: {"颜色" => "红色"})
}.to raise_error(Shotengai::WebError)
end
it 'Associations' do
# has_many Snapshot
expect(@series.snapshots.to_a).to eq([ @snapshot ])
# belongs to Product
expect(@series.product).to eq(@good)
expect(@series.test_good).to eq(@good)
end
it 'methods' do
expect(@good.default_series).to eq(@series)
end
end
describe 'About Snapshot' do
it 'validate' do
expect(@snapshot.spec.class).to eq(Hash)
expect(@snapshot.meta.class).to eq(Hash)
end
it 'Associations' do
# belongs to Series
expect(@snapshot.series).to eq(@series)
expect(@snapshot.test_good_series).to eq(@series)
end
it 'pay' do
@unpaid_snapshot = TestGoodSnapshot.create(
series: @series,
count: 2
)
# 支付前 属性 delegate to Series
expect(@unpaid_snapshot.price).to eq(@series.price)
expect(@unpaid_snapshot.attributes['price']).to eq(nil)
# 支付后 copy_info
@unpaid_snapshot.copy_info
copy_attrs = [:original_price, :price, :spec, :banners, :cover_image, :detail]
expect(@unpaid_snapshot.attributes.slice(copy_attrs)).to eq(@series.attributes.slice(copy_attrs))
expect(@unpaid_snapshot.meta).to eq(@good.meta.merge(@series.meta))
end
end
describe 'About Cart' do
it 'methods' do
TestOrder::Cart.destroy_all
# 不存在时创建
expect(@buyer.test_order_cart.class).to eq(TestOrder::Cart)
# 存在时加载
expect(@buyer.test_order_cart.class).to eq(TestOrder::Cart)
# Add Snapshot to Cart
@buyer.add_to_test_order_cart(@snapshot)
expect(@snapshot.test_order_cart).to eq(@buyer.test_order_cart)
end
end
describe 'About Order' do
it 'Associations' do
@buyer.test_order_cart # create a TestOrder::Cart instance object
# Should not include the TestOrder::Cart instance object
expect(@buyer.test_orders.empty?).to be(true)
end
describe do
before do
@order = @buyer.test_orders.create
@snapshot_1 = @order.test_good_snapshots.create(count: 2, series: @series)
@snapshot_2 = @order.test_good_snapshots.create(count: 10, series: @series)
end
it 'Methods' do
# total_price
expect(@order.total_price).to eq(@snapshot_1.total_price + @snapshot_2.total_price)
expect(@order.total_original_price).to eq(@snapshot_1.total_original_price + @snapshot_2.total_original_price)
end
it 'About state machine' do
expect(@snapshot_1.reload.attributes.values.include?(nil)).to eq(true)
@order.pay!
expect(@order.reload.pay_time).not_to be_nil
# copy snapshot info from series
expect(@snapshot_1.reload.attributes.values.include?(nil)).to eq(false)
@order.send_out!
# set delivery_time
expect(@order.reload.delivery_time).not_to be_nil
@order.get_it!
# set receipt_time
expect(@order.reload.receipt_time).not_to be_nil
end
end
end
end
describe 'Catalog' do
before do
class ClothingCatalog < Shotengai::Catalog; end
class OtherCatalog < Shotengai::Catalog; end
@klass = ClothingCatalog
@clothing = @klass.create(name: '衣服')
@trousers = @klass.create(name: '下装', super_catalog: @clothing)
@pants = @klass.create(name: '短裤', super_catalog: @trousers)
@tops = @klass.create(name: '上装', super_catalog: @clothing)
@shirts = @klass.create(name: '衬衫', super_catalog: @tops)
@jacket = @klass.create(name: '夹克', super_catalog: @tops)
@sweaters = @klass.create(name: '毛衣', super_catalog: @tops)
@polo_shirts = @klass.create(name: 'Polo衫', super_catalog: @shirts)
@other_clothes_catalog = @klass.create(name: '其他分类')
@other_catalog = OtherCatalog.create(name: '其他分类')
end
describe 'Methods' do
it 'instance_methods' do
# .ancestors
expect(@jacket.ancestors).to eq( [@clothing, @tops, @jacket] )
# .chain_chain
expect(@polo_shirts.name_chain).to eq( ["衣服", "上装", "衬衫", "Polo衫"] )
# .nest_level
expect(@polo_shirts.nest_level).to eq(4)
# .brothers
expect(@shirts.brothers.sort).to eq( [@jacket, @sweaters, @shirts].sort)
end
it 'Class Methods' do
# self.top_catalogs
expect(@klass.top_catalogs.sort).to eq([@clothing, @other_clothes_catalog].sort)
# self.validate_chain
expect(@klass.validate_name_chain(["衣服", "上装", "衬衫", "Polo衫"])).to eq(["衣服", "上装", "衬衫", "Polo衫"])
expect {
@klass.validate_name_chain( ["衣服", "上装", "error 衬衫", "Polo衫"])
}.to raise_error(Shotengai::WebError)
end
end
describe 'Catalog System With Product' do
before do
class TestGood < Shotengai::Product
join_catalog_system ClothingCatalog, as: :sort
join_catalog_system OtherCatalog
end
end
it 'Edit Catalog' do
@good = create(:test_good)
# create clothes catalog_list
list = ["衣服", "上装", "衬衫"]
@good.sort_list = list
@good.save!
expect(@good.reload.sort_list).to eq(list)
expect {
@good.sort_list = ["衣服", "error 上装", "衬衫"]
}.to raise_error(Shotengai::WebError)
# 完整替换分类
@good.update!(
sort_list: ["衣服", "上装", "衬衫", "Polo衫"],
other_catalog_list: [ @other_catalog.name ]
)
expect(@good.reload.sort_list).to eq(["衣服", "上装", "衬衫", "Polo衫"])
expect(@good.reload.other_catalog_list).to eq( [ @other_catalog.name ] )
end
before do
@jacket_good = create(:test_good,
sort_list: @jacket.name_chain
)
@polo_shirts_good = create(:test_good,
sort_list: @polo_shirts.name_chain
)
@pants_good = create(:test_good,
sort_list: @pants.name_chain,
other_catalog_list: @other_catalog.name_chain
)
end
it 'Query by Catalog' do
expect(
TestGood.tagged_with('衣服', on: :sort).sort
).to eq([ @jacket_good, @polo_shirts_good, @pants_good ].sort)
expect(
TestGood.tagged_with('上装', on: :sort).sort
).to eq([ @jacket_good, @polo_shirts_good ].sort)
# other_catalog_list
expect(
TestGood.tagged_with(@other_catalog.name, on: :other_catalogs).sort
).to eq([ @pants_good ].sort)
end
end
end
after do
ActiveRecord::Migration[5.1].subclasses.each do |migrate|
migrate.migrate(:down)
end
end
end
# ActiveRecord::Migration[5.1].subclasses.each do |migrate|
# migrate.migrate(:down)
# end
\ No newline at end of file
require "bundler/setup"
require 'rails'
require 'shotengai'
require 'support/factory_girl'
# Dir['../support/**/*.rb'].each { |f| require f }
RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = ".rspec_status"
config.expect_with :rspec do |c|
c.syntax = :expect
end
end
require 'factory_girl_rails'
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
# RSpec without Rails
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.before(:suite) do
FactoryGirl.find_definitions
end
end
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment