require File.dirname(__FILE__) + '/spec_helper.rb'
TestSeriesOne=[[AMEE::Epoch,1],[AMEE::Epoch+1,2],[AMEE::Epoch+3,4]]
TestSeriesTwo=[[AMEE::Epoch,2],[AMEE::Epoch+1,6],[AMEE::Epoch+5,7],[AMEE::Epoch+9,11]]
MockResourcePath="/data/transport/plane/generic/AD63A83B4D41/kgCO2PerPassengerJourney"
MockDataItemPath="/data/transport/plane/generic/AD63A83B4D41"
MockResourceXML=''+
'kgCO2PerPassengerJourneykgCO2 Per Passenger Journey'+AMEE::Epoch.xmlschema+'1kgCO2PerPassengerJourneykgCO2 Per Passenger JourneyfalsetruekgCO2PerJourneyDOUBLE'+
'kgCO2PerPassengerJourneykgCO2 Per Passenger Journey'+(AMEE::Epoch+1).xmlschema+'2kgCO2PerPassengerJourneykgCO2 Per Passenger JourneyfalsetruekgCO2PerJourneyDOUBLE'+
'kgCO2PerPassengerJourneykgCO2 Per Passenger Journey'+(AMEE::Epoch+3).xmlschema+'4kgCO2PerPassengerJourneykgCO2 Per Passenger JourneyfalsetruekgCO2PerJourneyDOUBLE'+
''
MockResourceJSON='{"dataItem":{"uid":"AD63A83B4D41"},"itemValues":['+
' {"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"}}'+
',{"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"}}'+
',{"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"}}'+
']}'
MockResourceXMLTwo=''+
'kgCO2PerPassengerJourneykgCO2 Per Passenger Journey'+AMEE::Epoch.xmlschema+'1kgCO2PerPassengerJourneykgCO2 Per Passenger JourneyfalsetruekgCO2PerJourneyDOUBLE'+
'kgCO2PerPassengerJourneykgCO2 Per Passenger Journey'+(AMEE::Epoch+1).xmlschema+'6kgCO2PerPassengerJourneykgCO2 Per Passenger JourneyfalsetruekgCO2PerJourneyDOUBLE'+
'kgCO2PerPassengerJourneykgCO2 Per Passenger Journey'+(AMEE::Epoch+5).xmlschema+'7kgCO2PerPassengerJourneykgCO2 Per Passenger JourneyfalsetruekgCO2PerJourneyDOUBLE'+
'kgCO2PerPassengerJourneykgCO2 Per Passenger Journey'+(AMEE::Epoch+9).xmlschema+'11kgCO2PerPassengerJourneykgCO2 Per Passenger JourneyfalsetruekgCO2PerJourneyDOUBLE'+
''
MockResourceJSONTwo='{"dataItem":{"uid":"AD63A83B4D41"},"itemValues":['+
' {"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"}}'+
',{"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"}}'+
',{"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"}}'+
',{"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"}}'+
']}'
MockResourceXMLSingle='kgCO2PerPassengerJourneykgCO2 Per Passenger Journey'+AMEE::Epoch.xmlschema+'1kgCO2PerPassengerJourneykgCO2 Per Passenger JourneyfalsetruekgCO2PerJourneyDOUBLE'+
''
MockResourceJSONSingle='{"dataItem":{"uid":"AD63A83B4D41"},"itemValue":'+
' {"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"}}'+
'}'
describe AMEE::Data::ItemValueHistory do
before(:each) do
@history = AMEE::Data::ItemValueHistory.new
end
it "should NOT have common AMEE object properties" do
# we can't be an AMEE object, since we have a set of UIDs, not one UID
@history.is_a?(AMEE::Data::Object).should be_false
end
it "should have an array of ItemValue objects" do
@history.should respond_to(:values)
end
it "should be able to return a time series" do
@history.should respond_to(:series)
end
it "should have a type" do
@history.should respond_to(:type)
end
it "can be created with a type, and a time-value pairs array" do
series=TestSeriesOne
type = "TEXT"
@history = AMEE::Data::ItemValueHistory.new(:series => series, :type => type)
@history.series.should == series
@history.type.should == type
@history.values[0].is_a?(AMEE::Data::ItemValue).should be_true
@history.values[0].value.should == 1
@history.values[0].start_date.should == AMEE::Epoch
end
it "can be created by pushing to the array of item values" do
series=TestSeriesOne
type = "TEXT"
@history = AMEE::Data::ItemValueHistory.new(:type=>type)
@history.series.should == []
@history.type.should == type
@fstvalue = AMEE::Data::ItemValue.new(:type=>type,:value=>1,:start_date=>AMEE::Epoch)
@sndvalue = AMEE::Data::ItemValue.new(:type=>type,:value=>2,:start_date=>AMEE::Epoch+1)
@trdvalue = AMEE::Data::ItemValue.new(:type=>type,:value=>4,:start_date=>AMEE::Epoch+3)
@history.values.push @fstvalue
@history.values.push @sndvalue
@history.values.push @trdvalue
@history.values[0].is_a?(AMEE::Data::ItemValue).should be_true
@history.values[0].value.should == 1
@history.values[0].start_date.should == AMEE::Epoch
@history.series.should == series
end
it "should support DOUBLE data type" do
@history = AMEE::Data::ItemValueHistory.new(:series => TestSeriesOne, :type => "DOUBLE")
@history.series.should == TestSeriesOne
end
it "should support TEXT data type" do
@history = AMEE::Data::ItemValueHistory.new(:series => TestSeriesOne, :type => "TEXT")
@history.series.should == TestSeriesOne
end
it "allows value to be changed after creation" do
series=TestSeriesOne
type = "TEXT"
@history = AMEE::Data::ItemValueHistory.new(:series => series, :type => type)
@history.series.should == series
series = TestSeriesTwo
@history.series = series
@history.series.should == series
@history.values[1].value.should == 6
end
it "allows item values to be found by time" do
series=TestSeriesTwo
type = "TEXT"
@history = AMEE::Data::ItemValueHistory.new(:series => series, :type => type)
@value=@history.value_at(AMEE::Epoch+5)
@value.value.should == 7
lambda {
@history.value_at(AMEE::Epoch+6)
}.should raise_error
@history.values_at([AMEE::Epoch+1,AMEE::Epoch+9]).length.should eql 2
end
end
describe AMEE::Data::ItemValueHistory, "when comparing to another history" do
before(:each) do
@historyone = AMEE::Data::ItemValueHistory.new(:series=>TestSeriesOne)
@historytwo = AMEE::Data::ItemValueHistory.new(:series=>TestSeriesTwo)
@comparison=@historytwo.compare(@historyone)
end
it "should be able to compare histories" do
@comparison.should be_a Hash
@comparison[:deletions].should be_a Array
@comparison[:updates].should be_a Array
@comparison[:insertions].should be_a Array
end
it "should return an array of items to update" do
# note comparison list isnt order stable so sort here for test
@changes=@comparison[:updates].sort{|x,y| x.start_date <=> y.start_date}
@changes.length.should eql 2
@changes[1].value.should eql 6
@changes[1].start_date.should eql AMEE::Epoch+1
@changes[0].start_date.should eql AMEE::Epoch
@changes[0].value.should eql 2
end
it "should return an array of items to create" do
@changes=@comparison[:insertions].sort{|x,y| x.start_date <=> y.start_date}
@changes.length.should eql 2
@changes[0].start_date.should eql AMEE::Epoch+5
@changes[1].start_date.should eql AMEE::Epoch+9
@changes[0].value.should eql 7
@changes[1].value.should eql 11
end
it "should return an array of items to delete" do
@changes=@comparison[:deletions].sort{|x,y| x.start_date <=> y.start_date}
@changes.length.should eql 1
@changes[0].start_date.should eql AMEE::Epoch+3
end
end
describe AMEE::Data::ItemValueHistory, "with an authenticated connection" do
it "should parse XML correctly" do
connection = flexmock "connection"
connection.should_receive(:get).with(MockResourcePath,:valuesPerPage=>2).
and_return(flexmock(:body => MockResourceXML))
@history = AMEE::Data::ItemValueHistory.get(connection, MockResourcePath)
@history.series.should == TestSeriesOne
@fstvalue=@history.values[0]
@sndvalue=@history.values[1]
@fstvalue.uid.should == "127612FA4921"
@sndvalue.uid.should == "127612FA4922"
@fstvalue.name.should == "kgCO2 Per Passenger Journey"
@fstvalue.full_path.should == MockResourcePath
@fstvalue.created.should == DateTime.new(2007,8,1,9,00,41)
@fstvalue.modified.should == DateTime.new(2007,8,1,9,00,41)
@fstvalue.type.should == "DOUBLE"
@history.series.should == TestSeriesOne
@history.type.should == "DOUBLE"
end
it "should parse JSON correctly" do
connection = flexmock "connection"
connection.should_receive(:get).with(MockResourcePath,:valuesPerPage=>2).and_return(flexmock(:body => MockResourceJSON))
@history = AMEE::Data::ItemValueHistory.get(connection, MockResourcePath)
@fstvalue=@history.values[0]
@sndvalue=@history.values[1]
@fstvalue.uid.should == "127612FA4921"
@sndvalue.uid.should == "127612FA4922"
@fstvalue.name.should == "kgCO2 Per Passenger Journey"
@fstvalue.full_path.should == MockResourcePath
@fstvalue.created.should == DateTime.new(2007,8,1,9,00,41)
@fstvalue.modified.should == DateTime.new(2007,8,1,9,00,41)
@fstvalue.type.should == "DOUBLE"
@history.series.should == TestSeriesOne
@history.type.should == "DOUBLE"
end
it "should parse JSON correctly if theres only one point" do
connection = flexmock "connection"
connection.should_receive(:get).with(MockResourcePath,:valuesPerPage=>2).and_return(flexmock(:body => MockResourceJSONSingle))
@history = AMEE::Data::ItemValueHistory.get(connection, MockResourcePath)
@fstvalue=@history.values[0]
@fstvalue.uid.should == "127612FA4921"
@fstvalue.name.should == "kgCO2 Per Passenger Journey"
@fstvalue.full_path.should == MockResourcePath
@fstvalue.created.should == DateTime.new(2007,8,1,9,00,41)
@fstvalue.modified.should == DateTime.new(2007,8,1,9,00,41)
@fstvalue.type.should == "DOUBLE"
@history.series.should == [[AMEE::Epoch,1]]
@history.type.should == "DOUBLE"
end
it "should parse XML correctly if theres only one point" do
connection = flexmock "connection"
connection.should_receive(:get).with(MockResourcePath,:valuesPerPage=>2).and_return(flexmock(:body => MockResourceXMLSingle))
@history = AMEE::Data::ItemValueHistory.get(connection, MockResourcePath)
@fstvalue=@history.values[0]
@fstvalue.uid.should == "127612FA4921"
@fstvalue.name.should == "kgCO2 Per Passenger Journey"
@fstvalue.full_path.should == MockResourcePath
@fstvalue.created.should == DateTime.new(2007,8,1,9,00,41)
@fstvalue.modified.should == DateTime.new(2007,8,1,9,00,41)
@fstvalue.type.should == "DOUBLE"
@history.series.should == [[AMEE::Epoch,1]]
@history.type.should == "DOUBLE"
end
it "should fail gracefully with incorrect XML data" do
connection = flexmock "connection"
xml = ''
connection.should_receive(:get).with("/data",:valuesPerPage=>2).and_return(flexmock(:body => xml))
lambda{AMEE::Data::ItemValueHistory.get(connection, "/data")}.should raise_error(AMEE::BadData)
end
it "should fail gracefully with incorrect JSON data" do
connection = flexmock "connection"
json = '{}'
connection.should_receive(:get).with("/data",:valuesPerPage=>2).and_return(flexmock(:body => json))
lambda{AMEE::Data::ItemValueHistory.get(connection, "/data")}.should raise_error(AMEE::BadData)
end
it "should fail gracefully on other errors" do
connection = flexmock "connection"
connection.should_receive(:get).with("/data",:valuesPerPage=>2).and_raise("unidentified error")
lambda{AMEE::Data::ItemValueHistory.get(connection, "/data")}.should raise_error(AMEE::BadData)
end
it "should fail gracefully if create series without epoch" do
@history = AMEE::Data::ItemValueHistory.new()
lambda{@history.series=[[AMEE::Epoch+1,5]]}.should raise_error(AMEE::BadData)
end
end
describe AMEE::Data::ItemValueHistory, "after loading" do
before(:each) do
@path = MockResourcePath
@connection = flexmock "connection"
@connection.should_receive(:get).with(@path,:valuesPerPage=>2).and_return(flexmock(:body => MockResourceJSON))
@val = AMEE::Data::ItemValueHistory.get(@connection, @path)
end
it "can have series changed and saved back to server" do
@connection.should_receive(:put).with(MockDataItemPath+"/127612FA4921", :value => 2).once.and_return(flexmock(:body => ''))
# note this one shouldn't include a start date as it is the epoch point
@connection.should_receive(:put).with(MockDataItemPath+"/127612FA4922", :value => 6, :startDate => AMEE::Epoch+1).once.and_return(flexmock(:body => ''))
@connection.should_receive(:delete).with(MockDataItemPath+"/127612FA4923").once.and_return(flexmock(:body => ''))
@connection.should_receive(:post).with(MockDataItemPath,
:kgCO2PerPassengerJourney => 7, :startDate => AMEE::Epoch+5).once.and_return({'Location'=>'http://foo.com/'})
@connection.should_receive(:post).with(MockDataItemPath,
:kgCO2PerPassengerJourney => 11, :startDate => AMEE::Epoch+9).once.and_return({'Location'=>'http://foo.com/'})
lambda {
@val.series = TestSeriesTwo
@val.save!
}.should_not raise_error
end
it "can have series changed and saved back to server (using SSL)" do
@connection.should_receive(:put).with(MockDataItemPath+"/127612FA4921", :value => 2).once.and_return(flexmock(:body => ''))
# note this one shouldn't include a start date as it is the epoch point
@connection.should_receive(:put).with(MockDataItemPath+"/127612FA4922", :value => 6, :startDate => AMEE::Epoch+1).once.and_return(flexmock(:body => ''))
@connection.should_receive(:delete).with(MockDataItemPath+"/127612FA4923").once.and_return(flexmock(:body => ''))
@connection.should_receive(:post).with(MockDataItemPath,
:kgCO2PerPassengerJourney => 7, :startDate => AMEE::Epoch+5).once.and_return({'Location'=>'https://foo.com/'})
@connection.should_receive(:post).with(MockDataItemPath,
:kgCO2PerPassengerJourney => 11, :startDate => AMEE::Epoch+9).once.and_return({'Location'=>'https://foo.com/'})
lambda {
@val.series = TestSeriesTwo
@val.save!
}.should_not raise_error
end
it "cannot create a new series (unsupported by platform)" do
lambda {
@val.series = TestSeriesTwo
@val.create!
}.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")
end
it "cannot delete an entire series (unsupported by platform)" do
lambda {
@val.delete!
}.should raise_error(AMEE::NotSupported,"Cannot delete all of history: at least one data point must always exist.")
end
end