mad_cart 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+
3
+ describe "configuration" do
4
+
5
+ # use a clean instance for each test
6
+ before(:each) do
7
+ clear_config
8
+ end
9
+
10
+ describe "stores" do
11
+
12
+ it "does not require store to be added if credentials are passed to constructor" do
13
+ lambda{ MadCart::Store::BigCommerce.new({:api_key => 'a_fake_key', :store_url => '/path/to/store', :username => 'bob'}) }.should_not raise_error
14
+ end
15
+
16
+ it "allows config values to be set for a store" do
17
+ config_data = {:arbitrary => 'data'}
18
+ MadCart.configure do |config|
19
+ config.add_store :big_commerce, config_data
20
+ end
21
+
22
+ MadCart.config.big_commerce.should == config_data
23
+ end
24
+
25
+ it "gives returns nil if there's no config" do
26
+ MadCart.config.missing_store.should be_nil
27
+ end
28
+
29
+ end
30
+
31
+ describe "models" do
32
+ it "allows custom attribute names to be set" do
33
+ lambda {
34
+ MadCart.configure do |config|
35
+ config.attribute_map :products, {"name" => "title"}
36
+ end
37
+ }.should_not raise_error
38
+
39
+ MadCart.config.attribute_maps["products"].should == {"name" => "title"}
40
+ end
41
+
42
+ it "allows additional attributes to be included in models" do
43
+ MadCart.configure do |config|
44
+ config.include_attributes :products => [:external_id, :url]
45
+ end
46
+
47
+ MadCart.config.included_attributes[:products].should == [:external_id, :url]
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,25 @@
1
+ require "spec_helper"
2
+
3
+ describe MadCart do
4
+
5
+ it "allows configuration" do
6
+ MadCart.configure do |config|
7
+ config.should be_a(MadCart::Configuration)
8
+ config.should be_a(Singleton)
9
+ end
10
+ end
11
+
12
+ it "provides config values" do
13
+ MadCart.config.should be_a(MadCart::Configuration::Data)
14
+ end
15
+
16
+ it "complains when the #config getter is used to set config values" do
17
+ lambda do
18
+ MadCart.config {|config| config.add_store :big_commerce }
19
+ end.should raise_error(ArgumentError, "MadCart.config does not support blocks. Use MadCart.configure to set config values.")
20
+ end
21
+
22
+ it "complains if #configure is used incorrectly" do
23
+ lambda { MadCart.configure }.should raise_error(ArgumentError, "MadCart.configure requires a block argument.")
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ describe MadCart::Store::Base do
4
+ before(:each) do
5
+ clear_config
6
+ Object.send(:remove_const, :MyModel) if Object.const_defined?(:MyModel)
7
+ class MyModel
8
+ include MadCart::Model::Base
9
+ required_attributes :name, :description
10
+ end
11
+ end
12
+
13
+ describe "attributes" do
14
+ it "can be configured to include additional attributes" do
15
+ MadCart.configure do |config|
16
+ config.include_attributes :my_models => [:external_id, :url]
17
+ end
18
+
19
+ o = MyModel.new(:name => 'whiskey', :description => 'tasty', :external_id => 2, :url => 'path/to/whiskey', :discarded => 'property')
20
+ o.attributes.should == {"name" => 'whiskey', "description" => 'tasty', "external_id" => 2, "url" => 'path/to/whiskey'}
21
+ end
22
+
23
+ it "includes mapped attributes" do
24
+ MadCart.configure do |config|
25
+ config.attribute_map :my_models, :old_name => :new_name
26
+ end
27
+
28
+ o = MyModel.new(:name => 'whiskey', :description => 'tasty', :discarded => 'property', :old_name => 'is included')
29
+ o.attributes.should == {"name" => 'whiskey', "description" => 'tasty', "new_name" => 'is included'}
30
+ end
31
+
32
+ it "allows two sources to map to the same model" do
33
+ MadCart.configure do |config|
34
+ config.include_attributes :my_models => [:external_id]
35
+ config.attribute_map :my_models, :id => :external_id
36
+ end
37
+
38
+ source_a = {:name => 'whiskey', :description => 'tasty', :discarded => 'property', :id => 'has been renamed'}
39
+ source_b = {:name => 'whiskey', :description => 'tasty', :discarded => 'property', :external_id => 'is included'}
40
+
41
+ model_a = MyModel.new(source_a)
42
+ model_a.attributes.should == {"name" => 'whiskey', "description" => 'tasty', "external_id" => 'has been renamed'}
43
+
44
+ model_b = MyModel.new(source_b)
45
+ model_b.attributes.should == {"name" => 'whiskey', "description" => 'tasty', "external_id" => 'is included'}
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe MadCart::Model::Customer do
4
+
5
+ before(:each) do
6
+ clear_config
7
+ end
8
+
9
+ it "returns the default attributes" do
10
+ attrs = {"first_name" => 'Bob', "last_name" => 'Sagat', "email" => 'bob@sagat.com'}
11
+ c = MadCart::Model::Customer.new(attrs)
12
+
13
+ c.attributes.should eql(attrs)
14
+ end
15
+
16
+ it "allows attributes to be overwritten" do
17
+ MadCart.configure do |config|
18
+ config.attribute_map :customers, {:first_name => :name}
19
+ end
20
+
21
+ c = MadCart::Model::Customer.new(:first_name => 'Bob', :last_name => 'Sagat', :email => 'bob@sagat.com')
22
+
23
+ c.attributes.should eql({"name" => 'Bob', "last_name" => 'Sagat', "email" => 'bob@sagat.com'})
24
+ end
25
+
26
+ it "exposes all additional attributes provided by the api" do
27
+ c = MadCart::Model::Customer.new("first_name" => 'Bob', "last_name" => 'Sagat', "email" => 'bob@sagat.com', "with" => 'some', "additional" => 'fields' )
28
+
29
+ c.additional_attributes.should eql({"with" => 'some', "additional" => 'fields'})
30
+ end
31
+
32
+ describe "validation" do
33
+
34
+ before(:each) do
35
+ @args = {:first_name => 'first_name',
36
+ :last_name => 'last_name',
37
+ :email => 'email'
38
+ }
39
+ end
40
+
41
+ it "requires first_name" do
42
+ @args.delete(:first_name)
43
+ lambda{ MadCart::Model::Customer.new(@args) }.should raise_error(ArgumentError)
44
+ end
45
+
46
+ it "requires last_name" do
47
+ @args.delete(:last_name)
48
+ lambda{ MadCart::Model::Customer.new(@args) }.should raise_error(ArgumentError)
49
+ end
50
+
51
+ it "requires email" do
52
+ @args.delete(:email)
53
+ lambda{ MadCart::Model::Customer.new(@args) }.should raise_error(ArgumentError)
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,74 @@
1
+ require "spec_helper"
2
+
3
+ describe MadCart::Model::Product do
4
+
5
+ before(:each) do
6
+ clear_config
7
+ end
8
+
9
+ it "returns the default attributes" do
10
+ extra_attrs = {"external_id" => 'id', "price" => '12USD',
11
+ "url" => 'path/to/product', "currency_code" => 'ZAR',
12
+ "square_image_url" => 'path/to/square/image'}
13
+ default_attrs = {"name" => "product name", "description" => 'product description',
14
+ "image_url" => 'path/to/image'}
15
+
16
+ c = MadCart::Model::Product.new(default_attrs.merge(extra_attrs))
17
+
18
+ c.attributes.should eql(default_attrs)
19
+ end
20
+
21
+ it "allows attribute names to be overwritten" do
22
+ MadCart.configure do |config|
23
+ config.attribute_map :products, {:square_image_url => :thumbnail,
24
+ :name => :title}
25
+ end
26
+
27
+ attrs = {"description" => 'product description',
28
+ "image_url" => 'path/to/image'}
29
+
30
+ c = MadCart::Model::Product.new(attrs.merge(:name => "product name", :square_image_url => 'path/to/square/image'))
31
+
32
+ c.attributes.should eql(attrs.merge("title" => "product name",
33
+ "thumbnail" => 'path/to/square/image'))
34
+ end
35
+
36
+ it "exposes all additional attributes provided by the api" do
37
+ attrs = {"name" => "product name", "description" => 'product description',
38
+ "image_url" => 'path/to/image'}
39
+
40
+ c = MadCart::Model::Product.new(attrs.merge(:with => 'some', :additional => 'fields'))
41
+
42
+ c.additional_attributes.should eql({"with" => 'some', "additional" => 'fields'})
43
+ end
44
+
45
+ describe "validation" do
46
+
47
+ before(:each) do
48
+ @args = {:name => 'name',
49
+ :external_id => 'external_id',
50
+ :description => 'description',
51
+ :price => 'price',
52
+ :url => 'url',
53
+ :currency_code => 'currency_code',
54
+ :image_url => 'image_url',
55
+ :squre_image_url => 'square_image_url'
56
+ }
57
+ end
58
+
59
+ it "requires name" do
60
+ @args.delete(:name)
61
+ lambda{ MadCart::Model::Product.new(@args) }.should raise_error(ArgumentError)
62
+ end
63
+
64
+ it "requires description" do
65
+ @args.delete(:description)
66
+ lambda{ MadCart::Model::Product.new(@args) }.should raise_error(ArgumentError)
67
+ end
68
+
69
+ it "requires image_url" do
70
+ @args.delete(:image_url)
71
+ lambda{ MadCart::Model::Product.new(@args) }.should raise_error(ArgumentError)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,231 @@
1
+ require "spec_helper"
2
+
3
+ describe MadCart::Store::Base do
4
+ before(:each) do
5
+ Object.send(:remove_const, :MyStore) if Object.const_defined?(:MyStore)
6
+ class MyStore; include MadCart::Store::Base; end
7
+ class TestResult; end
8
+ end
9
+
10
+ describe "connection" do
11
+ it "adds a create_connection_with method" do
12
+ MyStore.should respond_to(:create_connection_with)
13
+ end
14
+
15
+ it "accepts a method reference" do
16
+ MyStore.class_eval do
17
+ create_connection_with :connection_method
18
+
19
+ def connection_method(args={})
20
+ return TestResult
21
+ end
22
+ end
23
+
24
+ MyStore.new.connection.should == TestResult
25
+ end
26
+
27
+ it "accepts a proc" do
28
+ MyStore.class_eval do
29
+ create_connection_with Proc.new {|args| TestResult }
30
+ end
31
+
32
+ MyStore.new.connection.should == TestResult
33
+ end
34
+
35
+ it "raises an error if any required connection arguments are not present" do
36
+ MyStore.class_eval do
37
+ create_connection_with Proc.new { }, :requires => [:api_key, :username]
38
+ end
39
+
40
+ lambda { MyStore.new(:api_key => 'key').connection }.should raise_error(ArgumentError, "Missing connection arguments: username")
41
+ end
42
+
43
+ it "retrieves configured connection arguments" do
44
+ class MyStore
45
+ create_connection_with Proc.new { }, :requires => [:several, :args]
46
+ end
47
+
48
+ MadCart.configure do |config|
49
+ config.add_store :my_store, {:several => 'of', :args => 'yes?'}
50
+ end
51
+
52
+ lambda { MyStore.new().connection }.should_not raise_error
53
+ end
54
+
55
+ it "retrieves a combination of configured and initialised connection arguments" do
56
+ class MyStore
57
+ create_connection_with Proc.new { }, :requires => [:several, :args]
58
+ end
59
+
60
+ MadCart.configure do |config|
61
+ config.add_store :my_store, {:several => 'only'}
62
+ end
63
+
64
+ lambda { MyStore.new(:args => 'too').connection }.should_not raise_error
65
+ end
66
+ end
67
+
68
+ describe "fetch" do
69
+ it "adds a fetch method" do
70
+ MyStore.should respond_to(:fetch)
71
+ end
72
+
73
+ it "accepts a method reference" do
74
+ MyStore.class_eval do
75
+ fetch :products, :with => :fetch_method
76
+
77
+ def fetch_method
78
+ return [{:some => 'attrs'}]
79
+ end
80
+ end
81
+
82
+ result = double(MadCart::Model::Product)
83
+ MadCart::Model::Product.should_receive(:new).with({:some => 'attrs'}).and_return(result)
84
+ MyStore.new.products.should == [result]
85
+ end
86
+
87
+ it "accepts a proc" do
88
+ MyStore.class_eval do
89
+ fetch :products, :with => Proc.new { [{:some => 'attrs'}, {:some => 'attrs'}] }
90
+ end
91
+
92
+ result = double(MadCart::Model::Product)
93
+ MadCart::Model::Product.should_receive(:new).twice.with({:some => 'attrs'}).and_return(result)
94
+ MyStore.new.products.should == [result, result]
95
+ end
96
+
97
+ it "converts hashes into instances of the mad cart model" do
98
+ attrs = {"external_id" => 'id', "name" => "product name", "description" => 'product description', "price" => '12USD',
99
+ "url" => 'path/to/product', "currency_code" => 'ZAR',
100
+ "image_url" => 'path/to/image', "square_image_url" => 'path/to/square/image'}
101
+ MyStore.class_eval do
102
+ fetch :products, :with => Proc.new { [attrs, attrs] }
103
+ end
104
+
105
+ MyStore.new.products.each{|p| p.should be_a(MadCart::Model::Product) }
106
+ end
107
+
108
+ it "returns instances of the mad cart model if the fetch method returns them" do
109
+ attrs = {"external_id" => 'id', "name" => "product name", "description" => 'product description', "price" => '12USD',
110
+ "url" => 'path/to/product', "currency_code" => 'ZAR',
111
+ "image_url" => 'path/to/image', "square_image_url" => 'path/to/square/image'}
112
+ MyStore.class_eval do
113
+ fetch :products, :with => Proc.new { [MadCart::Model::Product.new(attrs), MadCart::Model::Product.new(attrs)] }
114
+ end
115
+
116
+ MyStore.new.products.each{|p| p.should be_a(MadCart::Model::Product) }
117
+ end
118
+
119
+ it "returns instances of the mad cart model if the format method returns them" do
120
+ attrs = {"external_id" => 'id', "name" => "product name", "description" => 'product description', "price" => '12USD',
121
+ "url" => 'path/to/product', "currency_code" => 'ZAR',
122
+ "image_url" => 'path/to/image', "square_image_url" => 'path/to/square/image'}
123
+ MyStore.class_eval do
124
+ fetch :products, :with => Proc.new { [attrs, attrs] }
125
+ format :products, :with => Proc.new {|p| MadCart::Model::Product.new(p) }
126
+ end
127
+
128
+ MyStore.new.products.each{|p| p.should be_a(MadCart::Model::Product) }
129
+ end
130
+
131
+ it "can be configured to retrieve additional attributes" do
132
+ MadCart.configure do |config|
133
+ config.include_attributes :products => [:external_id]
134
+ end
135
+
136
+ MyStore.class_eval do
137
+ fetch :products, :with => Proc.new { }
138
+ end
139
+ end
140
+ end
141
+
142
+ describe "format" do
143
+ it "adds a format method" do
144
+ MyStore.should respond_to(:format)
145
+ end
146
+
147
+ it "accepts a method reference" do
148
+ MyStore.class_eval do
149
+ fetch :products, :with => Proc.new { [:one => 1] }
150
+ format :products, :with => :format_method
151
+
152
+ def format_method(product)
153
+ return product
154
+ end
155
+ end
156
+
157
+ store = MyStore.new
158
+ store.should_receive(:format_method).with(:one => 1).and_return(double(MadCart::Model::Product))
159
+ store.stub(:ensure_model_format)
160
+ store.products
161
+ end
162
+
163
+ it "accepts a proc" do
164
+ MyStore.class_eval do
165
+ fetch :products, :with => Proc.new { [1] }
166
+ format :products, :with => Proc.new {|product| product.to_s }
167
+ end
168
+
169
+ store = MyStore.new
170
+ store.should_receive(:ensure_model_format).with(:products, ["1"])
171
+ store.products
172
+ end
173
+ end
174
+
175
+ describe "initialize" do
176
+
177
+ it "raises an exception on connection if initialize doesn't store the required connection args" do
178
+ class MyStore
179
+ create_connection_with Proc.new { }, :requires => [:args]
180
+
181
+ def initialize
182
+ end
183
+ end
184
+
185
+ o = MyStore.new
186
+ lambda { o.connection }.should raise_error(MadCart::Store::SetupError,
187
+ "It appears MyStore has overrided the default MadCart::Base initialize method. " +
188
+ "That's fine, but please store any required connection arguments as @init_args " +
189
+ "for the #connection method to use later. Remember to call #after_initialize " +
190
+ "in your initialize method should you require it.")
191
+ end
192
+ end
193
+
194
+ describe "after_initialize" do
195
+ it "adds an after_initialize method" do
196
+ MyStore.should respond_to(:after_initialize)
197
+ end
198
+
199
+ it "accepts a method reference" do
200
+ MyStore.class_eval do
201
+ after_initialize :init_method
202
+ create_connection_with :connect_method
203
+
204
+ def init_method(*args)
205
+ @my_instance_var = args.first[:connection]
206
+ end
207
+
208
+ def connect_method(*args)
209
+ return @my_instance_var
210
+ end
211
+ end
212
+
213
+ MyStore.new(:connection => TestResult).connection.should == TestResult
214
+ end
215
+
216
+ it "accepts a proc" do
217
+ MyStore.class_eval do
218
+ after_initialize Proc.new {|arg| TestResult.new }
219
+ create_connection_with :connect_method
220
+
221
+ def connect_method(*args)
222
+ return @my_instance_var
223
+ end
224
+ end
225
+
226
+ TestResult.should_receive(:new)
227
+
228
+ MyStore.new(:connection => TestResult)
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,117 @@
1
+ require "spec_helper"
2
+
3
+ describe MadCart::Store::BigCommerce do
4
+
5
+ let(:valid_credentials) {
6
+ { :api_key => '0ff0e3939f5f160f36047cf0caa6f699fe24bdeb',
7
+ :store_url => 'store-cr4wsh4.mybigcommerce.com',
8
+ :username => 'admin' }
9
+ }
10
+
11
+ describe "store" do
12
+
13
+ it "expects to be instantiated with an api key, username and store url" do
14
+ lambda { MadCart::Store::BigCommerce.new(:username => 'test', :store_url => 'test').connection }.should raise_error(ArgumentError)
15
+ lambda { MadCart::Store::BigCommerce.new(:api_key => 'test', :username => 'test', :store_url => 'test').connection }.should_not raise_error
16
+ end
17
+
18
+ it "authenticates via basic auth" do
19
+ connection = Faraday.new
20
+ Faraday.stub(:new).and_return(connection)
21
+
22
+ connection.should_receive(:basic_auth).with('username', 'api_key')
23
+
24
+ MadCart::Store::BigCommerce.new(:api_key => 'api_key', :username => 'username', :store_url => 'url').connection
25
+ end
26
+
27
+ end
28
+
29
+ describe "products" do
30
+
31
+ context "retrieval" do
32
+
33
+ it "returns all products" do
34
+ VCR.use_cassette('big_commerce') do
35
+ api = MadCart::Store::BigCommerce.new(valid_credentials)
36
+
37
+ api.products.size.should == 45
38
+
39
+ first_product = api.products.first
40
+
41
+ first_product.should be_a(MadCart::Model::Product)
42
+ first_product.name.should_not be_nil
43
+ first_product.description.should_not be_nil
44
+ first_product.image_url.should_not be_nil
45
+ end
46
+ end
47
+
48
+ it "returns an empty array when there are no products" do
49
+ VCR.use_cassette('big_commerce_no_records') do
50
+ api = MadCart::Store::BigCommerce.new(valid_credentials)
51
+ api.products.should == []
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ context "count" do
58
+
59
+ it "returns how many products there are" do
60
+ VCR.use_cassette('big_commerce') do
61
+ api = MadCart::Store::BigCommerce.new(valid_credentials)
62
+ api.products_count.should == 45
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ describe "customers" do
71
+ context "retrieval" do
72
+
73
+ it "returns all customers" do
74
+ VCR.use_cassette('big_commerce') do
75
+ api = MadCart::Store::BigCommerce.new(valid_credentials)
76
+
77
+ api.customers.size.should be > 0
78
+ api.customers.first.should be_a(MadCart::Model::Customer)
79
+ end
80
+ end
81
+
82
+ it "returns an empty array whern there are no customers" do
83
+ VCR.use_cassette('big_commerce_no_records') do
84
+ api = MadCart::Store::BigCommerce.new(valid_credentials)
85
+
86
+ api.customers.should eql([])
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ describe "validating credentials" do
93
+
94
+ it "succeeds if it can get time.json from big commerce" do
95
+ VCR.use_cassette('big_commerce') do
96
+ api = MadCart::Store::BigCommerce.new(valid_credentials)
97
+
98
+ api.should be_valid
99
+ end
100
+ end
101
+
102
+ it "fails if it cannot get time.json from big commerce" do
103
+ VCR.use_cassette('big_commerce') do
104
+ api = MadCart::Store::BigCommerce.new(
105
+ :api_key => 'an-invalid-key',
106
+ :store_url => 'store-cr4wsh4.mybigcommerce.com',
107
+ :username => 'admin'
108
+ )
109
+
110
+ api.should_not be_valid
111
+ end
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+ end