qa-ldf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rubocop.yml +21 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +9 -0
  6. data/CONTRIBUTING.md +109 -0
  7. data/Gemfile +18 -0
  8. data/Guardfile +27 -0
  9. data/LICENSE +14 -0
  10. data/README.md +42 -0
  11. data/Rakefile +30 -0
  12. data/VERSION +1 -0
  13. data/config/ldf.yml +15 -0
  14. data/lib/qa/ldf.rb +43 -0
  15. data/lib/qa/ldf/authorities.rb +1 -0
  16. data/lib/qa/ldf/authorities/lc_names.rb +31 -0
  17. data/lib/qa/ldf/authority.rb +104 -0
  18. data/lib/qa/ldf/client.rb +39 -0
  19. data/lib/qa/ldf/configuration.rb +67 -0
  20. data/lib/qa/ldf/empty_search_service.rb +18 -0
  21. data/lib/qa/ldf/json_mapper.rb +35 -0
  22. data/lib/qa/ldf/model.rb +49 -0
  23. data/lib/qa/ldf/spec.rb +3 -0
  24. data/lib/qa/ldf/spec/authority.rb +112 -0
  25. data/lib/qa/ldf/spec/model.rb +9 -0
  26. data/lib/qa/ldf/spec/search_service.rb +30 -0
  27. data/lib/qa/ldf/version.rb +34 -0
  28. data/qa-ldf.gemspec +35 -0
  29. data/spec/contracts/qa_loc_as_search_service_spec.rb +38 -0
  30. data/spec/qa/ldf/authorities/lc_names_spec.rb +52 -0
  31. data/spec/qa/ldf/authority_spec.rb +29 -0
  32. data/spec/qa/ldf/client_spec.rb +23 -0
  33. data/spec/qa/ldf/configuration_spec.rb +93 -0
  34. data/spec/qa/ldf/empty_search_service_spec.rb +11 -0
  35. data/spec/qa/ldf/json_mapper_spec.rb +22 -0
  36. data/spec/qa/ldf/model_spec.rb +53 -0
  37. data/spec/qa/ldf_spec.rb +43 -0
  38. data/spec/spec_helper.rb +18 -0
  39. data/spec/support/cache_server.rb +25 -0
  40. data/spec/support/fake_client.rb +31 -0
  41. data/spec/support/fake_search_service.rb +40 -0
  42. data/spec/support/shared_examples/ld_cache_client.rb +35 -0
  43. metadata +232 -0
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'qa/authorities/loc'
3
+
4
+ # We need to guarantee `Loc.subauthority_for('names')` behaves like a search
5
+ # service
6
+ describe Qa::Authorities::Loc do
7
+ subject(:search_service) { described_class.subauthority_for('names') }
8
+ before do
9
+ # an empty response for 'tove'
10
+ stub_request(:get, 'http://id.loc.gov/search/?format=json&'\
11
+ 'q=tove&q=cs:http://id.loc.gov/authorities/names')
12
+ .with(headers: { 'Accept' => 'application/json' })
13
+ .to_return(status: 200, body: '[]', headers: {})
14
+
15
+ # real responses from fixtures
16
+ search_config.each do |search|
17
+ stub_request(:get, 'http://id.loc.gov/search/?format=json' \
18
+ "&q=#{search[:query]}&q=cs:" \
19
+ 'http://id.loc.gov/authorities/names')
20
+ .with(headers: { 'Accept' => 'application/json' })
21
+ .to_return(status: 200, body: search[:body], headers: {})
22
+ end
23
+ end
24
+
25
+ let(:search_config) do
26
+ [
27
+ { query: 'moomin', body: '[]', result: [] }, # an empty body
28
+ ]
29
+ end
30
+
31
+ let(:searches) do
32
+ search_config.each_with_object({}) do |search, hsh|
33
+ hsh[search[:query]] = search[:result]
34
+ end
35
+ end
36
+
37
+ it_behaves_like 'an ldf search service'
38
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::LCNames do
4
+ it_behaves_like 'an ldf authority'
5
+
6
+ subject(:authority) do
7
+ auth = described_class.new
8
+
9
+ auth.client = FakeClient.new do |client|
10
+ client.graph = RDF::Graph.new
11
+ client.graph.insert(*statements)
12
+ client.label = ldf_label
13
+ end
14
+
15
+ auth
16
+ end
17
+
18
+ let(:ldf_label) { 'Marble Island (Nunavut)' }
19
+ let(:ldf_uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
20
+
21
+ let(:statements) do
22
+ [RDF::Statement(RDF::URI(ldf_uri),
23
+ RDF::Vocab::SKOS.prefLabel,
24
+ RDF::Literal(ldf_label))]
25
+ end
26
+
27
+ before do
28
+ # mock empty responses for all queries;
29
+ # see spec/contracts/qa_loc_as_search_service.rb for lc search service tests
30
+ stub_request(:get, 'http://id.loc.gov/search/?format=json&' \
31
+ 'q=cs:http://id.loc.gov/authorities/names')
32
+ .with(headers: { 'Accept' => 'application/json' })
33
+ .to_return(status: 200, body: '[]', headers: {})
34
+ end
35
+
36
+ describe '#search_service' do
37
+ it 'hits the upstream loc endpoint by default' do
38
+ query = 'a query'
39
+ url = 'http://id.loc.gov/search/?format=json' \
40
+ "&q=#{query}&q=cs:http://id.loc.gov/authorities/names"
41
+ expect(a_request(:get, url))
42
+
43
+ authority.search_service.search(query)
44
+ end
45
+ end
46
+
47
+ describe '#dataset' do
48
+ it 'defaults to :lcnames' do
49
+ expect(described_class.new.dataset).to eq :lcnames
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::Authority do
4
+ it_behaves_like 'an ldf authority'
5
+
6
+ subject(:authority) do
7
+ auth = described_class.new
8
+
9
+ auth.client = FakeClient.new do |client|
10
+ client.graph = RDF::Graph.new << statement
11
+ client.label = ldf_label
12
+ end
13
+
14
+ auth
15
+ end
16
+
17
+ let(:ldf_label) { 'Marble Island (Nunavut)' }
18
+ let(:ldf_uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
19
+
20
+ let(:statement) do
21
+ [RDF::URI(ldf_uri), RDF::Vocab::SKOS.prefLabel, RDF::Literal(ldf_label)]
22
+ end
23
+
24
+ describe '#dataset' do
25
+ it 'defaults an empty string symbol' do
26
+ expect(described_class.new.dataset).to eq :''
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ ##
3
+ # @todo: Stub LDF behavior at this point. Add integration/contract tests for
4
+ # this interface.
5
+ describe Qa::LDF::Client do
6
+ subject(:client) { described_class.new }
7
+
8
+ let(:uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
9
+ let(:graph_stub) { RDF::Graph.new << [RDF::URI(uri), RDF.type, RDF.Property] }
10
+
11
+ before do
12
+ # stub the external request, this behavior is owned by the server,
13
+ # we just allow it to make the request
14
+ stub_request(:get, uri)
15
+ .to_return(status: 200,
16
+ body: graph_stub.dump(:ntriples),
17
+ headers: { 'Content-Type' =>
18
+ RDF::Format.for(:ntriples).content_type })
19
+ end
20
+
21
+ include_context 'with cache server'
22
+ it_behaves_like 'an ld cache client'
23
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::Configuration do
4
+ subject(:config) { described_class.instance }
5
+
6
+ shared_context 'with configuration' do
7
+ before { config.configure!(**options) }
8
+ after { config.reset! }
9
+
10
+ let(:options) do
11
+ { option: :moomin }
12
+ end
13
+ end
14
+
15
+ describe '#[]' do
16
+ include_context 'with configuration'
17
+
18
+ it 'gives nil for unconfigured options' do
19
+ expect(config[:fake]).to be_nil
20
+ end
21
+
22
+ it 'accesses options' do
23
+ options.each { |k, v| expect(config[k]).to eq v }
24
+ end
25
+ end
26
+
27
+ describe '#configure!' do
28
+ it 'configures the options' do
29
+ expect { config.configure!(key: :value) }
30
+ .to change { config[:key] }.from(nil).to(:value)
31
+ end
32
+
33
+ it 'overrides the options' do
34
+ config.configure!(key: :value)
35
+
36
+ expect { config.configure!(key: :new_value) }
37
+ .to change { config[:key] }.from(:value).to(:new_value)
38
+ end
39
+
40
+ it 'overrides options not in new config' do
41
+ config.configure!(key: :value)
42
+
43
+ expect { config.configure!(new_key: :new_value) }
44
+ .to change { config[:key] }.from(:value).to(nil)
45
+ end
46
+
47
+ it 'yields itself' do
48
+ expect { |b| config.configure!(&b) }.to yield_with_args(config)
49
+ end
50
+
51
+ it 'yields configured self' do
52
+ options = { key1: :value, key2: :value }
53
+
54
+ config.configure!(**options) { |c| expect(c.to_h).to eq options }
55
+ end
56
+
57
+ it 'returns self' do
58
+ expect(config.configure!).to eql config
59
+ end
60
+ end
61
+
62
+ describe '#each' do
63
+ include_context 'with configuration'
64
+
65
+ it 'enumerates the options hash' do
66
+ expect(config.each).to contain_exactly(*options.each)
67
+ end
68
+ end
69
+
70
+ describe '#reset!' do
71
+ include_context 'with configuration'
72
+
73
+ it 'resets options hash' do
74
+ expect { config.reset! }.to change { config.to_a }.to be_empty
75
+ end
76
+ end
77
+
78
+ describe '#to_a' do
79
+ include_context 'with configuration'
80
+
81
+ it 'returns the configured options as an array' do
82
+ expect(config.to_a).to eq options.to_a
83
+ end
84
+ end
85
+
86
+ describe '#to_h' do
87
+ include_context 'with configuration'
88
+
89
+ it 'returns the configured options as a hash' do
90
+ expect(config.to_h).to eq options
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,11 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'qa/ldf/empty_search_service'
4
+
5
+ describe Qa::LDF::EmptySearchService do
6
+ it_behaves_like 'an ldf search service'
7
+
8
+ subject(:search_service) { described_class.new }
9
+
10
+ let(:searches) { { '-NonSensE\ !QUERY' => [], 'Bărăganul (Romania)' => [] } }
11
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::JsonMapper do
4
+ subject(:mapper) { described_class.new }
5
+
6
+ let(:uri) { 'http://example.com/moomin' }
7
+ let(:label) { RDF::Literal('Moomin') }
8
+
9
+ let(:graph) do
10
+ RDF::Graph.new << [RDF::URI(uri), RDF::Vocab::SKOS.prefLabel, label]
11
+ end
12
+
13
+ describe '#map_resource' do
14
+ it 'maps with an id' do
15
+ expect(mapper.map_resource(uri, graph)[:id]).to eq uri
16
+ end
17
+
18
+ it 'maps with a label' do
19
+ expect(mapper.map_resource(uri, graph)[:label]).to eq label
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF::Model do
4
+ define :be_term do |expected|
5
+ match { |actual| expect(actual.to_term).to eq expected }
6
+ end
7
+
8
+ let(:id) { 'http://example.com/authority/moomin' }
9
+ let(:label) { 'moomin' }
10
+
11
+ describe '.from_graph' do
12
+ let(:graph) do
13
+ graph = RDF::Graph.new
14
+ graph.insert(*statements)
15
+ graph
16
+ end
17
+
18
+ let(:statements) do
19
+ [RDF::Statement(RDF::URI(id), RDF::Vocab::SKOS.prefLabel, label),
20
+ RDF::Statement(RDF::URI(id), RDF::Vocab::DC.title, 'Moomin Papa'),
21
+ RDF::Statement(RDF::Node.new, RDF::Vocab::DC.title, 'Moomin Papa')]
22
+ end
23
+
24
+ it 'builds a model with the id as uri' do
25
+ expect(described_class.from_graph(uri: id, graph: graph))
26
+ .to be_term RDF::URI(id)
27
+ end
28
+
29
+ it 'builds a model with the label as label' do
30
+ expect(described_class.from_graph(uri: id, graph: graph))
31
+ .to have_attributes rdf_label: a_collection_containing_exactly(label)
32
+ end
33
+
34
+ it 'contains passed graph' do
35
+ expect(described_class.from_graph(uri: id, graph: graph).statements)
36
+ .to include(*statements)
37
+ end
38
+ end
39
+
40
+ describe '.from_qa_result' do
41
+ let(:json_hash) { { id: id, label: label, papa: 'moomin papa' } }
42
+
43
+ it 'builds a model with the id as uri' do
44
+ expect(described_class.from_qa_result(qa_result: json_hash))
45
+ .to be_term RDF::URI(id)
46
+ end
47
+
48
+ it 'builds a model with the label as label' do
49
+ expect(described_class.from_qa_result(qa_result: json_hash))
50
+ .to have_attributes rdf_label: a_collection_containing_exactly(label)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qa::LDF do
4
+ subject(:mod) { described_class }
5
+
6
+ it 'has a version' do
7
+ expect(mod::VERSION.to_str).to be_a String
8
+ end
9
+
10
+ describe '#version' do
11
+ it 'returns VERISON' do
12
+ expect(mod.version).to eql mod::VERSION
13
+ end
14
+ end
15
+
16
+ describe '.config' do
17
+ it 'defaults to an empty config' do
18
+ expect(mod.config).not_to be_any
19
+ end
20
+
21
+ context 'when configured' do
22
+ before { mod.configure!(**options) }
23
+
24
+ let(:options) { { opt: :value, opt2: :value2 } }
25
+
26
+ it 'returns the configuration instance' do
27
+ expect(mod.config.to_h).to eq options
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#configure!' do
33
+ it 'dispatches arguments to the configuration instance' do
34
+ opts = { opt: :value }
35
+ block = proc {}
36
+
37
+ expect(Qa::LDF::Configuration.instance)
38
+ .to receive(:configure!).with(**opts, &block).once
39
+
40
+ mod.configure!(**opts, &block)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/setup'
3
+ Bundler.setup
4
+
5
+ require 'pry' unless ENV['CI']
6
+ require 'webmock/rspec'
7
+
8
+ require 'qa/ldf'
9
+ require 'qa/ldf/spec'
10
+
11
+ Dir['./spec/support/**/*.rb'].each { |f| require f }
12
+
13
+ RSpec.configure do |config|
14
+ config.color = true
15
+ config.tty = true
16
+
17
+ config.formatter = :progress
18
+ end
@@ -0,0 +1,25 @@
1
+ require 'sham_rack'
2
+
3
+ require 'linked_data_fragments/cache_server'
4
+ require 'linked_data_fragments/repository'
5
+
6
+ shared_context 'with cache server' do
7
+ let(:server_endpoint) { 'http://ldcache.example.com/' }
8
+
9
+ before(:context) do
10
+ server_endpoint = 'ldcache.example.com'
11
+
12
+ ShamRack
13
+ .at(server_endpoint)
14
+ .mount(LinkedDataFragments::CacheServer::APPLICATION)
15
+
16
+ Qa::LDF.configure! do |config|
17
+ config[:endpoint] = "http://#{server_endpoint}"
18
+ end
19
+ end
20
+
21
+ after(:context) do
22
+ ShamRack.reset
23
+ Qa::LDF::Configuration.instance.reset!
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'support/shared_examples/ld_cache_client'
2
+
3
+ require 'rdf'
4
+ require 'rdf/vocab/skos'
5
+
6
+ ##
7
+ # A fake version of `Qa::LDF::Client`.
8
+ class FakeClient
9
+ attr_accessor :label, :graph
10
+
11
+ def initialize
12
+ yield self if block_given?
13
+ end
14
+
15
+ def get(uri:, dataset: :'')
16
+ graph ||= RDF::Graph.new
17
+ # Use dataset just to satisfy rubocop.
18
+ # Is there a better config setting for this; exclude this cop from fakes?
19
+ graph =
20
+ graph.dup <<
21
+ [RDF::URI(uri), RDF::Vocab::SKOS.prefLabel, label || "#{dataset}: moomin"]
22
+ graph
23
+ end
24
+ end
25
+
26
+ describe FakeClient do
27
+ subject(:client) { described_class.new }
28
+
29
+ let(:uri) { 'http://id.loc.gov/authorities/subjects/sh2004002557' }
30
+ it_behaves_like 'an ld cache client'
31
+ end