foundationapi 0.9.9

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.
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