* Add coverage for daemon 'store_resource'
 * Rename metastore's 'merge' function to 'update' to avoid the implication that it works
   the same as Ruby's Hash#merge.
4 files changed, 109 insertions(+), 8 deletions(-)

M lib/thingfish/daemon.rb
M lib/thingfish/metastore.rb
M spec/daemon_spec.rb
M spec/metastore_spec.rb
M lib/thingfish/daemon.rb +5 -2
@@ 177,11 177,13 @@ class ThingFish::Daemon < Mongrel::HttpS
 		new_resource = uuid.nil?
 		uuid ||= UUID.timestamp_create
 
+		# :BRANCH: Deletes new resources if there's an error while storing
+		# :BRANCH: Doesn't delete it if it's an update
 		# Store the data, catching and cleaning up on error
 		begin
 			checksum = @filestore.store_io( uuid, body )
 		rescue => err
-			self.log.notice "Error saving resource to filestore."
+			self.log.error "Error saving resource to filestore: %s" % [ err.message ]
 			@filestore.delete( uuid ) if new_resource
 			raise
 		end

          
@@ 189,8 191,9 @@ class ThingFish::Daemon < Mongrel::HttpS
 		# Store some default metadata about the resource
 		now = Time.now
 		metadata = @metastore[ uuid ]
-		metadata.merge( body_metadata )
+		metadata.update( body_metadata )
 
+		# :BRANCH: Won't overwrite an existing creation date
 		metadata.checksum = checksum
 		metadata.created  = now unless metadata.created
 		metadata.modified = now

          
M lib/thingfish/metastore.rb +4 -3
@@ 72,13 72,14 @@ class ThingFish::MetaStore
 		end
 
 
-		### Merge the values from the given +hash+ into the metadata for the proxied
-		### resource.
-		def merge( hash )
+		### Adds the contents of +hash+ to the values for the referenced UUID, overwriting entries
+		### with duplicate keys.
+		def update( hash )
 			hash.each_pair do |key, value|
 				@store.set_property( @uuid, key.to_s, value )
 			end
 		end
+		alias_method :merge!, :update
 		
 
 		### Returns true if the +other+ object is equivalent to the receiver

          
M spec/daemon_spec.rb +99 -2
@@ 454,9 454,106 @@ describe ThingFish::Daemon do
 	
 	### Resource storing
 	
-	it "knows how to store a new resource"
-	it "knows how to update the data and metadata for an existing resource"
+	it "knows how to store a new resource" do
+		UUID.stub!( :timestamp_create ).and_return( TEST_UUID_OBJ )
+		body = mock( "body IO" )
+		metadata = mock( "metadata hash", :null_object => true )
+
+		# Replace the filestore and metastore with mocks
+		filestore = mock( "filestore", :null_object => true )
+		@daemon.instance_variable_set( :@filestore, filestore )
+		metastore = mock( "metastore", :null_object => true )
+		@daemon.instance_variable_set( :@metastore, metastore )
+
+		filestore.should_receive( :store_io ).
+			with( TEST_UUID_OBJ, body ).
+			and_return( :a_checksum )
+
+		metadata_proxy = mock( "metadata proxy", :null_object => true )
+		metastore.should_receive( :[] ).with( TEST_UUID_OBJ ).and_return( metadata_proxy )
+		metadata_proxy.should_receive( :update ).with( metadata )
+
+		metadata_proxy.should_receive( :checksum= ).with( :a_checksum )
+		metadata_proxy.should_receive( :created ).and_return( nil )
+		metadata_proxy.should_receive( :created= ).with an_instance_of( Time )
+		metadata_proxy.should_receive( :modified= ).with( an_instance_of(Time) )
+
+		metadata_proxy.stub!( :content_length ).and_return( 1 )
+
+		@daemon.store_resource( body, metadata ).should == TEST_UUID_OBJ
+	end
+	
 	
+	it "knows how to update the data and metadata for an existing resource" do
+		body = mock( "body IO" )
+		metadata = mock( "metadata hash", :null_object => true )
+
+		# Replace the filestore and metastore with mocks
+		filestore = mock( "filestore", :null_object => true )
+		@daemon.instance_variable_set( :@filestore, filestore )
+		metastore = mock( "metastore", :null_object => true )
+		@daemon.instance_variable_set( :@metastore, metastore )
+
+		filestore.should_receive( :store_io ).
+			with( TEST_UUID_OBJ, body ).
+			and_return( :a_checksum )
+
+		metadata_proxy = mock( "metadata proxy", :null_object => true )
+		metastore.should_receive( :[] ).with( TEST_UUID_OBJ ).and_return( metadata_proxy )
+		metadata_proxy.should_receive( :update ).with( metadata )
+
+		metadata_proxy.should_receive( :checksum= ).with( :a_checksum )
+		metadata_proxy.should_receive( :created ).and_return( Time.now )
+		metadata_proxy.should_not_receive( :created= )
+		metadata_proxy.should_receive( :modified= ).with( an_instance_of(Time) )
+
+		metadata_proxy.stub!( :content_length ).and_return( 1 )
+
+		@daemon.store_resource( body, metadata, TEST_UUID_OBJ ).should == TEST_UUID_OBJ
+	end
+	
+	
+	it "cleans up new resources if there's an error while storing" do
+		UUID.stub!( :timestamp_create ).and_return( TEST_UUID_OBJ )
+		body = mock( "body IO" )
+		metadata = mock( "metadata hash", :null_object => true )
+
+		# Replace the filestore and metastore with mocks
+		filestore = mock( "filestore", :null_object => true )
+		@daemon.instance_variable_set( :@filestore, filestore )
+		metastore = mock( "metastore", :null_object => true )
+		@daemon.instance_variable_set( :@metastore, metastore )
+
+		filestore.should_receive( :store_io ).
+			with( TEST_UUID_OBJ, body ).
+			and_raise( RuntimeError.new('Happy time explosion!') )
+		filestore.should_receive( :delete ).with( TEST_UUID_OBJ )
+		
+		lambda {
+			@daemon.store_resource( body, metadata )
+		}.should raise_error( RuntimeError, 'Happy time explosion!' )
+	end
+	
+	
+	it "does not delete the resource if there's an error while updating" do
+		body = mock( "body IO" )
+		metadata = mock( "metadata hash", :null_object => true )
+
+		# Replace the filestore and metastore with mocks
+		filestore = mock( "filestore", :null_object => true )
+		@daemon.instance_variable_set( :@filestore, filestore )
+		metastore = mock( "metastore", :null_object => true )
+		@daemon.instance_variable_set( :@metastore, metastore )
+
+		filestore.should_receive( :store_io ).
+			with( TEST_UUID_OBJ, body ).
+			and_raise( RuntimeError.new('Happy time explosion!') )
+		filestore.should_not_receive( :delete ).with( TEST_UUID_OBJ )
+		
+		lambda {
+			@daemon.store_resource( body, metadata, TEST_UUID_OBJ )
+		}.should raise_error( RuntimeError, 'Happy time explosion!' )
+	end
 end
 
 

          
M spec/metastore_spec.rb +1 -1
@@ 138,7 138,7 @@ describe ThingFish::MetaStore::ResourceP
 	it "can merge new data from a hash" do
 		@store.should_receive( :set_property ).with( TEST_UUID, 'format', TEST_PROPVALUE )
 		@store.should_receive( :set_property ).with( TEST_UUID, 'owner', 'perqualee' )
-		@proxy.merge( :format => TEST_PROPVALUE, :owner => 'perqualee' )
+		@proxy.update( :format => TEST_PROPVALUE, :owner => 'perqualee' )
 	end
 
 end