foundationapi 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/Rakefile +37 -0
  2. data/lib/foundation_api.rb +9 -0
  3. data/lib/foundation_api/errors.rb +34 -0
  4. data/lib/foundation_api/event.rb +6 -0
  5. data/lib/foundation_api/event/client.rb +105 -0
  6. data/lib/foundation_api/event/exceptions.rb +20 -0
  7. data/lib/foundation_api/json_rpc.rb +6 -0
  8. data/lib/foundation_api/json_rpc/client.rb +80 -0
  9. data/lib/foundation_api/json_rpc/exceptions.rb +47 -0
  10. data/lib/foundation_api/model.rb +7 -0
  11. data/lib/foundation_api/model/attribute_methods.rb +85 -0
  12. data/lib/foundation_api/model/cached.rb +13 -0
  13. data/lib/foundation_api/model/mapping.rb +24 -0
  14. data/lib/foundation_api/request.rb +29 -0
  15. data/lib/foundation_api/service.rb +56 -0
  16. data/lib/foundation_api/shoulda_matcher.rb +15 -0
  17. data/lib/foundation_api/shoulda_matcher/attribute_alias_matcher.rb +32 -0
  18. data/lib/foundation_api/shoulda_matcher/persistence_method_matcher.rb +91 -0
  19. data/lib/foundation_api/table.rb +6 -0
  20. data/lib/foundation_api/table/persistence.rb +115 -0
  21. data/lib/foundation_api/table/record.rb +143 -0
  22. data/lib/foundation_api/test_helper.rb +53 -0
  23. data/lib/foundation_api/version.rb +27 -0
  24. data/test/foundation_api_test.rb +31 -0
  25. data/test/test_helper.rb +31 -0
  26. data/test/unit/foundation_api/event/client_test.rb +129 -0
  27. data/test/unit/foundation_api/json_rpc/client_test.rb +143 -0
  28. data/test/unit/foundation_api/model/attribute_methods_test.rb +96 -0
  29. data/test/unit/foundation_api/model/cached_test.rb +20 -0
  30. data/test/unit/foundation_api/model/mapping_test.rb +22 -0
  31. data/test/unit/foundation_api/request_test.rb +33 -0
  32. data/test/unit/foundation_api/service_test.rb +54 -0
  33. data/test/unit/foundation_api/table/persistence_test.rb +182 -0
  34. data/test/unit/foundation_api/table/record_test.rb +176 -0
  35. metadata +209 -0
@@ -0,0 +1,53 @@
1
+ require 'net/http'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'foundation_api'))
3
+
4
+ # Mock around Net::HTTP so we don't need a real connection.
5
+ # We just verify whether the correct data is posted and return
6
+ # know test data
7
+
8
+ class Net::HTTP < Net::Protocol
9
+ def connect
10
+ end
11
+ end
12
+
13
+ class Net::HTTPResponse
14
+ def body=(content)
15
+ @body = content
16
+ @read = true
17
+ end
18
+ end
19
+
20
+ class Net::HTTP < Net::Protocol
21
+
22
+ def self.raw_response_data
23
+ @raw_response_data
24
+ end
25
+
26
+ def self.raw_response_data=(data)
27
+ @raw_response_data = data
28
+ end
29
+
30
+ def self.raw_post_body=(body)
31
+ @raw_post_body = body
32
+ end
33
+
34
+ def self.raw_post_body
35
+ @raw_post_body
36
+ end
37
+
38
+ def self.raw_post_path=(path)
39
+ @raw_post_path = path
40
+ end
41
+
42
+ def self.raw_post_path
43
+ @raw_post_path
44
+ end
45
+
46
+ def post(path, body, headers = {})
47
+ res = Net::HTTPSuccess.new('1.2', '200', 'OK')
48
+ self.class.raw_post_path = path
49
+ self.class.raw_post_body = body
50
+ res.body = self.class.raw_response_data
51
+ res
52
+ end
53
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2012 Bingo Entreprenøren AS
2
+ # Copyright (c) 2012 Teknobingo Scandinavia AS
3
+ # Copyright (c) 2012 Knut I. Stenmark
4
+ # Copyright (c) 2012 Patrick Hanevold
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ module FoundationApi
26
+ VERSION = "0.9.9"
27
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2012 Bingo Entreprenøren AS
2
+ # Copyright (c) 2012 Teknobingo Scandinavia AS
3
+ # Copyright (c) 2012 Knut I. Stenmark
4
+ # Copyright (c) 2012 Patrick Hanevold
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ require 'test_helper'
26
+
27
+ class FoundationApiTest < ActiveSupport::TestCase
28
+ test "truth" do
29
+ assert_kind_of Module, FoundationApi
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'simplecov'
2
+ require 'active_support/testing/autorun'
3
+ require 'active_support/test_case'
4
+ require 'active_support/benchmarkable'
5
+ require 'shoulda'
6
+ SimpleCov.start
7
+ require 'foundation_api'
8
+
9
+ class Nada
10
+ def do_nothing(*args)
11
+ end
12
+ alias :debug :do_nothing
13
+ alias :notice :do_nothing
14
+ alias :info :do_nothing
15
+ alias :error :do_nothing
16
+ end
17
+
18
+ class CacheNada
19
+ def self.fetch(*args)
20
+ yield
21
+ end
22
+ end
23
+
24
+ class Rails
25
+ def self.logger
26
+ @logger ||= Nada.new
27
+ end
28
+ def self.cache
29
+ CacheNada
30
+ end
31
+ end
@@ -0,0 +1,129 @@
1
+ require 'test_helper'
2
+ require 'foundation_api/test_helper'
3
+
4
+ class FoundationApi::Event::ClientTest < ActiveSupport::TestCase
5
+ setup do
6
+ class TestClient < FoundationApi::Event::Client
7
+ self.site = "http://localhost:5555"
8
+ end
9
+ FoundationApi::JsonRPC::Client.site = "http://cyber:arts@localhost:4444/api"
10
+ end
11
+
12
+ context 'when authenticatiing it' do
13
+ should 'request token from api server' do
14
+ FoundationApi::JsonRPC::Client.expects(:authenticate).returns('auth_token')
15
+ assert_equal 'auth_token', TestClient.authenticate
16
+ end
17
+ end
18
+
19
+ context 'when requesting it' do
20
+ setup do
21
+ TestClient.auth_token = 'token'
22
+ end
23
+ should 'post the request correctly' do
24
+ TestClient.expects(:authenticate).never
25
+ TestClient.expects(:post_request).with('event', {:apitoken => 'token', :type => '/something/weird', :number => 2}).returns(:anything)
26
+ assert_equal = TestClient.request('event', {:type => '/something/weird', :number => 2})
27
+ end
28
+ should 'authenticate if no token' do
29
+ TestClient.auth_token = nil
30
+ TestClient.expects(:authenticate).once
31
+ TestClient.expects(:post_request).with('event', {:apitoken => nil, :type => '/something/weird', :number => 2}).returns(:anything)
32
+ assert_equal :anything, TestClient.request('event', {:type => '/something/weird', :number => 2})
33
+ end
34
+ should 'reauthenticate if token has expired' do
35
+ TestClient.expects(:authenticate).once
36
+ TestClient.stubs(:post_request).
37
+ raises(FoundationApi::Event::AuthenticationFailed).then.returns(:anything)
38
+ assert_equal :anything, TestClient.request('event', {:type => '/something/weird', :number => 2})
39
+ end
40
+ should 'reauthenticate if token has expired, but only once' do
41
+ TestClient.expects(:authenticate).once
42
+ TestClient.stubs(:post_request).raises(FoundationApi::Event::AuthenticationFailed)
43
+ assert_raises FoundationApi::Event::AuthenticationFailed do
44
+ assert_equal :anything, TestClient.request('event', {:type => '/something/weird', :number => 2})
45
+ end
46
+ end
47
+ should 'leave other exceptions to be handled by the caller' do
48
+ TestClient.expects(:authenticate).never
49
+ TestClient.stubs(:post_request).raises(NoMethodError)
50
+ assert_raises NoMethodError do
51
+ assert_equal :anything, TestClient.request('event', {:type => '/something/weird', :number => 2})
52
+ end
53
+ end
54
+ end
55
+
56
+ context 'when posting to the server it' do
57
+ setup do
58
+ @http = stub('http')
59
+ TestClient.stubs(:http).returns(@http)
60
+ end
61
+ should 'call post correctly' do
62
+ @conn = stub('connection')
63
+ @http.expects(:start).yields(@conn)
64
+
65
+ @conn.expects(:post).with('/event', '{"type":"/the/type","number":1}', {'Content-Type' => 'application/json', 'Accept' => '*/*'})
66
+ .returns(stub(:body =>'{"status":"SUCCESS"}'))
67
+ TestClient.post_request('event', {:type => '/the/type', :number => 1})
68
+ end
69
+ end
70
+
71
+ context 'when successful it' do
72
+ setup do
73
+ Net::HTTP.stubs(:raw_response_data).returns('{"status": "SUCCESS"}')
74
+ TestClient.auth_token = 'tns5ddd7cc5271c8c6b5fab8aaf9ec492ed'
75
+ @result = TestClient.request("geneate_event", :type => '/something/weird', :number => 3 )
76
+ end
77
+
78
+ should 'receive the correct call at the http level' do
79
+ assert_equal JSON.parse(
80
+ '{"apitoken":"tns5ddd7cc5271c8c6b5fab8aaf9ec492ed","type":"/something/weird","number":3}'),
81
+ JSON.parse(Net::HTTP.raw_post_body)
82
+ end
83
+
84
+ should 'correctly parse the response from the server' do
85
+ assert_equal "SUCCESS", @result
86
+ end
87
+ end
88
+
89
+ def expect_exception(status, exception)
90
+ assert_raises exception do
91
+ Net::HTTP.stubs(:raw_response_data).returns(%Q/{"status": "#{status}"}/)
92
+ @result = TestClient.request("geneate_event", :type => '/something/weird', :number => 3 )
93
+ end
94
+ end
95
+
96
+
97
+ context 'when failing it' do
98
+ setup do
99
+ TestClient.auth_token = 'tns5ddd7cc5271c8c6b5fab8aaf9ec492ed'
100
+ end
101
+ should 'generate the appropriate exception' do
102
+ expect_exception 'INVALID_ARGUMENT', FoundationApi::Event::InvalidArgument
103
+ expect_exception 'AUTHENTICATION_FAILED', FoundationApi::Event::AuthenticationFailed
104
+ expect_exception 'MALFORMED_REQUEST', FoundationApi::Event::MalformedRequest
105
+ expect_exception 'OPERATION_ERROR', FoundationApi::Event::OperationError
106
+ expect_exception 'TIMEOUT', FoundationApi::Event::Timeout
107
+ end
108
+ should 'generate exception if status unknown' do
109
+ expect_exception 'SWADA', FoundationApi::Event::UnknownResponseError
110
+ end
111
+ should 'log exception if status unknown' do
112
+ Rails.logger.expects(:error).twice
113
+ expect_exception 'SWADA', FoundationApi::Event::UnknownResponseError
114
+ end
115
+ end
116
+
117
+ context 'when logging it' do
118
+ should 'strip password from message' do
119
+ s = '{"username":"test5","password":"my22secret","phone":"error prone"}'
120
+ expected = '{"username":"test5","password":"[FILTERED]","phone":"error prone"}'
121
+ assert_equal expected, TestClient.send(:password_filter, s)
122
+ s = '{"username":"test5","password": "my22secret","phone":"error prone"}'
123
+ assert_equal expected, TestClient.send(:password_filter, s)
124
+ end
125
+ end
126
+
127
+
128
+
129
+ end
@@ -0,0 +1,143 @@
1
+ require 'test_helper'
2
+ require 'foundation_api/test_helper'
3
+
4
+ class FoundationApi::JsonRPC::ClientTest < ActiveSupport::TestCase
5
+ setup do
6
+ @client = FoundationApi::JsonRPC::Client
7
+ @client.site = "http://cyber:arts@localhost:4444/api"
8
+ end
9
+
10
+ should 'respond to post' do
11
+ assert @client.respond_to?(:post_request)
12
+ end
13
+
14
+ should 'respond to request' do
15
+ assert @client.respond_to?(:request)
16
+ end
17
+
18
+ should 'respond to authenticate' do
19
+ assert @client.respond_to?(:authenticate)
20
+ end
21
+
22
+ should 'generate unique id' do
23
+ t = Time.now
24
+ Time.stubs(:now).returns(t)
25
+ assert_equal "#{t.to_f}.1", @client.unique_id(1)
26
+ end
27
+
28
+ should 'create key value pear' do
29
+ # if you do not understand this joke, there are plenty of books to read
30
+ assert_equal [['key', 'value'],['pear', 'banana']], @client.key_value_array(:key => 'value', :pear => 'banana')
31
+ end
32
+
33
+ context 'when authenticating' do
34
+ setup do
35
+ Net::HTTP.stubs(:raw_response_data).returns('{"result": "abc", "id": "jsonrpc", "error": null}')
36
+ @client.authenticate
37
+ end
38
+
39
+ should 'receive the correct call at the http level' do
40
+ assert_equal JSON.parse('{"params":[{"username":"cyber","password":"arts"}],"method":"APIAuthenticate","id":"jsonrpc"}'),
41
+ JSON.parse(Net::HTTP.raw_post_body)
42
+ end
43
+
44
+ should 'set auth_token' do
45
+ assert_equal 'abc', @client.auth_token
46
+ end
47
+ end
48
+
49
+
50
+ context 'when successful it' do
51
+ setup do
52
+ Net::HTTP.stubs(:raw_response_data).returns('{"result": 9901014, "id": "jsonrpc", "error": null}')
53
+ @client.auth_token = 'tns5ddd7cc5271c8c6b5fab8aaf9ec492ed'
54
+ @result = @client.request("CreatePlayerAccount", :username => 'richard', :attributes => @client.key_value_array({:nickname => "rick", :mobile => "555-555-5555" }))
55
+ end
56
+
57
+ should 'receive the correct call at the http level' do
58
+ assert_equal JSON.parse(
59
+ '{"params":[{"authorization":"tns5ddd7cc5271c8c6b5fab8aaf9ec492ed","username":"richard","attributes":[["mobile","555-555-5555"],["nickname","rick"]]}],"method":"CreatePlayerAccount","id":"jsonrpc"}'),
60
+ JSON.parse(Net::HTTP.raw_post_body)
61
+ end
62
+
63
+ should 'correctly parse the response from the server' do
64
+ assert_equal 9901014, @result
65
+ end
66
+ end
67
+
68
+ context 'when unsuccessful it' do
69
+ setup do
70
+ Net::HTTP.stubs(:raw_response_data).returns('{"result": null, "error": {"code": 1, "message": "INVALID_SECURITY_TOKEN"} , "id": "jsonrpc"}' )
71
+ end
72
+
73
+ should 'raise an exception when a call went wrong' do
74
+ assert_raise FoundationApi::JsonRPC::JsonRPCError do
75
+ @result = @client.post_request("test_method", {:param1 => "value1", :param2 => "value2" })
76
+ end
77
+ end
78
+
79
+ should 'set exception properly' do
80
+ begin
81
+ @result = @client.post_request("test_method", {:param1 => "value1", :param2 => "value2" })
82
+ rescue => e
83
+ assert_equal 1, e.code
84
+ assert_equal "INVALID_SECURITY_TOKEN", e.message
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+ context 'when calling authenticated request' do
91
+ setup do
92
+ @client.auth_token = nil
93
+ Net::HTTP.stubs(:raw_response_data).returns(
94
+ '{"result": "tnsc0d764cb2555b26c8d5e0fbca439c07c", "id": "jsonrpc", "error": null}',
95
+ '{"result": "some data", "id": "jsonrpc", "error": null}')
96
+ @result = @client.request('ASimpleMethod', :with => 'a parameter')
97
+ end
98
+
99
+ should 'set auth_token' do
100
+ assert_equal 'tnsc0d764cb2555b26c8d5e0fbca439c07c', @client.auth_token
101
+ end
102
+
103
+ should 'return data' do
104
+ assert_equal "some data", @result
105
+ end
106
+ end
107
+
108
+ context 'when security token has expired it' do
109
+ setup do
110
+ @client.auth_token = 'kmsikss'
111
+ end
112
+ should 'try to authenticate again' do
113
+ Net::HTTP.stubs(:raw_response_data).returns(
114
+ '{"result": null, "error": {"code": 1, "message": "INVALID_SECURITY_TOKEN"} , "id": "jsonrpc"}',
115
+ '{"result": "tnsc0d764cb2555b26c8d5e0fbca439c07c", "id": "jsonrpc", "error": null}',
116
+ '{"result": "some data", "id": "jsonrpc", "error": null}')
117
+ @result = @client.request('ASimpleMethod', :with => 'a parameter')
118
+ assert_equal "some data", @result
119
+ end
120
+ should 'only try to authenticate once' do
121
+ Net::HTTP.stubs(:raw_response_data).returns(
122
+ '{"result": null, "error": {"code": 1, "message": "INVALID_SECURITY_TOKEN"} , "id": "jsonrpc"}',
123
+ '{"result": null, "error": {"code": 1, "message": "INVALID_SECURITY_TOKEN"} , "id": "jsonrpc"}')
124
+ begin
125
+ @result = @client.request("test_method", {:param1 => "value1", :param2 => "value2" })
126
+ rescue => e
127
+ assert_equal 1, e.code
128
+ assert_equal :invalid_security_token, e.response
129
+ end
130
+
131
+ end
132
+ end
133
+
134
+
135
+ context 'when logging it' do
136
+ should 'strip password from message' do
137
+ s = '{"method":"CreatePlayerAccount","id":"jsonrpc","params":[{"username":"test5","attributes":[["clubhouse_token","ZpXEtiIKHoVA6kyieXQ7yw"],["email","test@tecnobingo.no"],["firstName","Test"],["lastName","One"],["password","my22secret"],["phone","90909090"]],"authorization":"tec-t-ca001.osl.basefarm.net:000-813793a81974dab06b9095a820691135"}]}'
138
+ expected = '{"method":"CreatePlayerAccount","id":"jsonrpc","params":[{"username":"test5","attributes":[["clubhouse_token","ZpXEtiIKHoVA6kyieXQ7yw"],["email","test@tecnobingo.no"],["firstName","Test"],["lastName","One"],["password","[FILTERED]"],["phone","90909090"]],"authorization":"tec-t-ca001.osl.basefarm.net:000-813793a81974dab06b9095a820691135"}]}'
139
+ assert_equal expected, @client.send(:password_filter, s)
140
+ end
141
+ end
142
+
143
+ end
@@ -0,0 +1,96 @@
1
+ require 'test_helper'
2
+
3
+ class FoundationApi::Model::AttributeMethodsTest < ActiveSupport::TestCase
4
+ class Model
5
+ include FoundationApi::Model::AttributeMethods
6
+
7
+ alias_attribute :ip_subnet, :IPSubnet
8
+
9
+ attr_accessor :attributes
10
+
11
+ def initialize(attrs)
12
+ @attributes = HashWithIndifferentAccess.new attrs
13
+ end
14
+
15
+ end
16
+
17
+ context 'class method' do
18
+ context 'alias_attribute' do
19
+ should 'populate attribute_aliases' do
20
+ assert_equal ({'ip_subnet' => 'IPSubnet'}), Model.attribute_aliases
21
+ end
22
+ end
23
+ context 'map_attribute' do
24
+ should 'map attribute if known' do
25
+ assert_equal 'IPSubnet', Model.send(:map_attribute, :ip_subnet)
26
+ end
27
+ should 'return definition if unknown' do
28
+ assert_equal 'gurba', Model.send(:map_attribute, :gurba)
29
+ end
30
+ end
31
+ context 'reverse_map_attribute' do
32
+ should 'map attribute if known' do
33
+ assert_equal 'ip_subnet', Model.send(:reverse_map_attribute, :IPSubnet)
34
+ end
35
+ should 'return definition if unknown' do
36
+ assert_equal 'gurba', Model.send(:reverse_map_attribute, :gurba)
37
+ end
38
+ end
39
+ context 'translate_attributes' do
40
+ should 'return translated hash' do
41
+ expected = HashWithIndifferentAccess.new 'IPSubnet' => '123.234.56.0/24', 'melon' => :office
42
+ assert_equal expected, Model.send(:translate_attributes, :ip_subnet => '123.234.56.0/24', :melon => :office)
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'instance method' do
48
+ setup do
49
+ @model = Model.new(:boolean => true, :string => 'string', 'IPSubnet' => 'subnet')
50
+ end
51
+ context 'matching method' do
52
+ should 'return specified attribute if attribute exists' do
53
+ assert_equal :IPSubnet, @model.send(:matching_attribute, :IPSubnet)
54
+ end
55
+ should 'map attribute unless attribute exists' do
56
+ assert_equal 'IPSubnet', @model.send(:matching_attribute, :ip_subnet)
57
+ end
58
+ should 'return nil if attribute does not exist' do
59
+ assert_nil @model.send(:matching_attribute, :foo)
60
+ end
61
+ end
62
+ context 'accessing attributes' do
63
+ should 'give access through [] operator' do
64
+ assert_equal 'string', @model[:string]
65
+ assert_equal 'subnet', @model[:ip_subnet]
66
+ @model[:mood] = 'smiley'
67
+ assert_equal 'smiley', @model.mood
68
+ @model[:ip_subnet] = 'ugh'
69
+ assert_equal 'ugh', @model.ip_subnet
70
+ @model[:IPSubnet] = 'argh'
71
+ assert_equal 'argh', @model.ip_subnet
72
+ end
73
+ end
74
+ context 'method missing' do
75
+ should 'handle boolean methods' do
76
+ assert @model.boolean?
77
+ end
78
+ should 'handle assignments' do
79
+ assert !(@model.boolean = false)
80
+ assert !@model.boolean
81
+ end
82
+ should 'handle anythin else :p' do
83
+ assert_equal 'subnet', @model.ip_subnet
84
+ end
85
+ should 'allow to use unmapped attribute' do
86
+ assert_equal 'subnet', @model.IPSubnet
87
+ end
88
+ should 'raise exception if bad attribute name' do
89
+ assert_raises NoMethodError do
90
+ @model.bad
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ end