require 'spec_helper'

RSpec.describe Qa::LinkedData::GraphService do
  describe '.load_graph' do
    subject { described_class.load_graph(url: url) }
    let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }

    context 'when graph can be loaded' do
      before do
        stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
          .to_return(status: 200, body: webmock_fixture('lod_oclc_all_query_3_results.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
      end

      it 'builds a graph with many statements' do
        expect(subject).to be_kind_of RDF::Graph
        expect(subject.statements.size).to be > 10
      end
    end

    context 'when term is not found' do
      let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }

      before do
        stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
          .to_return(status: 404)
      end

      it 'raises error' do
        expect { described_class.load_graph(url: url) }.to raise_error(Qa::TermNotFound, "#{url} Not Found - Term may not exist at LOD Authority. (HTTPNotFound - 404)")
      end
    end

    context 'when service error' do
      subject { described_class.load_graph(url: url) }

      let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
      let(:uri) { URI(url) }

      before do
        stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
          .to_return(status: 500)
      end

      it 'raises error' do
        expect { subject }.to raise_error(Qa::ServiceError, "#{uri.hostname} on port #{uri.port} is not responding.  Try again later. (HTTPServerError - 500)")
      end
    end

    context 'when service unavailable' do
      subject { described_class.load_graph(url: url) }

      let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
      let(:uri) { URI(url) }

      before do
        stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
          .to_return(status: 503)
      end

      it 'raises error' do
        expect { subject }.to raise_error(Qa::ServiceUnavailable, "#{uri.hostname} on port #{uri.port} is not responding.  Try again later. (HTTPServiceUnavailable - 503)")
      end
    end

    context "when error isn't specifically handled" do
      subject { described_class.load_graph(url: url) }

      let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
      let(:regurl) { 'http:\/\/experimental.worldcat.org\/fast\/search\?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
      let(:uri) { URI(url) }

      before do
        stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
          .to_return(status: 504)
      end

      it 'raises error' do
        expect { subject }.to raise_error(Qa::ServiceError, /Unknown error for #{uri.hostname} on port #{uri.port}.  Try again later. \(Cause - <#{regurl}>: \(?504\)?\)/)
      end
    end
  end

  describe '.filter' do
    context 'with language filter' do
      subject { described_class.filter(graph: graph, language: language) }

      let(:url) { 'http://authority.with.language/search?query=foo' }
      let(:graph) { described_class.load_graph(url: url) }

      let(:en_dried_milk) { RDF::Literal.new("dried milk", language: :en) }
      let(:fr_dried_milk) { RDF::Literal.new("lait en poudre", language: :fr) }
      let(:de_dried_milk) { RDF::Literal.new("getrocknete Milch", language: :de) }

      let(:en_buttermilk) { RDF::Literal.new("buttermilk", language: :en) }
      let(:fr_buttermilk) { RDF::Literal.new("Babeurre", language: :fr) }
      let(:de_buttermilk) { RDF::Literal.new("Buttermilch", language: :de) }

      let(:en_condensed_milk) { RDF::Literal.new("condensed milk", language: :en) }
      let(:fr_condensed_milk) { RDF::Literal.new("lait condensé", language: :fr) }
      let(:de_condensed_milk) { RDF::Literal.new("Kondensmilch", language: :de) }

      before do
        stub_request(:get, 'http://authority.with.language/search?query=foo')
          .to_return(status: 200, body: webmock_fixture('lod_lang_search_enfrde.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
      end

      context 'when one language passed in' do
        let(:language) { [:fr] }

        it 'returns the graph with only the French subset of triples' do
          expect(subject.has_object?(en_dried_milk)).to be false
          expect(subject.has_object?(en_buttermilk)).to be false
          expect(subject.has_object?(en_condensed_milk)).to be false

          expect(subject.has_object?(fr_dried_milk)).to be true
          expect(subject.has_object?(fr_buttermilk)).to be true
          expect(subject.has_object?(fr_condensed_milk)).to be true

          expect(subject.has_object?(de_dried_milk)).to be false
          expect(subject.has_object?(de_buttermilk)).to be false
          expect(subject.has_object?(de_condensed_milk)).to be false
        end
      end

      context "when mix of language markers on graph statements" do
        let(:nomarker_de_buttermilk) { RDF::Literal.new("Buttermilch") }
        let(:language) { [:fr] }

        before do
          stub_request(:get, 'http://authority.with.language/search?query=foo')
            .to_return(status: 200, body: webmock_fixture('lod_lang_search_filtering.nt'), headers: { 'Content-Type' => 'application/n-triples' })
        end

        it 'filters down to the expected size' do
          expect(graph.size).to eq 11
          expect(subject.size).to eq 8
        end

        it 'the graph includes triples where object has the targeted language marker (e.g. :fr)' do
          expect(subject.has_object?(fr_dried_milk)).to be true
          expect(subject.has_object?(fr_buttermilk)).to be true
        end

        it 'the graph includes triples where object does not have a language marker' do
          expect(subject.has_object?(nomarker_de_buttermilk)).to be true
        end

        it "the graph includes triples regardless of language when there are no object's for the predicate that have the targeted language marker" do
          expect(subject.has_object?(en_condensed_milk)).to be true
          expect(subject.has_object?(de_condensed_milk)).to be true
        end

        it 'filters out the rest' do
          expect(subject.has_object?(en_dried_milk)).to be false
          expect(subject.has_object?(en_buttermilk)).to be false
          expect(subject.has_object?(de_dried_milk)).to be false
        end
      end

      context 'when multiple languages passed in' do
        let(:language) { [:en, :fr] }

        it 'returns the graph with English and French subset of triples' do
          expect(subject.has_object?(en_dried_milk)).to be true
          expect(subject.has_object?(en_buttermilk)).to be true
          expect(subject.has_object?(en_condensed_milk)).to be true

          expect(subject.has_object?(fr_dried_milk)).to be true
          expect(subject.has_object?(fr_buttermilk)).to be true
          expect(subject.has_object?(fr_condensed_milk)).to be true

          expect(subject.has_object?(de_dried_milk)).to be false
          expect(subject.has_object?(de_buttermilk)).to be false
          expect(subject.has_object?(de_condensed_milk)).to be false
        end
      end
    end

    context 'with filter out subject blanknodes' do
      subject { described_class.filter(graph: graph, remove_blanknode_subjects: true) }

      let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
      let(:graph) { described_class.load_graph(url: url) }

      before do
        stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
          .to_return(status: 200, body: webmock_fixture('lod_search_with_blanknode_subjects.nt'), headers: { 'Content-Type' => 'application/n-triples' })
      end

      it 'removes statements where the subject is a blanknode' do
        expect(graph.size).to be 18
        expect(subject.size).to be 12
      end
    end
  end

  describe '.object_values' do
    subject { described_class.object_values(graph: graph, subject: subject_uri, predicate: predicate_uri) }

    let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
    let(:graph) { described_class.load_graph(url: url) }
    let(:subject_uri) { RDF::URI('http://id.worldcat.org/fast/530369') }
    let(:predicate_uri) { RDF::URI('http://schema.org/sameAs') }

    before do
      stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
        .to_return(status: 200, body: webmock_fixture('lod_oclc_all_query_3_results.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
    end

    it 'returns all values for the subject-predicate pair' do
      expect(subject.size).to be 2
      expect(subject.map(&:to_s)).to match_array ['http://id.loc.gov/authorities/names/n79021621', 'https://viaf.org/viaf/126293486']
    end
  end

  describe '.deep_copy' do
    subject { described_class.object_values(graph: graph, subject: subject_uri, predicate: predicate_uri) }

    let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
    let(:graph) { described_class.load_graph(url: url) }
    let(:copied_graph) { described_class.deep_copy(graph: graph) }

    before do
      stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
        .to_return(status: 200, body: webmock_fixture('lod_oclc_all_query_3_results.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
    end

    it 'returns a copy of the graph' do
      expect(copied_graph).to be_kind_of RDF::Graph
      expect(copied_graph.statements.count).to eq graph.statements.count
    end
  end
end