amee 2.6.0 → 2.7.0

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 (46) hide show
  1. data/Gemfile +18 -0
  2. data/README +2 -2
  3. data/Rakefile +31 -0
  4. data/VERSION +1 -0
  5. data/amee-ruby.gemspec +147 -0
  6. data/examples/view_profile_item.rb +32 -0
  7. data/lib/amee.rb +0 -1
  8. data/lib/amee/profile_item.rb +27 -6
  9. data/spec/amee_spec.rb +25 -0
  10. data/spec/cache_spec.rb +152 -0
  11. data/spec/connection_spec.rb +295 -0
  12. data/spec/data_category_spec.rb +259 -0
  13. data/spec/data_item_spec.rb +224 -0
  14. data/spec/data_item_value_history_spec.rb +311 -0
  15. data/spec/data_item_value_spec.rb +231 -0
  16. data/spec/data_object_spec.rb +9 -0
  17. data/spec/drill_down_spec.rb +163 -0
  18. data/spec/fixtures/AD63A83B4D41.json +1 -0
  19. data/spec/fixtures/AD63A83B4D41.xml +1 -0
  20. data/spec/fixtures/create_item.json +1 -0
  21. data/spec/fixtures/create_item.xml +1 -0
  22. data/spec/fixtures/data.json +1 -0
  23. data/spec/fixtures/data.xml +1 -0
  24. data/spec/fixtures/data_home_energy_quantity.xml +146 -0
  25. data/spec/fixtures/data_home_energy_quantity_biodiesel.xml +177 -0
  26. data/spec/fixtures/data_transport_car_generic_drill_fuel_diesel.xml +33 -0
  27. data/spec/fixtures/empty.json +1 -0
  28. data/spec/fixtures/empty.xml +1 -0
  29. data/spec/fixtures/parse_test.xml +22 -0
  30. data/spec/fixtures/v0_data_transport_transport_drill_transportType_Car1.xml +20 -0
  31. data/spec/item_definition_spec.rb +313 -0
  32. data/spec/item_value_definition_spec.rb +253 -0
  33. data/spec/logger_spec.rb +16 -0
  34. data/spec/object_spec.rb +44 -0
  35. data/spec/parse_helper_spec.rb +72 -0
  36. data/spec/profile_category_spec.rb +565 -0
  37. data/spec/profile_item_spec.rb +451 -0
  38. data/spec/profile_item_value_spec.rb +196 -0
  39. data/spec/profile_object_spec.rb +24 -0
  40. data/spec/profile_spec.rb +88 -0
  41. data/spec/rails_spec.rb +48 -0
  42. data/spec/spec.opts +2 -0
  43. data/spec/spec_helper.rb +56 -0
  44. data/spec/user_spec.rb +249 -0
  45. metadata +189 -55
  46. data/lib/amee/version.rb +0 -10
@@ -0,0 +1,224 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe AMEE::Data::Item do
4
+
5
+ before(:each) do
6
+ @item = AMEE::Data::Item.new
7
+ end
8
+
9
+ it "should have common AMEE object properties" do
10
+ @item.is_a?(AMEE::Data::Object).should be_true
11
+ end
12
+
13
+ it "should have values" do
14
+ @item.should respond_to(:values)
15
+ end
16
+
17
+ it "should have values" do
18
+ @item.should respond_to(:choices)
19
+ end
20
+
21
+ it "should a label" do
22
+ @item.should respond_to(:label)
23
+ end
24
+
25
+ it "should initialize AMEE::Object data on creation" do
26
+ uid = 'ABCD1234'
27
+ @item = AMEE::Data::Item.new(:uid => uid)
28
+ @item.uid.should == uid
29
+ end
30
+
31
+ it "can be created with hash of data" do
32
+ values = ["one", "two"]
33
+ choices = [{:name => "one", :value => "two"}]
34
+ label = "test"
35
+ @item = AMEE::Data::Item.new(:label => label, :values => values, :choices => choices)
36
+ @item.values.should == values
37
+ @item.choices.should == choices
38
+ @item.label.should == label
39
+ end
40
+
41
+ end
42
+
43
+ describe AMEE::Data::Item, "with an authenticated connection" do
44
+
45
+ it "should parse XML correctly" do
46
+ connection = flexmock "connection"
47
+ connection.should_receive(:retries).and_return(0)
48
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.xml')))
49
+ @data = AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")
50
+ @data.uid.should == "AD63A83B4D41"
51
+ @data.path.should == "/transport/plane/generic/AD63A83B4D41"
52
+ @data.full_path.should == "/data/transport/plane/generic/AD63A83B4D41"
53
+ @data.category_uid.should == "FBA97B70DBDF"
54
+ @data.created.should == DateTime.new(2007,8,1,9,00,41)
55
+ @data.modified.should == DateTime.new(2007,8,1,9,00,41)
56
+ @data.label.should == "domestic"
57
+ @data.item_definition_uid.should == "441BF4BEA15B"
58
+ @data.values.size.should == 5
59
+ @data.values[0][:name].should == "kgCO2 Per Passenger Journey"
60
+ @data.values[0][:path].should == "kgCO2PerPassengerJourney"
61
+ @data.values[0][:value].should == "0"
62
+ @data.values[0][:uid].should == "127612FA4921"
63
+ end
64
+
65
+ it "should parse choices correctly from XML" do
66
+ connection = flexmock "connection"
67
+ connection.should_receive(:retries).and_return(0)
68
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.xml')))
69
+ @data = AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")
70
+ @data.choices.size.should == 6
71
+ @data.choices[0][:name].should == "distanceKmPerYear"
72
+ @data.choices[0][:value].should be_empty
73
+ @data.choices[1][:name].should == "journeysPerYear"
74
+ @data.choices[1][:value].should be_empty
75
+ @data.choices[2][:name].should == "lat1"
76
+ @data.choices[2][:value].should == "-999"
77
+ @data.choices[3][:name].should == "lat2"
78
+ @data.choices[3][:value].should == "-999"
79
+ @data.choices[4][:name].should == "long1"
80
+ @data.choices[4][:value].should == "-999"
81
+ @data.choices[5][:name].should == "long2"
82
+ @data.choices[5][:value].should == "-999"
83
+ end
84
+
85
+ it "should parse JSON correctly" do
86
+ connection = flexmock "connection"
87
+ connection.should_receive(:retries).and_return(0)
88
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json')))
89
+ @data = AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")
90
+ @data.uid.should == "AD63A83B4D41"
91
+ @data.path.should == "/transport/plane/generic/AD63A83B4D41"
92
+ @data.full_path.should == "/data/transport/plane/generic/AD63A83B4D41"
93
+ @data.category_uid.should == "FBA97B70DBDF"
94
+ @data.created.should == DateTime.new(2007,8,1,9,00,41)
95
+ @data.modified.should == DateTime.new(2007,8,1,9,00,41)
96
+ @data.label.should == "domestic"
97
+ @data.values.size.should == 5
98
+ @data.values[0][:name].should == "kgCO2 Per Passenger Journey"
99
+ @data.values[0][:path].should == "kgCO2PerPassengerJourney"
100
+ @data.values[0][:value].should == "0"
101
+ @data.values[0][:uid].should == "127612FA4921"
102
+ end
103
+
104
+ it "should parse choices correctly from JSON" do
105
+ connection = flexmock "connection"
106
+ connection.should_receive(:retries).and_return(0)
107
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json')))
108
+ @data = AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")
109
+ @data.choices.size.should == 6
110
+ @data.choices[0][:name].should == "distanceKmPerYear"
111
+ @data.choices[0][:value].should be_empty
112
+ @data.choices[1][:name].should == "journeysPerYear"
113
+ @data.choices[1][:value].should be_empty
114
+ @data.choices[2][:name].should == "lat1"
115
+ @data.choices[2][:value].should == "-999"
116
+ @data.choices[3][:name].should == "lat2"
117
+ @data.choices[3][:value].should == "-999"
118
+ @data.choices[4][:name].should == "long1"
119
+ @data.choices[4][:value].should == "-999"
120
+ @data.choices[5][:name].should == "long2"
121
+ @data.choices[5][:value].should == "-999"
122
+ end
123
+
124
+ it "should fail gracefully with bad XML" do
125
+ connection = flexmock "connection"
126
+ connection.should_receive(:retries).and_return(0)
127
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.xml').first(12)))
128
+ connection.should_receive(:expire).with("/data/transport/plane/generic/AD63A83B4D41").once
129
+ lambda{AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")}.should raise_error(REXML::ParseException)
130
+ end
131
+
132
+ it "should retry if bad XML is received first time" do
133
+ connection = flexmock "connection"
134
+ connection.should_receive(:retries).and_return(2)
135
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.xml').first(12))).twice
136
+ connection.should_receive(:expire).with("/data/transport/plane/generic/AD63A83B4D41").twice
137
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.xml'))).once
138
+ lambda{AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")}.should_not raise_error
139
+ end
140
+
141
+ it "should fail gracefully with bad data in XML" do
142
+ connection = flexmock "connection"
143
+ connection.should_receive(:retries).and_return(0)
144
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('empty.xml')))
145
+ connection.should_receive(:expire).with("/data/transport/plane/generic/AD63A83B4D41").once
146
+ lambda{AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")}.should raise_error(AMEE::BadData)
147
+ end
148
+
149
+ it "should fail gracefully with bad JSON" do
150
+ connection = flexmock "connection"
151
+ connection.should_receive(:retries).and_return(0)
152
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json').first(12)))
153
+ connection.should_receive(:expire).with("/data/transport/plane/generic/AD63A83B4D41").once
154
+ lambda{AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")}.should raise_error(JSON::ParserError)
155
+ end
156
+
157
+ it "should retry if bad JSON is received first time" do
158
+ connection = flexmock "connection"
159
+ connection.should_receive(:retries).and_return(2)
160
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json').first(12))).twice
161
+ connection.should_receive(:expire).with("/data/transport/plane/generic/AD63A83B4D41").twice
162
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json'))).once
163
+ lambda{AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")}.should_not raise_error
164
+ end
165
+
166
+ it "should fail gracefully with bad data in JSON" do
167
+ connection = flexmock "connection"
168
+ connection.should_receive(:retries).and_return(0)
169
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('empty.json')))
170
+ connection.should_receive(:expire).with("/data/transport/plane/generic/AD63A83B4D41").once
171
+ lambda{AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")}.should raise_error(AMEE::BadData)
172
+ end
173
+
174
+ it "should fail gracefully on other errors" do
175
+ connection = flexmock "connection"
176
+ connection.should_receive(:retries).and_return(0)
177
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_raise(Timeout::Error)
178
+ lambda{AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")}.should raise_error(Timeout::Error)
179
+ end
180
+
181
+ end
182
+
183
+ describe "with sensible data" do
184
+
185
+ it "allows client to get a value by name" do
186
+ connection = flexmock "connection"
187
+ connection.should_receive(:retries).and_return(0)
188
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json')))
189
+ @data = AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")
190
+ @data.value("kgCO2 Per Passenger Km").should_not be_nil
191
+ @data.value("Source").should_not be_nil
192
+ end
193
+
194
+ it "allows client to get a value by path" do
195
+ connection = flexmock "connection"
196
+ connection.should_receive(:retries).and_return(0)
197
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json')))
198
+ @data = AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")
199
+ @data.value("kgCO2PerPassengerKm").should_not be_nil
200
+ @data.value("source").should_not be_nil
201
+ end
202
+
203
+ it "allows update" do
204
+ connection = flexmock "connection"
205
+ connection.should_receive(:retries).and_return(0)
206
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json')))
207
+ connection.should_receive(:put).with("/data/transport/plane/generic/AD63A83B4D41", :kgCO2PerPassengerKm => 0.159).and_return(flexmock(:body => '{"amountPerMonth":0,"userValueChoices":{"choices":[{"value":"","name":"distanceKmPerYear"},{"value":"","name":"journeysPerYear"},{"value":"-999","name":"lat1"},{"value":"-999","name":"lat2"},{"value":"-999","name":"long1"},{"value":"-999","name":"long2"}],"name":"userValueChoices"},"path":"/transport/plane/generic/AD63A83B4D41","dataItem":{"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","itemDefinition":{"uid":"441BF4BEA15B"},"itemValues":[{"value":"0","uid":"127612FA4921","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}},{"value":"0.159","uid":"7F27A5707101","path":"kgCO2PerPassengerKm","name":"kgCO2 Per Passenger Km","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"996AE5477B3F","name":"kgCO2PerKm"},"uid":"D7B4340D9404","path":"kgCO2PerPassengerKm","name":"kgCO2 Per Passenger Km"}},{"value":"-","uid":"FF50EC918A8E","path":"size","name":"Size","itemValueDefinition":{"valueDefinition":{"valueType":"TEXT","uid":"CCEB59CACE1B","name":"text"},"uid":"5D7FB5F552A5","path":"size","name":"Size"}},{"value":"domestic","uid":"FDD62D27AA15","path":"type","name":"Type","itemValueDefinition":{"valueDefinition":{"valueType":"TEXT","uid":"CCEB59CACE1B","name":"text"},"uid":"C376560CB19F","path":"type","name":"Type"}},{"value":"DfT INAS Division, 29 March 2007","uid":"9BE08FBEC54E","path":"source","name":"Source","itemValueDefinition":{"valueDefinition":{"valueType":"TEXT","uid":"CCEB59CACE1B","name":"text"},"uid":"0F0592F05AAC","path":"source","name":"Source"}}],"label":"domestic","dataCategory":{"uid":"FBA97B70DBDF","path":"generic","name":"Generic"},"uid":"AD63A83B4D41","environment":{"uid":"5F5887BCF726"},"path":"","name":"AD63A83B4D41"}}'))
208
+ @data = AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")
209
+ @data.value("kgCO2PerPassengerKm").should == "0.158"
210
+ @data.update(:kgCO2PerPassengerKm => 0.159)
211
+ #@data.value("kgCO2PerPassengerKm").should == "0.159"
212
+ end
213
+
214
+ it "fails gracefully if update fails" do
215
+ connection = flexmock "connection"
216
+ connection.should_receive(:retries).and_return(0)
217
+ connection.should_receive(:get).with("/data/transport/plane/generic/AD63A83B4D41", {}).and_return(flexmock(:body => fixture('AD63A83B4D41.json')))
218
+ connection.should_receive(:put).with("/data/transport/plane/generic/AD63A83B4D41", :kgCO2PerPassengerKm => 0.159).and_raise("generic error")
219
+ @data = AMEE::Data::Item.get(connection, "/data/transport/plane/generic/AD63A83B4D41")
220
+ lambda{@data.update(:kgCO2PerPassengerKm => 0.159)}.should raise_error(AMEE::BadData)
221
+ end
222
+
223
+
224
+ end
@@ -0,0 +1,311 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ TestSeriesOne=[[AMEE::Epoch,1],[AMEE::Epoch+1,2],[AMEE::Epoch+3,4]]
4
+ TestSeriesTwo=[[AMEE::Epoch,2],[AMEE::Epoch+1,6],[AMEE::Epoch+5,7],[AMEE::Epoch+9,11]]
5
+
6
+ MockResourcePath="/data/transport/plane/generic/AD63A83B4D41/kgCO2PerPassengerJourney"
7
+ MockDataItemPath="/data/transport/plane/generic/AD63A83B4D41"
8
+ MockResourceXML='<Resources><DataItemValueResource><ItemValues>'+
9
+ '<ItemValue Created="2007-08-01 09:00:41.0" Modified="2007-08-01 09:00:41.0" uid="127612FA4921"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><StartDate>'+AMEE::Epoch.xmlschema+'</StartDate><Value>1</Value><ItemValueDefinition uid="653828811D42"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><FromProfile>false</FromProfile><FromData>true</FromData><ValueDefinition uid="8CB8A1789CD6"><Name>kgCO2PerJourney</Name><ValueType>DOUBLE</ValueType></ValueDefinition></ItemValueDefinition><DataItem uid="AD63A83B4D41"/></ItemValue><DataItem uid="AD63A83B4D41"/>'+
10
+ '<ItemValue Created="2007-08-01 09:00:41.0" Modified="2007-08-01 09:00:41.0" uid="127612FA4922"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><StartDate>'+(AMEE::Epoch+1).xmlschema+'</StartDate><Value>2</Value><ItemValueDefinition uid="653828811D42"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><FromProfile>false</FromProfile><FromData>true</FromData><ValueDefinition uid="8CB8A1789CD6"><Name>kgCO2PerJourney</Name><ValueType>DOUBLE</ValueType></ValueDefinition></ItemValueDefinition><DataItem uid="AD63A83B4D41"/></ItemValue><DataItem uid="AD63A83B4D41"/>'+
11
+ '<ItemValue Created="2007-08-01 09:00:41.0" Modified="2007-08-01 09:00:41.0" uid="127612FA4923"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><StartDate>'+(AMEE::Epoch+3).xmlschema+'</StartDate><Value>4</Value><ItemValueDefinition uid="653828811D42"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><FromProfile>false</FromProfile><FromData>true</FromData><ValueDefinition uid="8CB8A1789CD6"><Name>kgCO2PerJourney</Name><ValueType>DOUBLE</ValueType></ValueDefinition></ItemValueDefinition><DataItem uid="AD63A83B4D41"/></ItemValue><DataItem uid="AD63A83B4D41"/>'+
12
+ '</ItemValues></DataItemValueResource></Resources>'
13
+ MockResourceJSON='{"dataItem":{"uid":"AD63A83B4D41"},"itemValues":['+
14
+ ' {"item":{"uid":"AD63A83B4D41"},"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","startDate":"'+AMEE::Epoch.xmlschema+'","value":"1","uid":"127612FA4921","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}}'+
15
+ ',{"item":{"uid":"AD63A83B4D41"},"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","startDate":"'+(AMEE::Epoch+1).xmlschema+'","value":"2","uid":"127612FA4922","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}}'+
16
+ ',{"item":{"uid":"AD63A83B4D41"},"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","startDate":"'+(AMEE::Epoch+3).xmlschema+'","value":"4","uid":"127612FA4923","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}}'+
17
+ ']}'
18
+ MockResourceXMLTwo='<Resources><DataItemValueResource><ItemValues>'+
19
+ '<ItemValue Created="2007-08-01 09:00:41.0" Modified="2007-08-01 09:00:41.0" uid="127612FA4921"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><StartDate>'+AMEE::Epoch.xmlschema+'</StartDate><Value>1</Value><ItemValueDefinition uid="653828811D42"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><FromProfile>false</FromProfile><FromData>true</FromData><ValueDefinition uid="8CB8A1789CD6"><Name>kgCO2PerJourney</Name><ValueType>DOUBLE</ValueType></ValueDefinition></ItemValueDefinition><DataItem uid="AD63A83B4D41"/></ItemValue><DataItem uid="AD63A83B4D41"/>'+
20
+ '<ItemValue Created="2007-08-01 09:00:41.0" Modified="2007-08-01 09:00:41.0" uid="127612FA4922"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><StartDate>'+(AMEE::Epoch+1).xmlschema+'</StartDate><Value>6</Value><ItemValueDefinition uid="653828811D42"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><FromProfile>false</FromProfile><FromData>true</FromData><ValueDefinition uid="8CB8A1789CD6"><Name>kgCO2PerJourney</Name><ValueType>DOUBLE</ValueType></ValueDefinition></ItemValueDefinition><DataItem uid="AD63A83B4D41"/></ItemValue><DataItem uid="AD63A83B4D41"/>'+
21
+ '<ItemValue Created="2007-08-01 09:00:41.0" Modified="2007-08-01 09:00:41.0" uid="127612FA4924"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><StartDate>'+(AMEE::Epoch+5).xmlschema+'</StartDate><Value>7</Value><ItemValueDefinition uid="653828811D42"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><FromProfile>false</FromProfile><FromData>true</FromData><ValueDefinition uid="8CB8A1789CD6"><Name>kgCO2PerJourney</Name><ValueType>DOUBLE</ValueType></ValueDefinition></ItemValueDefinition><DataItem uid="AD63A83B4D41"/></ItemValue><DataItem uid="AD63A83B4D41"/>'+
22
+ '<ItemValue Created="2007-08-01 09:00:41.0" Modified="2007-08-01 09:00:41.0" uid="127612FA4925"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><StartDate>'+(AMEE::Epoch+9).xmlschema+'</StartDate><Value>11</Value><ItemValueDefinition uid="653828811D42"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><FromProfile>false</FromProfile><FromData>true</FromData><ValueDefinition uid="8CB8A1789CD6"><Name>kgCO2PerJourney</Name><ValueType>DOUBLE</ValueType></ValueDefinition></ItemValueDefinition><DataItem uid="AD63A83B4D41"/></ItemValue><DataItem uid="AD63A83B4D41"/>'+
23
+ '</ItemValues></DataItemValueResource></Resources>'
24
+ MockResourceJSONTwo='{"dataItem":{"uid":"AD63A83B4D41"},"itemValues":['+
25
+ ' {"item":{"uid":"AD63A83B4D41"},"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","startDate":"'+AMEE::Epoch.xmlschema+'","value":"1","uid":"127612FA4921","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}}'+
26
+ ',{"item":{"uid":"AD63A83B4D41"},"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","startDate":"'+(AMEE::Epoch+1).xmlschema+'","value":"6","uid":"127612FA4922","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}}'+
27
+ ',{"item":{"uid":"AD63A83B4D41"},"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","startDate":"'+(AMEE::Epoch+5).xmlschema+'","value":"7","uid":"127612FA4924","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}}'+
28
+ ',{"item":{"uid":"AD63A83B4D41"},"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","startDate":"'+(AMEE::Epoch+9).xmlschema+'","value":"11","uid":"127612FA4925","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}}'+
29
+ ']}'
30
+ MockResourceXMLSingle='<Resources><DataItemValueResource><ItemValue Created="2007-08-01 09:00:41.0" Modified="2007-08-01 09:00:41.0" uid="127612FA4921"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><StartDate>'+AMEE::Epoch.xmlschema+'</StartDate><Value>1</Value><ItemValueDefinition uid="653828811D42"><Path>kgCO2PerPassengerJourney</Path><Name>kgCO2 Per Passenger Journey</Name><FromProfile>false</FromProfile><FromData>true</FromData><ValueDefinition uid="8CB8A1789CD6"><Name>kgCO2PerJourney</Name><ValueType>DOUBLE</ValueType></ValueDefinition></ItemValueDefinition><DataItem uid="AD63A83B4D41"/></ItemValue><DataItem uid="AD63A83B4D41"/></DataItemValueResource>'+
31
+ '</Resources>'
32
+ MockResourceJSONSingle='{"dataItem":{"uid":"AD63A83B4D41"},"itemValue":'+
33
+ ' {"item":{"uid":"AD63A83B4D41"},"modified":"2007-08-01 09:00:41.0","created":"2007-08-01 09:00:41.0","startDate":"'+AMEE::Epoch.xmlschema+'","value":"1","uid":"127612FA4921","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey","itemValueDefinition":{"valueDefinition":{"valueType":"DOUBLE","uid":"8CB8A1789CD6","name":"kgCO2PerJourney"},"uid":"653828811D42","path":"kgCO2PerPassengerJourney","name":"kgCO2 Per Passenger Journey"}}'+
34
+ '}'
35
+
36
+
37
+ describe AMEE::Data::ItemValueHistory do
38
+
39
+ before(:each) do
40
+ @history = AMEE::Data::ItemValueHistory.new
41
+ end
42
+
43
+ it "should NOT have common AMEE object properties" do
44
+ # we can't be an AMEE object, since we have a set of UIDs, not one UID
45
+ @history.is_a?(AMEE::Data::Object).should be_false
46
+ end
47
+
48
+ it "should have an array of ItemValue objects" do
49
+ @history.should respond_to(:values)
50
+ end
51
+
52
+ it "should be able to return a time series" do
53
+ @history.should respond_to(:series)
54
+ end
55
+
56
+ it "should have a type" do
57
+ @history.should respond_to(:type)
58
+ end
59
+
60
+ it "can be created with a type, and a time-value pairs array" do
61
+ series=TestSeriesOne
62
+ type = "TEXT"
63
+ @history = AMEE::Data::ItemValueHistory.new(:series => series, :type => type)
64
+ @history.series.should == series
65
+ @history.type.should == type
66
+ @history.values[0].is_a?(AMEE::Data::ItemValue).should be_true
67
+ @history.values[0].value.should == 1
68
+ @history.values[0].start_date.should == AMEE::Epoch
69
+ end
70
+
71
+ it "can be created by pushing to the array of item values" do
72
+ series=TestSeriesOne
73
+ type = "TEXT"
74
+ @history = AMEE::Data::ItemValueHistory.new(:type=>type)
75
+ @history.series.should == []
76
+ @history.type.should == type
77
+ @fstvalue = AMEE::Data::ItemValue.new(:type=>type,:value=>1,:start_date=>AMEE::Epoch)
78
+ @sndvalue = AMEE::Data::ItemValue.new(:type=>type,:value=>2,:start_date=>AMEE::Epoch+1)
79
+ @trdvalue = AMEE::Data::ItemValue.new(:type=>type,:value=>4,:start_date=>AMEE::Epoch+3)
80
+ @history.values.push @fstvalue
81
+ @history.values.push @sndvalue
82
+ @history.values.push @trdvalue
83
+ @history.values[0].is_a?(AMEE::Data::ItemValue).should be_true
84
+ @history.values[0].value.should == 1
85
+ @history.values[0].start_date.should == AMEE::Epoch
86
+ @history.series.should == series
87
+ end
88
+
89
+ it "should support DOUBLE data type" do
90
+ @history = AMEE::Data::ItemValueHistory.new(:series => TestSeriesOne, :type => "DOUBLE")
91
+ @history.series.should == TestSeriesOne
92
+ end
93
+
94
+ it "should support TEXT data type" do
95
+ @history = AMEE::Data::ItemValueHistory.new(:series => TestSeriesOne, :type => "TEXT")
96
+ @history.series.should == TestSeriesOne
97
+ end
98
+
99
+ it "allows value to be changed after creation" do
100
+ series=TestSeriesOne
101
+ type = "TEXT"
102
+ @history = AMEE::Data::ItemValueHistory.new(:series => series, :type => type)
103
+ @history.series.should == series
104
+ series = TestSeriesTwo
105
+ @history.series = series
106
+ @history.series.should == series
107
+ @history.values[1].value.should == 6
108
+ end
109
+
110
+ it "allows item values to be found by time" do
111
+ series=TestSeriesTwo
112
+ type = "TEXT"
113
+ @history = AMEE::Data::ItemValueHistory.new(:series => series, :type => type)
114
+ @value=@history.value_at(AMEE::Epoch+5)
115
+ @value.value.should == 7
116
+ lambda {
117
+ @history.value_at(AMEE::Epoch+6)
118
+ }.should raise_error
119
+ @history.values_at([AMEE::Epoch+1,AMEE::Epoch+9]).length.should eql 2
120
+ end
121
+
122
+ end
123
+
124
+ describe AMEE::Data::ItemValueHistory, "when comparing to another history" do
125
+ before(:each) do
126
+ @historyone = AMEE::Data::ItemValueHistory.new(:series=>TestSeriesOne)
127
+ @historytwo = AMEE::Data::ItemValueHistory.new(:series=>TestSeriesTwo)
128
+ @comparison=@historytwo.compare(@historyone)
129
+ end
130
+
131
+ it "should be able to compare histories" do
132
+ @comparison.should be_a Hash
133
+ @comparison[:deletions].should be_a Array
134
+ @comparison[:updates].should be_a Array
135
+ @comparison[:insertions].should be_a Array
136
+ end
137
+
138
+ it "should return an array of items to update" do
139
+ # note comparison list isnt order stable so sort here for test
140
+ @changes=@comparison[:updates].sort{|x,y| x.start_date <=> y.start_date}
141
+ @changes.length.should eql 2
142
+ @changes[1].value.should eql 6
143
+ @changes[1].start_date.should eql AMEE::Epoch+1
144
+ @changes[0].start_date.should eql AMEE::Epoch
145
+ @changes[0].value.should eql 2
146
+
147
+ end
148
+ it "should return an array of items to create" do
149
+ @changes=@comparison[:insertions].sort{|x,y| x.start_date <=> y.start_date}
150
+ @changes.length.should eql 2
151
+ @changes[0].start_date.should eql AMEE::Epoch+5
152
+ @changes[1].start_date.should eql AMEE::Epoch+9
153
+ @changes[0].value.should eql 7
154
+ @changes[1].value.should eql 11
155
+ end
156
+ it "should return an array of items to delete" do
157
+ @changes=@comparison[:deletions].sort{|x,y| x.start_date <=> y.start_date}
158
+ @changes.length.should eql 1
159
+ @changes[0].start_date.should eql AMEE::Epoch+3
160
+ end
161
+
162
+ end
163
+
164
+ describe AMEE::Data::ItemValueHistory, "with an authenticated connection" do
165
+
166
+ it "should parse XML correctly" do
167
+ connection = flexmock "connection"
168
+ connection.should_receive(:get).with(MockResourcePath,:valuesPerPage=>2).
169
+ and_return(flexmock(:body => MockResourceXML))
170
+ @history = AMEE::Data::ItemValueHistory.get(connection, MockResourcePath)
171
+ @history.series.should == TestSeriesOne
172
+ @fstvalue=@history.values[0]
173
+ @sndvalue=@history.values[1]
174
+ @fstvalue.uid.should == "127612FA4921"
175
+ @sndvalue.uid.should == "127612FA4922"
176
+ @fstvalue.name.should == "kgCO2 Per Passenger Journey"
177
+ @fstvalue.full_path.should == MockResourcePath
178
+ @fstvalue.created.should == DateTime.new(2007,8,1,9,00,41)
179
+ @fstvalue.modified.should == DateTime.new(2007,8,1,9,00,41)
180
+ @fstvalue.type.should == "DOUBLE"
181
+ @history.series.should == TestSeriesOne
182
+ @history.type.should == "DOUBLE"
183
+ end
184
+
185
+ it "should parse JSON correctly" do
186
+ connection = flexmock "connection"
187
+ connection.should_receive(:get).with(MockResourcePath,:valuesPerPage=>2).and_return(flexmock(:body => MockResourceJSON))
188
+ @history = AMEE::Data::ItemValueHistory.get(connection, MockResourcePath)
189
+ @fstvalue=@history.values[0]
190
+ @sndvalue=@history.values[1]
191
+ @fstvalue.uid.should == "127612FA4921"
192
+ @sndvalue.uid.should == "127612FA4922"
193
+ @fstvalue.name.should == "kgCO2 Per Passenger Journey"
194
+ @fstvalue.full_path.should == MockResourcePath
195
+ @fstvalue.created.should == DateTime.new(2007,8,1,9,00,41)
196
+ @fstvalue.modified.should == DateTime.new(2007,8,1,9,00,41)
197
+ @fstvalue.type.should == "DOUBLE"
198
+ @history.series.should == TestSeriesOne
199
+ @history.type.should == "DOUBLE"
200
+ end
201
+
202
+ it "should parse JSON correctly if theres only one point" do
203
+ connection = flexmock "connection"
204
+ connection.should_receive(:get).with(MockResourcePath,:valuesPerPage=>2).and_return(flexmock(:body => MockResourceJSONSingle))
205
+ @history = AMEE::Data::ItemValueHistory.get(connection, MockResourcePath)
206
+ @fstvalue=@history.values[0]
207
+ @fstvalue.uid.should == "127612FA4921"
208
+ @fstvalue.name.should == "kgCO2 Per Passenger Journey"
209
+ @fstvalue.full_path.should == MockResourcePath
210
+ @fstvalue.created.should == DateTime.new(2007,8,1,9,00,41)
211
+ @fstvalue.modified.should == DateTime.new(2007,8,1,9,00,41)
212
+ @fstvalue.type.should == "DOUBLE"
213
+ @history.series.should == [[AMEE::Epoch,1]]
214
+ @history.type.should == "DOUBLE"
215
+ end
216
+
217
+ it "should parse XML correctly if theres only one point" do
218
+ connection = flexmock "connection"
219
+ connection.should_receive(:get).with(MockResourcePath,:valuesPerPage=>2).and_return(flexmock(:body => MockResourceXMLSingle))
220
+ @history = AMEE::Data::ItemValueHistory.get(connection, MockResourcePath)
221
+ @fstvalue=@history.values[0]
222
+ @fstvalue.uid.should == "127612FA4921"
223
+ @fstvalue.name.should == "kgCO2 Per Passenger Journey"
224
+ @fstvalue.full_path.should == MockResourcePath
225
+ @fstvalue.created.should == DateTime.new(2007,8,1,9,00,41)
226
+ @fstvalue.modified.should == DateTime.new(2007,8,1,9,00,41)
227
+ @fstvalue.type.should == "DOUBLE"
228
+ @history.series.should == [[AMEE::Epoch,1]]
229
+ @history.type.should == "DOUBLE"
230
+ end
231
+
232
+ it "should fail gracefully with incorrect XML data" do
233
+ connection = flexmock "connection"
234
+ xml = '<?xml version="1.0" encoding="UTF-8"?><Resources></Resources>'
235
+ connection.should_receive(:get).with("/data",:valuesPerPage=>2).and_return(flexmock(:body => xml))
236
+ lambda{AMEE::Data::ItemValueHistory.get(connection, "/data")}.should raise_error(AMEE::BadData)
237
+ end
238
+
239
+ it "should fail gracefully with incorrect JSON data" do
240
+ connection = flexmock "connection"
241
+ json = '{}'
242
+ connection.should_receive(:get).with("/data",:valuesPerPage=>2).and_return(flexmock(:body => json))
243
+ lambda{AMEE::Data::ItemValueHistory.get(connection, "/data")}.should raise_error(AMEE::BadData)
244
+ end
245
+
246
+ it "should fail gracefully on other errors" do
247
+ connection = flexmock "connection"
248
+ connection.should_receive(:get).with("/data",:valuesPerPage=>2).and_raise("unidentified error")
249
+ lambda{AMEE::Data::ItemValueHistory.get(connection, "/data")}.should raise_error(AMEE::BadData)
250
+ end
251
+
252
+ it "should fail gracefully if create series without epoch" do
253
+ @history = AMEE::Data::ItemValueHistory.new()
254
+ lambda{@history.series=[[AMEE::Epoch+1,5]]}.should raise_error(AMEE::BadData)
255
+ end
256
+
257
+ end
258
+
259
+ describe AMEE::Data::ItemValueHistory, "after loading" do
260
+
261
+ before(:each) do
262
+ @path = MockResourcePath
263
+ @connection = flexmock "connection"
264
+ @connection.should_receive(:get).with(@path,:valuesPerPage=>2).and_return(flexmock(:body => MockResourceJSON))
265
+ @val = AMEE::Data::ItemValueHistory.get(@connection, @path)
266
+ end
267
+
268
+ it "can have series changed and saved back to server" do
269
+ @connection.should_receive(:put).with(MockDataItemPath+"/127612FA4921", :value => 2).once.and_return(flexmock(:body => ''))
270
+ # note this one shouldn't include a start date as it is the epoch point
271
+ @connection.should_receive(:put).with(MockDataItemPath+"/127612FA4922", :value => 6, :startDate => AMEE::Epoch+1).once.and_return(flexmock(:body => ''))
272
+ @connection.should_receive(:delete).with(MockDataItemPath+"/127612FA4923").once.and_return(flexmock(:body => ''))
273
+ @connection.should_receive(:post).with(MockDataItemPath,
274
+ :kgCO2PerPassengerJourney => 7, :startDate => AMEE::Epoch+5).once.and_return({'Location'=>'http://foo.com/'})
275
+ @connection.should_receive(:post).with(MockDataItemPath,
276
+ :kgCO2PerPassengerJourney => 11, :startDate => AMEE::Epoch+9).once.and_return({'Location'=>'http://foo.com/'})
277
+ lambda {
278
+ @val.series = TestSeriesTwo
279
+ @val.save!
280
+ }.should_not raise_error
281
+ end
282
+
283
+ it "can have series changed and saved back to server (using SSL)" do
284
+ @connection.should_receive(:put).with(MockDataItemPath+"/127612FA4921", :value => 2).once.and_return(flexmock(:body => ''))
285
+ # note this one shouldn't include a start date as it is the epoch point
286
+ @connection.should_receive(:put).with(MockDataItemPath+"/127612FA4922", :value => 6, :startDate => AMEE::Epoch+1).once.and_return(flexmock(:body => ''))
287
+ @connection.should_receive(:delete).with(MockDataItemPath+"/127612FA4923").once.and_return(flexmock(:body => ''))
288
+ @connection.should_receive(:post).with(MockDataItemPath,
289
+ :kgCO2PerPassengerJourney => 7, :startDate => AMEE::Epoch+5).once.and_return({'Location'=>'https://foo.com/'})
290
+ @connection.should_receive(:post).with(MockDataItemPath,
291
+ :kgCO2PerPassengerJourney => 11, :startDate => AMEE::Epoch+9).once.and_return({'Location'=>'https://foo.com/'})
292
+ lambda {
293
+ @val.series = TestSeriesTwo
294
+ @val.save!
295
+ }.should_not raise_error
296
+ end
297
+
298
+ it "cannot create a new series (unsupported by platform)" do
299
+ lambda {
300
+ @val.series = TestSeriesTwo
301
+ @val.create!
302
+ }.should raise_error(AMEE::NotSupported,"Cannot create a Data Item Value History from scratch: at least one data point must exist when the DI is created")
303
+ end
304
+
305
+ it "cannot delete an entire series (unsupported by platform)" do
306
+ lambda {
307
+ @val.delete!
308
+ }.should raise_error(AMEE::NotSupported,"Cannot delete all of history: at least one data point must always exist.")
309
+ end
310
+
311
+ end