Skip to content

Commit

Permalink
Adding #match_fedora_datastream rspec matcher
Browse files Browse the repository at this point in the history
Given that ActiveFedora is an adaptor for CRUD operations on Fedora, I
want to provide Rspec matchers that can greatly assist in validating
that data written to Fedora is written how you are expecting it to be
written.

Adding have_predicate matcher

Adding additional specs for have_predicate

Adding #has_many_associated_active_fedora_objects

Adding rspec matchers for belongs_to

Updating bits of documentation for rspec helper

Finalizing method for including rspec_matcher
  • Loading branch information
jeremyf committed Feb 25, 2013
1 parent 0ef74cf commit a92a719
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 0 deletions.
2 changes: 2 additions & 0 deletions active-fedora.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Gem::Specification.new do |s|
s.add_development_dependency("jettywrapper", ">=1.2.0")
s.add_development_dependency("rspec", ">= 2.9.0")
s.add_development_dependency("equivalent-xml")
s.add_development_dependency("rest-client")
s.add_development_dependency("webmock")

s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
Expand Down
8 changes: 8 additions & 0 deletions lib/active_fedora/rspec_matchers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module ActiveFedora
# in ./spec/spec_helper.rb
# ``` require 'active_fedora/rspec_matchers' ```
module RspecMatchers
end
end
pattern = Dir.glob(File.join(File.dirname(__FILE__), 'rspec_matchers/*_matcher.rb'))
pattern.each { |f| require f }
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# RSpec matcher to spec delegations.
RSpec::Matchers.define :belong_to_associated_active_fedora_object do |association_name|
match do |subject|
@association_name = association_name
if @association_name.nil? || @expected_object.nil?
raise(
ArgumentError,
"subject.should belong_to_associated_active_fedora_object(<association_name>).with_object(<object>)"
)
end

@subject = subject.class.find(subject.pid)
@actual_object = @subject.send(@association_name)

@expected_object == @actual_object
end

chain(:with_object) { |object| @expected_object = object }


description do
"#{@subject.class} PID=#{@subject.pid} association: #{@association_name.inspect} matches ActiveFedora"
end

failure_message_for_should do |text|
"expected #{@subject.class} PID=#{@subject.pid} association: #{@association_name.inspect} to match"
end

failure_message_for_should_not do |text|
"expected #{@subject.class} PID=#{@subject.pid} association: #{@association_name.inspect} to NOT match"
end

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# RSpec matcher to spec delegations.

RSpec::Matchers.define :have_many_associated_active_fedora_objects do |association_name|
match do |subject|
@association_name = association_name
if @association_name.nil? || !@expected_objects.respond_to?(:count)
raise(
ArgumentError,
"subject.should have_many_associated_active_fedora_objects(<association_name>).with_objects(<objects[]>)"
)
end

@subject = subject.class.find(subject.pid)
@actual_objects = @subject.send(@association_name)

if @expected_objects
actual_count = @actual_objects.count
expected_count = @expected_objects.count
if actual_count != expected_count
raise(
RSpec::Expectations::ExpectationNotMetError,
"#{@subject.class} PID=#{@subject.pid} relationship: #{@association_name.inspect} count <Expected Count: #{expected_count}> <Actual: #{actual_count}>"
)
end
intersection = @actual_objects & @expected_objects
intersection.count == @expected_objects.count
end
end

chain(:with_objects) { |objects| @expected_objects = objects }


description do
"#{@subject.class} PID=#{@subject.pid} association: #{@association_name.inspect} matches ActiveFedora"
end

failure_message_for_should do |text|
"expected #{@subject.class} PID=#{@subject.pid} association: #{@association_name.inspect} to match"
end

failure_message_for_should_not do |text|
"expected #{@subject.class} PID=#{@subject.pid} association: #{@association_name.inspect} to NOT match"
end

end
47 changes: 47 additions & 0 deletions lib/active_fedora/rspec_matchers/have_predicate_matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# RSpec matcher to spec delegations.

RSpec::Matchers.define :have_predicate do |predicate|
match do |subject|
@predicate = predicate
if @predicate.nil? || !@expected_objects.respond_to?(:count)
raise(
ArgumentError,
"subject.should have_predicate(<predicate>).with_objects(<objects[]>)"
)
end
@subject = subject.class.find(subject.pid)
@actual_objects = @subject.relationships(predicate)

if @expected_objects
actual_count = @actual_objects.count
expected_count = @expected_objects.count
if actual_count != expected_count
raise(
RSpec::Expectations::ExpectationNotMetError,
"#{@subject.class} PID=#{@subject.pid} relationship: #{@predicate.inspect} count <Expected Count: #{expected_count}> <Actual: #{actual_count}>"
)
end
intersection = @actual_objects.collect do |ao|
internal_uri = ao.respond_to?(:internal_uri) ? ao.internal_uri : ao
end & @expected_objects

intersection.count == @expected_objects.count
end
end

chain(:with_objects) { |objects| @expected_objects = objects }


description do
"#{@subject.class} PID=#{@subject.pid} relationship: #{@predicate.inspect} matches Fedora"
end

failure_message_for_should do |text|
"expected #{@subject.class} PID=#{@subject.pid} relationship: #{@predicate.inspect} to match"
end

failure_message_for_should_not do |text|
"expected #{@subject.class} PID=#{@subject.pid} relationship: #{@predicate.inspect} to NOT match"
end

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# RSpec matcher to spec delegations.

RSpec::Matchers.define :match_fedora_datastream do |method|
match do |object|
@method = method
@object = object
if @expected_xml.nil?
raise(
ArgumentError,
"match_fedora_datastream(<datastream_name>).with(<expected_xml>)"
)
end
expected = Nokogiri::XML(@expected_xml)

base_url = ActiveFedora.config.credentials[:url]
@fedora_datastream_url = File.join(
base_url, 'objects', @object.pid.to_s,'datastreams', @method, 'content'
)

response = RestClient.get(@fedora_datastream_url)

actual = Nokogiri::XML(response.body)

EquivalentXml.equivalent?(expected, actual, :normalize_whitespace => true)
end

chain(:with) { |expected_xml| @expected_xml = expected_xml }

description do
"#{@object.class} PID=#{@object.pid} datastream: #{@method.inspect} matches Fedora"
end

failure_message_for_should do |text|
"expected #{@object.class} PID=#{@object.pid} datastream: #{@method.inspect} to match Fedora"
end

failure_message_for_should_not do |text|
"expected #{@object.class} PID=#{@object.pid} datastream: #{@method.inspect} to NOT match Fedora"
end

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require "spec_helper"
require 'ostruct'
require 'webmock/rspec'
require 'lib/active_fedora/rspec_matchers/belong_to_associated_active_fedora_object_matcher'

describe RSpec::Matchers, "belong_to_associated_active_fedora_object_matcher" do
subject { OpenStruct.new(:pid => pid )}
let(:pid) { 123 }
let(:object1) { Object.new }
let(:object2) { Object.new }
let(:association) { :association }

before(:each) do
subject.class.should_receive(:find).with(pid).and_return(subject)
end

it 'should match when association is properly stored in fedora' do
subject.should_receive(association).and_return(object1)
subject.should belong_to_associated_active_fedora_object(association).with_object(object1)
end

it 'should not match when association is different' do
subject.should_receive(association).and_return(object1)
lambda {
subject.should belong_to_associated_active_fedora_object(association).with_object(object2)
}.should (
raise_error(
RSpec::Expectations::ExpectationNotMetError,
/expected #{subject.class} PID=#{pid} association: #{association.inspect}/
)
)
end

it 'should require :with_object option' do
lambda {
subject.should belong_to_associated_active_fedora_object(association)
}.should(
raise_error(
ArgumentError,
"subject.should belong_to_associated_active_fedora_object(<association_name>).with_object(<object>)"
)
)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require "spec_helper"
require 'ostruct'
require 'webmock/rspec'
require 'active_fedora/rspec_matchers/have_many_associated_active_fedora_objects_matcher'

describe RSpec::Matchers, "have_many_associated_active_fedora_objects_matcher" do
subject { OpenStruct.new(:pid => pid )}
let(:pid) { 123 }
let(:object1) { Object.new }
let(:object2) { Object.new }
let(:object3) { Object.new }
let(:association) { :association }

before(:each) do
subject.class.should_receive(:find).with(pid).and_return(subject)
end

it 'should match when association is properly stored in fedora' do
subject.should_receive(association).and_return([object1,object2])
subject.should have_many_associated_active_fedora_objects(association).with_objects([object1, object2])
end

it 'should not match when association is different' do
subject.should_receive(association).and_return([object1,object3])
lambda {
subject.should have_many_associated_active_fedora_objects(association).with_objects([object1, object2])
}.should (
raise_error(
RSpec::Expectations::ExpectationNotMetError,
/expected #{subject.class} PID=#{pid} association: #{association.inspect}/
)
)
end

it 'should require :with_objects option' do
lambda {
subject.should have_many_associated_active_fedora_objects(association)
}.should(
raise_error(
ArgumentError,
"subject.should have_many_associated_active_fedora_objects(<association_name>).with_objects(<objects[]>)"
)
)
end
end
45 changes: 45 additions & 0 deletions spec/unit/rspec_matchers/have_predicate_matcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require "spec_helper"
require 'ostruct'
require 'webmock/rspec'
require 'active_fedora/rspec_matchers/have_predicate_matcher'

describe RSpec::Matchers, "have_predicate_matcher" do
subject { OpenStruct.new(:pid => pid )}
let(:pid) { 123 }
let(:object1) { Object.new }
let(:object2) { Object.new }
let(:object3) { Object.new }
let(:predicate) { :predicate }

before(:each) do
subject.class.should_receive(:find).with(pid).and_return(subject)
end

it 'should match when relationship is "what we have in Fedora"' do
subject.should_receive(:relationships).with(predicate).and_return([object1,object2])
subject.should have_predicate(predicate).with_objects([object1, object2])
end

it 'should not match when relationship is different' do
subject.should_receive(:relationships).with(predicate).and_return([object1,object3])
lambda {
subject.should have_predicate(predicate).with_objects([object1, object2])
}.should (
raise_error(
RSpec::Expectations::ExpectationNotMetError,
/expected #{subject.class} PID=#{pid} relationship: #{predicate.inspect}/
)
)
end

it 'should require :with_objects option' do
lambda {
subject.should have_predicate(predicate)
}.should(
raise_error(
ArgumentError,
"subject.should have_predicate(<predicate>).with_objects(<objects[]>)"
)
)
end
end
43 changes: 43 additions & 0 deletions spec/unit/rspec_matchers/match_fedora_datastream_matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require "spec_helper"
require 'ostruct'
require 'webmock/rspec'
require 'active_fedora/rspec_matchers/match_fedora_datastream_matcher'

describe RSpec::Matchers, "match_fedora_datastream" do
let(:pid) { 123 }
let(:expected_xml) { '<xml><node>Value</node></xml>' }
let(:datastream_name) { 'metadata' }
let(:datastream_url) {
File.join(ActiveFedora.config.credentials[:url], 'objects', pid.to_s,'datastreams', datastream_name, 'content')
}
subject { OpenStruct.new(:pid => pid )}

it 'should match based on request' do
stub_request(:get, datastream_url).to_return(:body => expected_xml, :status => 200)
subject.should match_fedora_datastream(datastream_name).with(expected_xml)
end

it 'should handle non-matching requests' do
stub_request(:get, datastream_url).to_return(:body => "<parent>#{expected_xml}</parent>", :status => 200)
lambda {
subject.should match_fedora_datastream(datastream_name).with(expected_xml)
}.should(
raise_error(
RSpec::Expectations::ExpectationNotMetError,
/expected #{subject.class} PID=#{pid} datastream: #{datastream_name.inspect} to match Fedora/
)
)
end

it 'should require :with option' do
stub_request(:get, datastream_url).to_return(:body => "<parent>#{expected_xml}</parent>", :status => 200)
lambda {
subject.should match_fedora_datastream(datastream_name)
}.should(
raise_error(
ArgumentError,
"match_fedora_datastream(<datastream_name>).with(<expected_xml>)"
)
)
end
end

0 comments on commit a92a719

Please sign in to comment.