rets 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/Manifest.txt +24 -0
  4. data/README.md +73 -1
  5. data/Rakefile +1 -1
  6. data/lib/rets.rb +202 -1
  7. data/lib/rets/client.rb +83 -94
  8. data/lib/rets/http_client.rb +42 -0
  9. data/lib/rets/metadata.rb +15 -3
  10. data/lib/rets/metadata/caching.rb +59 -0
  11. data/lib/rets/metadata/file_cache.rb +29 -0
  12. data/lib/rets/metadata/json_serializer.rb +27 -0
  13. data/lib/rets/metadata/lookup_table.rb +65 -0
  14. data/lib/rets/metadata/lookup_type.rb +3 -4
  15. data/lib/rets/metadata/marshal_serializer.rb +27 -0
  16. data/lib/rets/metadata/multi_lookup_table.rb +70 -0
  17. data/lib/rets/metadata/null_cache.rb +24 -0
  18. data/lib/rets/metadata/resource.rb +39 -29
  19. data/lib/rets/metadata/rets_class.rb +27 -23
  20. data/lib/rets/metadata/rets_object.rb +32 -0
  21. data/lib/rets/metadata/table.rb +9 -101
  22. data/lib/rets/metadata/table_factory.rb +19 -0
  23. data/lib/rets/metadata/yaml_serializer.rb +27 -0
  24. data/lib/rets/parser/compact.rb +61 -18
  25. data/lib/rets/parser/error_checker.rb +8 -1
  26. data/test/fixtures.rb +58 -0
  27. data/test/test_caching.rb +89 -0
  28. data/test/test_client.rb +44 -24
  29. data/test/test_error_checker.rb +18 -0
  30. data/test/test_file_cache.rb +42 -0
  31. data/test/test_http_client.rb +96 -60
  32. data/test/test_json_serializer.rb +26 -0
  33. data/test/test_marshal_serializer.rb +26 -0
  34. data/test/test_metadata.rb +62 -450
  35. data/test/test_metadata_class.rb +50 -0
  36. data/test/test_metadata_lookup_table.rb +21 -0
  37. data/test/test_metadata_lookup_type.rb +12 -0
  38. data/test/test_metadata_multi_lookup_table.rb +60 -0
  39. data/test/test_metadata_object.rb +20 -0
  40. data/test/test_metadata_resource.rb +140 -0
  41. data/test/test_metadata_root.rb +151 -0
  42. data/test/test_metadata_table.rb +21 -0
  43. data/test/test_metadata_table_factory.rb +24 -0
  44. data/test/test_parser_compact.rb +23 -28
  45. data/test/test_yaml_serializer.rb +26 -0
  46. metadata +29 -5
@@ -0,0 +1,89 @@
1
+ require_relative "helper"
2
+
3
+ require "stringio"
4
+
5
+ class TestCaching < MiniTest::Test
6
+
7
+ class MemoryCache
8
+
9
+ def initialize
10
+ reset
11
+ end
12
+
13
+ def save(&block)
14
+ reset
15
+ yield @io
16
+ end
17
+
18
+ def load(&block)
19
+ @io.rewind
20
+ yield @io
21
+ end
22
+
23
+ private
24
+
25
+ def reset
26
+ @io = StringIO.new
27
+ end
28
+
29
+ end
30
+
31
+ def test_make_defaults
32
+ caching = Rets::Metadata::Caching.make({})
33
+ assert_instance_of Rets::Metadata::NullCache, caching.cache
34
+ assert_instance_of Rets::Metadata::MarshalSerializer, caching.serializer
35
+ end
36
+
37
+ def test_round_trip
38
+ logger = Logger.new("/dev/null")
39
+ sources = {"foo" => "bar"}
40
+ metadata = Rets::Metadata::Root.new(logger, sources)
41
+ caching = Rets::Metadata::Caching.make(metadata_cache: MemoryCache.new)
42
+ caching.save(metadata)
43
+ loaded_metadata = caching.load(logger)
44
+ assert_same logger, loaded_metadata.logger
45
+ assert_equal sources, loaded_metadata.sources
46
+ end
47
+
48
+ def test_load_when_serializer_fails
49
+ serializer = Class.new do
50
+ def load(file)
51
+ nil
52
+ end
53
+ end.new
54
+ logger = Logger.new("/dev/null")
55
+ caching = Rets::Metadata::Caching.make(
56
+ metadata_cache: MemoryCache.new,
57
+ metadata_serializer: serializer,
58
+ )
59
+ loaded_metadata = caching.load(logger)
60
+ assert_nil loaded_metadata
61
+ end
62
+
63
+ def test_load_when_serializer_returns_wrong_type
64
+ serializer = Class.new do
65
+ def load(file)
66
+ Object.new
67
+ end
68
+ end.new
69
+ logger = Logger.new("/dev/null")
70
+ caching = Rets::Metadata::Caching.make(
71
+ metadata_cache: MemoryCache.new,
72
+ metadata_serializer: serializer,
73
+ )
74
+ loaded_metadata = caching.load(logger)
75
+ assert_nil loaded_metadata
76
+ end
77
+
78
+ def test_load_when_cache_fails
79
+ logger = Logger.new("/dev/null")
80
+ cache = stub
81
+ cache.expects(:load).returns(nil)
82
+ caching = Rets::Metadata::Caching.make(
83
+ metadata_cache: cache,
84
+ )
85
+ loaded_metadata = caching.load(logger)
86
+ assert_nil loaded_metadata
87
+ end
88
+
89
+ end
@@ -21,9 +21,15 @@ class TestClient < MiniTest::Test
21
21
  end
22
22
 
23
23
  def test_capability_url_returns_parsed_url
24
- @client.capabilities = { "foo" => "/foo" }
24
+ client = Rets::Client.new(:login_url => "http://example.com", :capabilities => { "foo" => "/foo" })
25
25
 
26
- assert_equal "http://example.com/foo", @client.capability_url("foo")
26
+ assert_equal "http://example.com/foo", client.capability_url("foo")
27
+ end
28
+
29
+ def test_cached_capabilities_case_insensitive
30
+ client = Rets::Client.new(:login_url => "http://example.com", :capabilities => { "foo" => "/foo" })
31
+
32
+ assert_equal client.capabilities.default_proc, Rets::Client::CASE_INSENSITIVE_PROC
27
33
  end
28
34
 
29
35
  def test_capabilities_calls_login_when_nil
@@ -31,6 +37,16 @@ class TestClient < MiniTest::Test
31
37
  @client.capabilities
32
38
  end
33
39
 
40
+ def test_capabilities_does_not_call_login_after_login
41
+ response = mock
42
+ response.stubs(:body).returns(CAPABILITIES)
43
+ @client.stubs(:http_get).returns(response)
44
+ @client.login
45
+
46
+ @client.expects(:login).never
47
+ @client.capabilities
48
+ end
49
+
34
50
  def test_tries_increments_with_each_call
35
51
  assert_equal 1, @client.tries
36
52
  assert_equal 2, @client.tries
@@ -81,6 +97,18 @@ class TestClient < MiniTest::Test
81
97
  logger.debug "foo"
82
98
  end
83
99
 
100
+ def test_find_first_calls_find_every_with_limit_one
101
+ assert_raises ArgumentError do
102
+ @client.find_every({})
103
+ end
104
+ assert_raises ArgumentError do
105
+ @client.find_every(:search_type => "Foo")
106
+ end
107
+ assert_raises ArgumentError do
108
+ @client.find_every(:class => "Bar")
109
+ end
110
+ end
111
+
84
112
  def test_find_first_calls_find_every_with_limit_one
85
113
  @client.expects(:find_every).with({:limit => 1, :foo => :bar}, nil).returns([1,2,3])
86
114
 
@@ -108,7 +136,7 @@ class TestClient < MiniTest::Test
108
136
 
109
137
  Rets::Parser::Compact.expects(:parse_document).with("An ascii string")
110
138
 
111
- @client.find_every({}, false)
139
+ @client.find_every(:search_type => "Foo", :class => "Bar")
112
140
  end
113
141
 
114
142
  def test_response_text_encoding_from_utf_8
@@ -120,7 +148,7 @@ class TestClient < MiniTest::Test
120
148
 
121
149
  Rets::Parser::Compact.expects(:parse_document).with("Some string with non-ascii characters \u0119")
122
150
 
123
- @client.find_every({}, false)
151
+ @client.find_every(:search_type => "Foo", :class => "Bar")
124
152
  end
125
153
 
126
154
  def test_response_text_encoding_from_utf_16
@@ -132,7 +160,7 @@ class TestClient < MiniTest::Test
132
160
 
133
161
  Rets::Parser::Compact.expects(:parse_document).with("Some string with non-utf-8 characters \uFFFD")
134
162
 
135
- @client.find_every({}, false)
163
+ @client.find_every(:search_type => "Foo", :class => "Bar")
136
164
  end
137
165
 
138
166
  def test_find_retries_when_receiving_no_records_found
@@ -155,24 +183,24 @@ class TestClient < MiniTest::Test
155
183
 
156
184
  def test_find_retries_on_errors
157
185
  @client.stubs(:find_every).raises(Rets::AuthorizationFailure.new(401, 'Not Authorized')).then.raises(Rets::InvalidRequest.new(20134, 'Not Found')).then.returns([])
186
+ @client.stubs(:login)
158
187
  @client.find(:all, :foo => :bar)
159
188
  end
160
189
 
161
- def test_find_retries_on_errors_preserves_resolve
162
- @client.stubs(:find_every).raises(Rets::AuthorizationFailure.new(401, 'Not Authorized')).then.raises(Rets::InvalidRequest.new(20134, 'Not Found')).then.with({:foo => :bar}, true).returns([])
163
- @client.find(:all, {:foo => :bar, :resolve => true})
164
- end
165
-
166
190
  def test_find_eventually_reraises_errors
167
191
  @client.stubs(:find_every).raises(Rets::AuthorizationFailure.new(401, 'Not Authorized'))
192
+ @client.stubs(:login)
193
+
168
194
  assert_raises Rets::AuthorizationFailure do
169
195
  @client.find(:all, :foo => :bar)
170
196
  end
171
197
  end
172
198
 
173
- def test_fixup_keys
174
- assert_equal({ "Foo" => "bar" }, @client.fixup_keys(:foo => "bar"))
175
- assert_equal({ "FooFoo" => "bar" }, @client.fixup_keys(:foo_foo => "bar"))
199
+ def test_find_logs_in_after_auth_error
200
+ @client.stubs(:find_every).raises(Rets::AuthorizationFailure.new(401, 'Not Authorized')).then.returns(["foo"])
201
+
202
+ @client.expects(:login)
203
+ @client.find(:all, :foo => :bar)
176
204
  end
177
205
 
178
206
  def test_all_objects_calls_objects
@@ -189,10 +217,10 @@ class TestClient < MiniTest::Test
189
217
  end
190
218
 
191
219
  def test_objects_handle_array_argument
192
- @client.expects(:fetch_object).with("1,2", :foo => :bar)
220
+ @client.expects(:fetch_object).with("1:2:3", :foo => :bar)
193
221
  @client.stubs(:create_parts_from_response)
194
222
 
195
- @client.objects([1,2], :foo => :bar)
223
+ @client.objects([1,2,3], :foo => :bar)
196
224
  end
197
225
 
198
226
  def test_objects_raises_on_other_arguments
@@ -228,7 +256,7 @@ class TestClient < MiniTest::Test
228
256
  def test_create_parts_from_response_returns_a_single_part_when_not_multipart_response
229
257
  response = {}
230
258
  response.stubs(:header => { "content-type" => ['text/plain']})
231
- response.stubs(:headers => { "content-type" => ['text/plain']})
259
+ response.stubs(:headers => { "Content-Type" => 'text/plain'})
232
260
  response.stubs(:body => "fakebody")
233
261
 
234
262
  parts = @client.create_parts_from_response(response)
@@ -249,14 +277,6 @@ class TestClient < MiniTest::Test
249
277
  assert_equal "foo", @client.object("1", :foo => :bar)
250
278
  end
251
279
 
252
- def test_metadata_caches
253
- metadata = stub(:current? => true)
254
- @client.metadata = metadata
255
- @client.stubs(:capabilities => {})
256
-
257
- assert_same metadata, @client.metadata, "Should be memoized"
258
- end
259
-
260
280
  def test_decorate_result_handles_bad_metadata
261
281
  result = {'foo' => 'bar'}
262
282
  rets_class = stub
@@ -59,6 +59,16 @@ class TestErrorChecker < MiniTest::Test
59
59
  end
60
60
  end
61
61
 
62
+ Rets::Parser::ErrorChecker::INVALID_REQUEST_ERROR_MAPPING.each do |error_code, error_class|
63
+ define_method("test_#{error_class}_failure") do
64
+ response = mock
65
+ response.stubs(:body).returns(error_body_with_code(error_code))
66
+ assert_raises error_class do
67
+ Rets::Parser::ErrorChecker.check(response)
68
+ end
69
+ end
70
+ end
71
+
62
72
  def test_invalid_request_failure
63
73
  response = mock
64
74
  response.stubs(:body).returns(RETS_INVALID_REQUEST_ERROR)
@@ -66,4 +76,12 @@ class TestErrorChecker < MiniTest::Test
66
76
  Rets::Parser::ErrorChecker.check(response)
67
77
  end
68
78
  end
79
+
80
+ def error_body_with_code(code)
81
+ <<-XML
82
+ <?xml version="1.0"?>
83
+ <RETS ReplyCode="#{code}" ReplyText="Error message">
84
+ </RETS>
85
+ XML
86
+ end
69
87
  end
@@ -0,0 +1,42 @@
1
+ require_relative "helper"
2
+
3
+ require "tempfile"
4
+
5
+ class TestFileCache < MiniTest::Test
6
+
7
+ def with_tempfile(&block)
8
+ Tempfile.open(File.basename(__FILE__), &block)
9
+ end
10
+
11
+ def with_temp_path
12
+ with_tempfile do |file|
13
+ path = file.path
14
+ begin
15
+ file.close!
16
+ yield path
17
+ ensure
18
+ File.delete(path)
19
+ end
20
+ end
21
+ end
22
+
23
+ def test_save
24
+ with_temp_path do |path|
25
+ cache = Rets::Metadata::FileCache.new(path)
26
+ cache.save { |file| file.print "foo" }
27
+ file_contents = File.read(path)
28
+ assert_equal "foo", file_contents
29
+ end
30
+ end
31
+
32
+ def test_load
33
+ with_tempfile do |file|
34
+ file.print "foo"
35
+ file.close
36
+ cache = Rets::Metadata::FileCache.new(file.path)
37
+ file_contents = cache.load(&:read)
38
+ assert_equal "foo", file_contents
39
+ end
40
+ end
41
+
42
+ end
@@ -31,66 +31,102 @@ class TestHttpClient < MiniTest::Test
31
31
 
32
32
  assert_equal @http_client.http_post(url, {}), response
33
33
  end
34
+ end
35
+
36
+ class CookieManagement < MiniTest::Test
37
+ def setup
38
+ @logger = Rets::Client::FakeLogger.new
39
+ @logger.stubs(:debug?).returns(false)
40
+
41
+ @cm = WebAgent::CookieManager.new
42
+ http = HTTPClient.new
43
+ http.cookie_manager = @cm
44
+ @client = Rets::HttpClient.new(http, {}, nil, "http://rets.rets.com/somestate/login.aspx")
45
+ end
46
+
47
+ def teardown
48
+ # Empty cookie jar
49
+ @cm.cookies = []
50
+ end
51
+
52
+ def test_http_cookie_with_one_cookie_from_one_domain
53
+ set_cookie = "RETS-Session-ID=879392834723043209; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
54
+ @cm.parse(set_cookie, URI.parse("http://www.rets.rets.com"))
55
+ assert_equal "879392834723043209", @client.http_cookie('RETS-Session-ID')
56
+ end
57
+
58
+ def test_http_cookie_with_multiple_cookies_from_one_domain
59
+ # NOTE: Cookies are ordered alphabetically by name when retrieving
60
+ set_cookie_1 = "RETS-Session-ID=879392834723043209; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
61
+ @cm.parse(set_cookie_1, URI.parse("http://www.rets.rets.com"))
62
+
63
+ set_cookie_2 = "Zoo=Bar; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
64
+ @cm.parse(set_cookie_2, URI.parse("http://www.rets.rets.com"))
65
+
66
+ set_cookie_3 = "Foo=Bar; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
67
+ @cm.parse(set_cookie_3, URI.parse("http://www.rets.rets.com"))
68
+
69
+ assert_equal "879392834723043209", @client.http_cookie('RETS-Session-ID')
70
+ end
71
+
72
+ def test_http_cookie_with_no_cookies_from_domain
73
+ assert_equal nil, @client.http_cookie('RETS-Session-ID')
74
+ end
75
+
76
+ def test_save_cookie_store
77
+ cookie_file = Tempfile.new('cookie_file').path
78
+
79
+ #setup cookie store
80
+ client_a = Rets::HttpClient.from_options({ cookie_store: cookie_file, login_url: "http://rets.rets.com/somestate/login.aspx" }, @logger)
81
+
82
+ #add cookie
83
+ cookie = "RETS-Session-ID=879392834723043209; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
84
+ client_a.http.cookie_manager.parse(cookie, URI.parse("http://www.rets.rets.com"))
85
+
86
+ #save cookie
87
+ client_a.save_cookie_store
88
+
89
+ #create new HTTPCLient with same cookie store
90
+ client_b = Rets::HttpClient.from_options({ cookie_store: cookie_file, login_url: "http://rets.rets.com/somestate/login.aspx" }, @logger)
91
+
92
+ #check added cookie exists
93
+ assert_equal "879392834723043209", client_b.http_cookie('RETS-Session-ID')
94
+ end
95
+
96
+ def test_creates_cookie_store_if_missing_during_initialization
97
+ cookie_file = Tempfile.new('cookie_file')
98
+ cookie_file_path = cookie_file.path
99
+
100
+ #remove cookie store
101
+ cookie_file.unlink
102
+
103
+ client = Rets::HttpClient.from_options({cookie_store: cookie_file_path}, @logger)
104
+
105
+ #add cookie
106
+ cookie = "RETS-Session-ID=879392834723043209; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
107
+ client.http.cookie_manager.parse(cookie, URI.parse("http://www.rets.rets.com"))
108
+
109
+ #check added cookie exists
110
+ assert_equal "879392834723043209", client.http_cookie('RETS-Session-ID')
111
+ end
112
+
113
+ def test_creates_cookie_store_if_missing_during_save
114
+ cookie_file = Tempfile.new('cookie_file')
115
+ cookie_file_path = cookie_file.path
116
+
117
+ client = Rets::HttpClient.from_options({cookie_store: cookie_file_path}, @logger)
118
+
119
+ #remove cookie store
120
+ cookie_file.unlink
121
+
122
+ #add cookie
123
+ cookie = "RETS-Session-ID=879392834723043209; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
124
+ client.http.cookie_manager.parse(cookie, URI.parse("http://www.rets.rets.com"))
125
+
126
+ #save cookie
127
+ client.save_cookie_store
34
128
 
35
- class CookieManagement < MiniTest::Test
36
- def setup
37
- @cm = WebAgent::CookieManager.new
38
- http = HTTPClient.new
39
- http.cookie_manager = @cm
40
- @client = Rets::HttpClient.new(http, {}, nil, "http://rets.rets.com/somestate/login.aspx")
41
- end
42
-
43
- def teardown
44
- # Empty cookie jar
45
- @cm.cookies = []
46
- end
47
-
48
- def test_http_cookie_with_one_cookie_from_one_domain
49
- set_cookie = "RETS-Session-ID=879392834723043209; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
50
- @cm.parse(set_cookie, URI.parse("http://www.rets.rets.com"))
51
- assert_equal "879392834723043209", @client.http_cookie('RETS-Session-ID')
52
- end
53
-
54
- def test_http_cookie_with_multiple_cookies_from_one_domain
55
- # NOTE: Cookies are ordered alphabetically by name when retrieving
56
- set_cookie_1 = "RETS-Session-ID=879392834723043209; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
57
- @cm.parse(set_cookie_1, URI.parse("http://www.rets.rets.com"))
58
-
59
- set_cookie_2 = "Zoo=Bar; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
60
- @cm.parse(set_cookie_2, URI.parse("http://www.rets.rets.com"))
61
-
62
- set_cookie_3 = "Foo=Bar; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
63
- @cm.parse(set_cookie_3, URI.parse("http://www.rets.rets.com"))
64
-
65
- assert_equal "879392834723043209", @client.http_cookie('RETS-Session-ID')
66
- end
67
-
68
- def test_http_cookie_with_no_cookies_from_domain
69
- assert_equal nil, @client.http_cookie('RETS-Session-ID')
70
- end
71
-
72
- def test_save_cookie_store
73
- cookie_file = Tempfile.new('cookie_file').path
74
-
75
- #setup cookie store
76
- http_a = HTTPClient.new
77
- http_a.set_cookie_store(cookie_file)
78
- client_a = Rets::HttpClient.new(http_a, { cookie_store: cookie_file }, nil, "http://rets.rets.com/somestate/login.aspx")
79
-
80
- #add cookie
81
- cookie = "RETS-Session-ID=879392834723043209; path=/; domain=rets.rets.com; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
82
- http_a.cookie_manager.parse(cookie, URI.parse("http://www.rets.rets.com"))
83
-
84
- #save cookie
85
- client_a.save_cookie_store
86
-
87
- #create new HTTPCLient with same cookie store
88
- http_b = HTTPClient.new
89
- http_b.set_cookie_store(cookie_file)
90
- client_b = Rets::HttpClient.new(http_b, { cookie_store: cookie_file }, nil, "http://rets.rets.com/somestate/login.aspx")
91
-
92
- #check added cookie exists
93
- assert_equal "879392834723043209", client_b.http_cookie('RETS-Session-ID')
94
- end
129
+ #check added cookie exists
130
+ assert_equal "879392834723043209", client.http_cookie('RETS-Session-ID')
95
131
  end
96
132
  end