require 'spec_helper' @@last_pid = 0 describe ActiveFedora::Base do it_behaves_like "An ActiveModel" describe 'descendants' do it "should record the decendants" do ActiveFedora::Base.descendants.should include(ModsArticle, SpecialThing) end end describe "sharding" do it "should have a shard_index" do ActiveFedora::Base.shard_index(@this_pid).should == 0 end context "When the repository is NOT sharded" do subject {ActiveFedora::Base.connection_for_pid('test:bar')} before(:each) do ActiveFedora.config.stub(:sharded?).and_return(false) ActiveFedora::Base.fedora_connection = {} ActiveFedora.config.stub(:credentials).and_return(:url=>'myfedora') end it { should be_kind_of Rubydora::Repository} it "should be the standard connection" do subject.client.url.should == 'myfedora' end describe "assign_pid" do it "should use fedora to generate pids" do # TODO: This juggling of Fedora credentials & establishing connections should be handled by an establish_fedora_connection method, # possibly wrap it all into a fedora_connection method - MZ 06-05-2012 stubfedora = double("Fedora") stubfedora.should_receive(:connection).and_return(double("Connection", :mint =>"sample:newpid")) # Should use ActiveFedora.config.credentials as a single hash rather than an array of shards ActiveFedora::RubydoraConnection.should_receive(:new).with(ActiveFedora.config.credentials).and_return(stubfedora) ActiveFedora::Base.assign_pid(ActiveFedora::Base.new.inner_object) end end describe "shard_index" do it "should always return zero (the first and only connection)" do ActiveFedora::Base.shard_index('test:bar').should == 0 end end end context "When the repository is sharded" do before :each do ActiveFedora.config.stub(:sharded?).and_return(true) ActiveFedora::Base.fedora_connection = {} ActiveFedora.config.stub(:credentials).and_return([{:url=>'shard1'}, {:url=>'shard2'} ]) end describe "assign_pid" do it "should always use the first shard to generate pids" do stubhard1 = double("Shard") stubhard2 = double("Shard") stubhard1.should_receive(:connection).and_return(double("Connection", :mint =>"sample:newpid")) stubhard2.should_receive(:connection).never ActiveFedora::Base.fedora_connection = {0 => stubhard1, 1 => stubhard2} ActiveFedora::Base.assign_pid(ActiveFedora::Base.new.inner_object) end end describe "shard_index" do it "should use modulo of md5 of the pid to distribute objects across shards" do ActiveFedora::Base.shard_index('test:bar').should == 0 ActiveFedora::Base.shard_index('test:nanana').should == 1 end end describe "the repository" do describe "for test:bar" do subject {ActiveFedora::Base.connection_for_pid('test:bar')} it "should be shard1" do subject.client.url.should == 'shard1' end end describe "for test:baz" do subject {ActiveFedora::Base.connection_for_pid('test:nanana')} it "should be shard1" do subject.client.url.should == 'shard2' end end end end end describe "reindex_everything" do it "should call update_index on every object except for the fedora-system objects" do Rubydora::Repository.any_instance.should_receive(:search). and_yield(double(pid:'XXX')).and_yield(double(pid:'YYY')).and_yield(double(pid:'ZZZ')). and_yield(double(pid:'fedora-system:ServiceDeployment-3.0')). and_yield(double(pid:'fedora-system:ServiceDefinition-3.0')). and_yield(double(pid:'fedora-system:FedoraObject-3.0')) mock_update = double(:mock_obj) mock_update.should_receive(:update_index).exactly(3).times ActiveFedora::Base.should_receive(:find).with('XXX').and_return(mock_update) ActiveFedora::Base.should_receive(:find).with('YYY').and_return(mock_update) ActiveFedora::Base.should_receive(:find).with('ZZZ').and_return(mock_update) ActiveFedora::Base.reindex_everything end it "should accept a query param for the search" do query_string = "pid~*" Rubydora::Repository.any_instance.should_receive(:search).with(query_string). and_yield(double(pid:'XXX')).and_yield(double(pid:'YYY')).and_yield(double(pid:'ZZZ')) mock_update = double(:mock_obj) mock_update.should_receive(:update_index).exactly(3).times ActiveFedora::Base.should_receive(:find).with('XXX').and_return(mock_update) ActiveFedora::Base.should_receive(:find).with('YYY').and_return(mock_update) ActiveFedora::Base.should_receive(:find).with('ZZZ').and_return(mock_update) ActiveFedora::Base.reindex_everything(query_string) end end describe "With a test class" do before :all do class FooHistory < ActiveFedora::Base has_metadata :type=>ActiveFedora::SimpleDatastream, :name=>"someData", :autocreate => true do |m| m.field "fubar", :string m.field "swank", :text end has_metadata :type=>ActiveFedora::SimpleDatastream, :name=>"withText", :autocreate => true do |m| m.field "fubar", :text end has_metadata :type=>ActiveFedora::SimpleDatastream, :name=>"withText2", :label=>"withLabel", :autocreate => true do |m| m.field "fubar", :text end has_attributes :fubar, datastream: 'withText', multiple: true has_attributes :swank, datastream: 'someData', multiple: true end class FooAdaptation < ActiveFedora::Base has_metadata :type=>ActiveFedora::OmDatastream, :name=>'someData' end class FooInherited < FooHistory end end after :all do Object.send(:remove_const, :FooHistory) Object.send(:remove_const, :FooAdaptation) Object.send(:remove_const, :FooInherited) end def increment_pid @@last_pid += 1 end before(:each) do @this_pid = increment_pid.to_s stub_get(@this_pid) Rubydora::Repository.any_instance.stub(:client).and_return(@mock_client) ActiveFedora::Base.stub(:assign_pid).and_return(@this_pid) @test_object = ActiveFedora::Base.new end after(:each) do begin ActiveFedora::SolrService.stub(:instance) #@test_object.delete rescue end end describe '#new' do it "should create an inner object" do # for doing AFObject.new(params[:foo]) when nothing is in params[:foo] Rubydora::DigitalObject.any_instance.should_receive(:save).never result = ActiveFedora::Base.new(nil) result.inner_object.should be_kind_of(ActiveFedora::UnsavedDigitalObject) end it "should not save or get an pid on init" do Rubydora::DigitalObject.any_instance.should_receive(:save).never ActiveFedora::Base.should_receive(:assign_pid).never f = FooHistory.new end it "should be able to create with a custom pid" do f = FooHistory.new(:pid=>'numbnuts:1') f.pid.should == 'numbnuts:1' end end describe ".datastream_class_for_name" do it "should return the specifed class" do FooAdaptation.datastream_class_for_name('someData').should == ActiveFedora::OmDatastream end it "should return the specifed class" do FooAdaptation.datastream_class_for_name('content').should == ActiveFedora::Datastream end end describe ".internal_uri" do it "should return pid as fedors uri" do @test_object.internal_uri.should eql("info:fedora/#{@test_object.pid}") end end ### Methods for ActiveModel::Conversions it "should have to_param once it's saved" do @test_object.to_param.should be_nil @test_object.inner_object.stub(new_record?: false, pid: 'foo:123') @test_object.to_param.should == 'foo:123' end it "should have to_key once it's saved" do @test_object.to_key.should be_nil @test_object.inner_object.stub(new_record?: false, pid: 'foo:123') @test_object.to_key.should == ['foo:123'] end it "should have to_model when it's saved" do @test_object.to_model.should be @test_object end ### end ActiveModel::Conversions ### Methods for ActiveModel::Naming it "Should know the model_name" do FooHistory.model_name.should == 'FooHistory' FooHistory.model_name.human.should == 'Foo history' end ### End ActiveModel::Naming describe ".datastreams" do let(:test_history) { FooHistory.new } it "should create accessors for datastreams declared with has_metadata" do test_history.withText.should == test_history.datastreams['withText'] end describe "dynamic accessors" do before do test_history.add_datastream(ds) test_history.class.build_datastream_accessor(ds.dsid) end describe "when the datastream is named with dash" do let(:ds) {double('datastream', :dsid=>'eac-cpf')} it "should convert dashes to underscores" do test_history.eac_cpf.should == ds end end describe "when the datastream is named with underscore" do let (:ds) { double('datastream', :dsid=>'foo_bar') } it "should preserve the underscore" do test_history.foo_bar.should == ds end end end end it 'should provide #find' do ActiveFedora::Base.should respond_to(:find) end it "should provide .create_date" do @test_object.should respond_to(:create_date) end it "should provide .modified_date" do @test_object.should respond_to(:modified_date) end it 'should respond to .rels_ext' do @test_object.should respond_to(:rels_ext) end describe '.rels_ext' do it 'should return the RelsExtDatastream object from the datastreams array' do @test_object.stub(:datastreams => {"RELS-EXT" => "foo"}) @test_object.rels_ext.should == "foo" end end it 'should provide #add_relationship' do @test_object.should respond_to(:add_relationship) end describe '#add_relationship' do it 'should call #add_relationship on the rels_ext datastream' do @test_object.add_relationship("predicate", "info:fedora/object") pred = ActiveFedora::Predicates.vocabularies["info:fedora/fedora-system:def/relations-external#"]["predicate"] @test_object.relationships.should have_statement(RDF::Statement.new(RDF::URI.new(@test_object.internal_uri), pred, RDF::URI.new("info:fedora/object"))) end it "should update the RELS-EXT datastream and set the datastream as dirty when relationships are added" do mock_ds = double("Rels-Ext") mock_ds.stub(:content_will_change!) @test_object.datastreams["RELS-EXT"] = mock_ds @test_object.add_relationship(:is_member_of, "info:fedora/demo:5") @test_object.add_relationship(:is_member_of, "info:fedora/demo:10") end it 'should add a relationship to an object only if it does not exist already' do next_pid = increment_pid.to_s ActiveFedora::Base.stub(:assign_pid).and_return(next_pid) stub_get(next_pid) @test_object3 = ActiveFedora::Base.new @test_object.add_relationship(:has_part,@test_object3) @test_object.ids_for_outbound(:has_part).should == [@test_object3.pid] #try adding again and make sure not there twice @test_object.add_relationship(:has_part,@test_object3) @test_object.ids_for_outbound(:has_part).should == [@test_object3.pid] end it 'should add literal relationships if requested' do @test_object.add_relationship(:conforms_to,"AnInterface",true) @test_object.ids_for_outbound(:conforms_to).should == ["AnInterface"] end end it 'should provide #remove_relationship' do @test_object.should respond_to(:remove_relationship) end describe '#remove_relationship' do it 'should remove a relationship from the relationships hash' do @test_object3 = ActiveFedora::Base.new() @test_object3.stub(:pid=>'7') @test_object4 = ActiveFedora::Base.new() @test_object4.stub(:pid=>'8') @test_object.add_relationship(:has_part,@test_object3) @test_object.add_relationship(:has_part,@test_object4) #check both are there @test_object.ids_for_outbound(:has_part).should == [@test_object3.pid,@test_object4.pid] @test_object.remove_relationship(:has_part,@test_object3) #check only one item removed @test_object.ids_for_outbound(:has_part).should == [@test_object4.pid] @test_object.remove_relationship(:has_part,@test_object4) #check last item removed and predicate removed since now emtpy @test_object.relationships.size.should == 0 end end it 'should provide #relationships' do @test_object.should respond_to(:relationships) end describe '#relationships' do it 'should return a graph' do @test_object.relationships.kind_of?(RDF::Graph).should be_true @test_object.relationships.size.should == 0 end end describe '.assert_content_model' do it "should default to the name of the class" do stub_get(@this_pid) stub_add_ds(@this_pid, ['RELS-EXT']) @test_object.assert_content_model @test_object.relationships(:has_model).should == ["info:fedora/afmodel:ActiveFedora_Base"] end end describe '.save' do it "should create a new record" do @test_object.stub(:new_record? => true) @test_object.should_receive(:assign_pid) @test_object.should_receive(:serialize_datastreams) @test_object.inner_object.should_receive(:save) @test_object.should_receive(:update_index) @test_object.save end it "should update an existing record" do @test_object.stub(:new_record? => false) @test_object.should_receive(:serialize_datastreams) @test_object.inner_object.should_receive(:save) @test_object.should_receive(:update_index) @test_object.save end end describe "#create" do it "should build a new record and save it" do obj = double() obj.should_receive(:save) FooHistory.should_receive(:new).and_return(obj) @hist = FooHistory.create(:fubar=>'ta', :swank=>'da') end end describe ".adapt_to" do it "should return an adapted object of the requested type" do @test_object = FooHistory.new() @test_object.adapt_to(FooAdaptation).class.should == FooAdaptation end it "should not make an additional call to fedora to create the adapted object" do @test_object = FooHistory.new() adapted = @test_object.adapt_to(FooAdaptation) end it "should propagate new datastreams to the adapted object" do @test_object = FooHistory.new() @test_object.add_file_datastream("XXX", :dsid=>'MY_DSID') adapted = @test_object.adapt_to(FooAdaptation) adapted.datastreams.keys.should include 'MY_DSID' adapted.datastreams['MY_DSID'].content.should == "XXX" adapted.datastreams['MY_DSID'].changed?.should be_true end it "should propagate modified datastreams to the adapted object" do @test_object = FooHistory.new() orig_ds = @test_object.datastreams['someData'] orig_ds.content="" adapted = @test_object.adapt_to(FooAdaptation) adapted.datastreams.keys.should include 'someData' adapted.datastreams['someData'].should == orig_ds adapted.datastreams['someData'].content.strip.should == "" adapted.datastreams['someData'].changed?.should be_true end it "should use the datastream definitions from the adapted object" do @test_object = FooHistory.new() adapted = @test_object.adapt_to(FooAdaptation) adapted.datastreams.keys.should include 'someData' adapted.datastreams['someData'].class.should == ActiveFedora::OmDatastream end end describe ".adapt_to_cmodel with implemented (non-ActiveFedora::Base) cmodel" do subject { FooHistory.new } it "should not cast to a random first cmodel if it has a specific cmodel already" do ActiveFedora::ContentModel.should_receive(:known_models_for).with(subject).and_return([FooAdaptation]) subject.adapt_to_cmodel.should be_kind_of FooHistory end it "should cast to an inherited model over a random one" do ActiveFedora::ContentModel.should_receive(:known_models_for).with(subject).and_return([FooAdaptation, FooInherited]) subject.adapt_to_cmodel.should be_kind_of FooInherited end it "should not cast when a cmodel is same as the class" do ActiveFedora::ContentModel.should_receive(:known_models_for).with(subject).and_return([FooHistory]) subject.adapt_to_cmodel.should === subject end end describe ".adapt_to_cmodel with ActiveFedora::Base" do subject { ActiveFedora::Base.new } it "should cast to the first cmodel if ActiveFedora::Base (or no specified cmodel)" do ActiveFedora::ContentModel.should_receive(:known_models_for).with(subject).and_return([FooAdaptation, FooHistory]) subject.adapt_to_cmodel.should be_kind_of FooAdaptation end end describe ".to_solr" do it "should provide .to_solr" do @test_object.should respond_to(:to_solr) end it "should add pid, system_create_date, system_modified_date and object_state from object attributes" do @test_object.should_receive(:create_date).and_return("2012-03-04T03:12:02Z") @test_object.should_receive(:modified_date).and_return("2012-03-07T03:12:02Z") @test_object.stub(pid: 'changeme:123') @test_object.state = "D" solr_doc = @test_object.to_solr solr_doc[ActiveFedora::SolrService.solr_name("system_create", :stored_sortable, type: :date)].should eql("2012-03-04T03:12:02Z") solr_doc[ActiveFedora::SolrService.solr_name("system_modified", :stored_sortable, type: :date)].should eql("2012-03-07T03:12:02Z") solr_doc[ActiveFedora::SolrService.solr_name("object_state", :stored_sortable)].should eql("D") solr_doc[:id].should eql("changeme:123") end it "should omit base metadata and RELS-EXT if :model_only==true" do @test_object.add_relationship(:has_part, "foo", true) solr_doc = @test_object.to_solr(Hash.new, :model_only => true) solr_doc[ActiveFedora::SolrService.solr_name("system_create", type: :date)].should be_nil solr_doc[ActiveFedora::SolrService.solr_name("system_modified", type: :date)].should be_nil solr_doc["id"].should be_nil solr_doc[ActiveFedora::SolrService.solr_name("has_part", :symbol)].should be_nil end it "should add self.class as the :active_fedora_model" do stub_get(@this_pid) stub_get_content(@this_pid, ['RELS-EXT', 'someData', 'withText2', 'withText']) @test_history = FooHistory.new() solr_doc = @test_history.to_solr solr_doc[ActiveFedora::SolrService.solr_name("active_fedora_model", :stored_sortable)].should eql("FooHistory") end it "should call .to_solr on all SimpleDatastreams and OmDatastreams, passing the resulting document to solr" do mock1 = double("ds1", :to_solr => {}) mock2 = double("ds2", :to_solr => {}) ngds = double("ngds", :to_solr => {}) ngds.should_receive(:solrize_profile) mock1.should_receive(:solrize_profile) mock2.should_receive(:solrize_profile) @test_object.should_receive(:datastreams).twice.and_return({:ds1 => mock1, :ds2 => mock2, :ngds => ngds}) @test_object.should_receive(:solrize_relationships) @test_object.to_solr end it "should call .to_solr on all RDFDatastreams, passing the resulting document to solr" do mock = double("ds1", :to_solr => {}) mock.should_receive(:solrize_profile) @test_object.should_receive(:datastreams).twice.and_return({:ds1 => mock}) @test_object.should_receive(:solrize_relationships) @test_object.to_solr end it "should call .to_solr on the relationships rels-ext is dirty" do @test_object.add_relationship(:has_collection_member, "info:fedora/test:member") rels_ext = @test_object.rels_ext rels_ext.should be_changed @test_object.should_receive(:solrize_relationships) @test_object.to_solr end end describe ".label" do it "should return the label of the inner object" do @test_object.inner_object.should_receive(:label).and_return("foo label") @test_object.label.should == "foo label" end end describe ".label=" do it "should set the label of the inner object" do @test_object.label.should_not == "foo label" @test_object.label = "foo label" @test_object.label.should == "foo label" end end describe "update_attributes" do it "should set the attributes and save" do m = FooHistory.new att= {"fubar"=> '1234', "baz" =>'stuff'} m.should_receive(:fubar=).with('1234') m.should_receive(:baz=).with('stuff') m.should_receive(:save) m.update_attributes(att) end end describe "update" do it "should set the attributes and save" do m = FooHistory.new att= {"fubar"=> '1234', "baz" =>'stuff'} m.should_receive(:fubar=).with('1234') m.should_receive(:baz=).with('stuff') m.should_receive(:save) m.update(att) end end describe ".solrize_relationships" do it "should serialize the relationships into a Hash" do graph = RDF::Graph.new subject = RDF::URI.new "info:fedora/test:sample_pid" graph.insert RDF::Statement.new(subject, ActiveFedora::Predicates.find_graph_predicate(:is_member_of), RDF::URI.new('info:fedora/demo:10')) graph.insert RDF::Statement.new(subject, ActiveFedora::Predicates.find_graph_predicate(:is_part_of), RDF::URI.new('info:fedora/demo:11')) graph.insert RDF::Statement.new(subject, ActiveFedora::Predicates.find_graph_predicate(:has_part), RDF::URI.new('info:fedora/demo:12')) graph.insert RDF::Statement.new(subject, ActiveFedora::Predicates.find_graph_predicate(:conforms_to), "AnInterface") @test_object.should_receive(:relationships).and_return(graph) solr_doc = @test_object.solrize_relationships solr_doc[ActiveFedora::SolrService.solr_name("is_member_of", :symbol)].should == "info:fedora/demo:10" solr_doc[ActiveFedora::SolrService.solr_name("is_part_of", :symbol)].should == "info:fedora/demo:11" solr_doc[ActiveFedora::SolrService.solr_name("has_part", :symbol)].should == "info:fedora/demo:12" solr_doc[ActiveFedora::SolrService.solr_name("conforms_to", :symbol)].should == "AnInterface" end end end end