defmodule RDF.DescriptionTest do use RDF.Test.Case doctest RDF.Description describe "new" do test "with a subject IRI" do assert description_of_subject(Description.new(~I), ~I) end test "with a raw subject IRI string" do assert description_of_subject(Description.new("http://example.com/description/subject"), ~I) end test "with an unresolved subject IRI term atom" do assert description_of_subject(Description.new(EX.Bar), iri(EX.Bar)) end test "with a BlankNode subject" do assert description_of_subject(Description.new(bnode(:foo)), bnode(:foo)) end test "with a single initial triple" do desc = Description.new({EX.Subject, EX.predicate, EX.Object}) assert description_of_subject(desc, iri(EX.Subject)) assert description_includes_predication(desc, {EX.predicate, iri(EX.Object)}) desc = Description.new(EX.Subject, EX.predicate, 42) assert description_of_subject(desc, iri(EX.Subject)) assert description_includes_predication(desc, {EX.predicate, literal(42)}) end test "with a list of initial triples" do desc = Description.new([{EX.Subject, EX.predicate1, EX.Object1}, {EX.Subject, EX.predicate2, EX.Object2}]) assert description_of_subject(desc, iri(EX.Subject)) assert description_includes_predication(desc, {EX.predicate1, iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate2, iri(EX.Object2)}) desc = Description.new(EX.Subject, EX.predicate, [EX.Object, bnode(:foo), "bar"]) assert description_of_subject(desc, iri(EX.Subject)) assert description_includes_predication(desc, {EX.predicate, iri(EX.Object)}) assert description_includes_predication(desc, {EX.predicate, bnode(:foo)}) assert description_includes_predication(desc, {EX.predicate, literal("bar")}) end test "from another description" do desc1 = Description.new({EX.Other, EX.predicate, EX.Object}) desc2 = Description.new(EX.Subject, desc1) assert description_of_subject(desc2, iri(EX.Subject)) assert description_includes_predication(desc2, {EX.predicate, iri(EX.Object)}) end test "from a map with coercible RDF term" do desc = Description.new(EX.Subject, %{EX.Predicate => EX.Object}) assert description_of_subject(desc, iri(EX.Subject)) assert description_includes_predication(desc, {iri(EX.Predicate), iri(EX.Object)}) end test "with another description as subject, it performs and add " do desc = Description.new({EX.S, EX.p, EX.O}) assert Description.new(desc, EX.p2, EX.O2) == Description.add(desc, EX.p2, EX.O2) assert Description.new(desc, EX.p, [EX.O1, EX.O2]) == Description.add(desc, EX.p, [EX.O1, EX.O2]) end end describe "add" do test "a predicate-object-pair of proper RDF terms" do assert Description.add(description(), EX.predicate, iri(EX.Object)) |> description_includes_predication({EX.predicate, iri(EX.Object)}) assert Description.add(description(), {EX.predicate, iri(EX.Object)}) |> description_includes_predication({EX.predicate, iri(EX.Object)}) end test "a predicate-object-pair of coercible RDF terms" do assert Description.add(description(), "http://example.com/predicate", iri(EX.Object)) |> description_includes_predication({EX.predicate, iri(EX.Object)}) assert Description.add(description(), {"http://example.com/predicate", 42}) |> description_includes_predication({EX.predicate, literal(42)}) assert Description.add(description(), {"http://example.com/predicate", true}) |> description_includes_predication({EX.predicate, literal(true)}) assert Description.add(description(), {"http://example.com/predicate", bnode(:foo)}) |> description_includes_predication({EX.predicate, bnode(:foo)}) end test "a proper triple" do assert Description.add(description(), {iri(EX.Subject), EX.predicate, iri(EX.Object)}) |> description_includes_predication({EX.predicate, iri(EX.Object)}) assert Description.add(description(), {iri(EX.Subject), EX.predicate, literal(42)}) |> description_includes_predication({EX.predicate, literal(42)}) assert Description.add(description(), {iri(EX.Subject), EX.predicate, bnode(:foo)}) |> description_includes_predication({EX.predicate, bnode(:foo)}) end test "add ignores triples not about the subject of the Description struct" do assert empty_description( Description.add(description(), {EX.Other, EX.predicate, iri(EX.Object)})) end test "a list of predicate-object-pairs" do desc = Description.add(description(), [{EX.predicate, EX.Object1}, {EX.predicate, EX.Object2}]) assert description_includes_predication(desc, {EX.predicate, iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate, iri(EX.Object2)}) end test "a list of triples" do desc = Description.add(description(), [ {EX.Subject, EX.predicate1, EX.Object1}, {EX.Subject, EX.predicate2, EX.Object2} ]) assert description_includes_predication(desc, {EX.predicate1, iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate2, iri(EX.Object2)}) end test "a list of mixed triples and predicate-object-pairs" do desc = Description.add(description(), [ {EX.predicate, EX.Object1}, {EX.Subject, EX.predicate, EX.Object2}, {EX.Other, EX.predicate, EX.Object3} ]) assert description_of_subject(desc, iri(EX.Subject)) assert description_includes_predication(desc, {EX.predicate, iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate, iri(EX.Object2)}) refute description_includes_predication(desc, {EX.predicate, iri(EX.Object3)}) end test "another description" do desc = description([{EX.predicate1, EX.Object1}, {EX.predicate2, EX.Object2}]) |> Description.add(Description.new({EX.Other, EX.predicate3, EX.Object3})) assert description_of_subject(desc, iri(EX.Subject)) assert description_includes_predication(desc, {EX.predicate1, iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate2, iri(EX.Object2)}) assert description_includes_predication(desc, {EX.predicate3, iri(EX.Object3)}) desc = Description.add(desc, Description.new({EX.Other, EX.predicate1, EX.Object4})) assert description_includes_predication(desc, {EX.predicate1, iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate2, iri(EX.Object2)}) assert description_includes_predication(desc, {EX.predicate3, iri(EX.Object3)}) assert description_includes_predication(desc, {EX.predicate1, iri(EX.Object4)}) end test "a map of predications with coercible RDF terms" do desc = description([{EX.predicate1, EX.Object1}, {EX.predicate2, EX.Object2}]) |> Description.add(%{EX.predicate3 => EX.Object3}) assert description_of_subject(desc, iri(EX.Subject)) assert description_includes_predication(desc, {EX.predicate1, iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate2, iri(EX.Object2)}) assert description_includes_predication(desc, {EX.predicate3, iri(EX.Object3)}) desc = Description.add(desc, %{EX.predicate1 => EX.Object1, EX.predicate2 => [EX.Object2, 42], EX.predicate3 => [bnode(:foo)]}) assert Description.count(desc) == 5 assert description_includes_predication(desc, {EX.predicate1, iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate2, iri(EX.Object2)}) assert description_includes_predication(desc, {EX.predicate2, literal(42)}) assert description_includes_predication(desc, {EX.predicate3, iri(EX.Object3)}) assert description_includes_predication(desc, {EX.predicate3, bnode(:foo)}) end test "a map of predications with non-coercible RDF terms" do assert_raise RDF.IRI.InvalidError, fn -> Description.add(description(), %{"not a IRI" => iri(EX.Object)}) end assert_raise RDF.Literal.InvalidError, fn -> Description.add(description(), %{EX.prop => self()}) end end test "duplicates are ignored" do desc = Description.add(description(), {EX.predicate, EX.Object}) assert Description.add(desc, {EX.predicate, EX.Object}) == desc assert Description.add(desc, {EX.Subject, EX.predicate, EX.Object}) == desc desc = Description.add(description(), {EX.predicate, 42}) assert Description.add(desc, {EX.predicate, literal(42)}) == desc end test "non-coercible Triple elements are causing an error" do assert_raise RDF.IRI.InvalidError, fn -> Description.add(description(), {"not a IRI", iri(EX.Object)}) end assert_raise RDF.Literal.InvalidError, fn -> Description.add(description(), {EX.prop, self()}) end end end describe "delete" do setup do {:ok, empty_description: Description.new(EX.S), description1: Description.new(EX.S, EX.p, EX.O), description2: Description.new(EX.S, EX.p, [EX.O1, EX.O2]), description3: Description.new(EX.S, [ {EX.p1, [EX.O1, EX.O2]}, {EX.p2, EX.O3}, {EX.p3, [~B, ~L"bar"]}, ]) } end test "a single statement as a predicate object", %{empty_description: empty_description, description1: description1, description2: description2} do assert Description.delete(empty_description, EX.p, EX.O) == empty_description assert Description.delete(description1, EX.p, EX.O) == empty_description assert Description.delete(description2, EX.p, EX.O1) == Description.new(EX.S, EX.p, EX.O2) end test "a single statement as a predicate-object tuple", %{empty_description: empty_description, description1: description1, description2: description2} do assert Description.delete(empty_description, {EX.p, EX.O}) == empty_description assert Description.delete(description1, {EX.p, EX.O}) == empty_description assert Description.delete(description2, {EX.p, EX.O2}) == Description.new(EX.S, EX.p, EX.O1) end test "a single statement as a subject-predicate-object tuple and the proper description subject", %{empty_description: empty_description, description1: description1, description2: description2} do assert Description.delete(empty_description, {EX.S, EX.p, EX.O}) == empty_description assert Description.delete(description1, {EX.S, EX.p, EX.O}) == empty_description assert Description.delete(description2, {EX.S, EX.p, EX.O2}) == Description.new(EX.S, EX.p, EX.O1) end test "a single statement as a subject-predicate-object tuple and another description subject", %{empty_description: empty_description, description1: description1, description2: description2} do assert Description.delete(empty_description, {EX.Other, EX.p, EX.O}) == empty_description assert Description.delete(description1, {EX.Other, EX.p, EX.O}) == description1 assert Description.delete(description2, {EX.Other, EX.p, EX.O2}) == description2 end test "multiple statements via predicate-objects tuple", %{empty_description: empty_description, description1: description1, description2: description2} do assert Description.delete(empty_description, {EX.p, [EX.O1, EX.O2]}) == empty_description assert Description.delete(description1, {EX.p, [EX.O, EX.O2]}) == empty_description assert Description.delete(description2, {EX.p, [EX.O1, EX.O2]}) == empty_description end test "multiple statements with a list", %{empty_description: empty_description, description3: description3} do assert Description.delete(empty_description, [{EX.p, [EX.O1, EX.O2]}]) == empty_description assert Description.delete(description3, [ {EX.p1, EX.O1}, {EX.p2, [EX.O2, EX.O3]}, {EX.S, EX.p3, [~B, ~L"bar"]}, ]) == Description.new(EX.S, EX.p1, EX.O2) end test "multiple statements with a map of predications", %{empty_description: empty_description, description3: description3} do assert Description.delete(empty_description, %{EX.p => EX.O1}) == empty_description assert Description.delete(description3, %{ EX.p1 => EX.O1, EX.p2 => [EX.O2, EX.O3], EX.p3 => [~B, ~L"bar"], }) == Description.new(EX.S, EX.p1, EX.O2) end test "multiple statements with another description", %{empty_description: empty_description, description1: description1, description3: description3} do assert Description.delete(empty_description, description1) == empty_description assert Description.delete(description3, Description.new(EX.S, %{ EX.p1 => EX.O1, EX.p2 => [EX.O2, EX.O3], EX.p3 => [~B, ~L"bar"], })) == Description.new(EX.S, EX.p1, EX.O2) end end describe "delete_predicates" do setup do {:ok, empty_description: Description.new(EX.S), description1: Description.new(EX.S, EX.p, [EX.O1, EX.O2]), description2: Description.new(EX.S, [ {EX.P1, [EX.O1, EX.O2]}, {EX.p2, [~B, ~L"bar"]}, ]) } end test "a single property", %{empty_description: empty_description, description1: description1, description2: description2} do assert Description.delete_predicates(description1, EX.p) == empty_description assert Description.delete_predicates(description2, EX.P1) == Description.new(EX.S, EX.p2, [~B, ~L"bar"]) end test "a list of properties", %{empty_description: empty_description, description1: description1, description2: description2} do assert Description.delete_predicates(description1, [EX.p]) == empty_description assert Description.delete_predicates(description2, [EX.P1, EX.p2, EX.p3]) == empty_description end end describe "update/4" do test "list values returned from the update function become new coerced objects of the predicate" do assert Description.new(EX.S, EX.P, [EX.O1, EX.O2]) |> Description.update(EX.P, fn [_object | other] -> [EX.O3 | other] end) == Description.new(EX.S, EX.P, [EX.O3, EX.O2]) end test "single values returned from the update function becomes new object of the predicate" do assert Description.new(EX.S, EX.P, [EX.O1, EX.O2]) |> Description.update(EX.P, fn _ -> EX.O3 end) == Description.new(EX.S, EX.P, EX.O3) end test "returning an empty list or nil from the update function causes a removal of the predications" do description = EX.S |> EX.p(EX.O1, EX.O2) assert description |> Description.update(EX.p, fn _ -> [] end) == Description.new(EX.S, {EX.p, []}) assert description |> Description.update(EX.p, fn _ -> nil end) == Description.new(EX.S, {EX.p, []}) end test "when the property is not present the initial object value is added for the predicate and the update function not called" do fun = fn _ -> raise "should not be called" end assert Description.new(EX.S) |> Description.update(EX.P, EX.O, fun) == Description.new(EX.S, EX.P, EX.O) assert Description.new(EX.S) |> Description.update(EX.P, fun) == Description.new(EX.S) end end test "pop" do assert Description.pop(Description.new(EX.S)) == {nil, Description.new(EX.S)} {triple, desc} = Description.new({EX.S, EX.p, EX.O}) |> Description.pop assert {iri(EX.S), iri(EX.p), iri(EX.O)} == triple assert Enum.count(desc.predications) == 0 {{subject, predicate, _}, desc} = Description.new([{EX.S, EX.p, EX.O1}, {EX.S, EX.p, EX.O2}]) |> Description.pop assert {subject, predicate} == {iri(EX.S), iri(EX.p)} assert Enum.count(desc.predications) == 1 {{subject, _, _}, desc} = Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) |> Description.pop assert subject == iri(EX.S) assert Enum.count(desc.predications) == 1 end test "values/1" do assert Description.new(EX.s) |> Description.values() == %{} assert Description.new({EX.s, EX.p, ~L"Foo"}) |> Description.values() == %{RDF.Term.value(EX.p) => ["Foo"]} end test "values/2" do mapping = fn {:predicate, predicate} -> predicate |> to_string() |> String.split("/") |> List.last() |> String.to_atom() {_, term} -> RDF.Term.value(term) end assert Description.new(EX.s) |> Description.values(mapping) == %{} assert Description.new({EX.s, EX.p, ~L"Foo"}) |> Description.values(mapping) == %{p: ["Foo"]} end describe "take/2" do test "with a non-empty property list" do assert Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) |> Description.take([EX.p2, EX.p3]) == Description.new({EX.S, EX.p2, EX.O2}) end test "with an empty property list" do assert Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) |> Description.take([]) == Description.new(EX.S) end test "with nil" do assert Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) |> Description.take(nil) == Description.new([{EX.S, EX.p1, EX.O1}, {EX.S, EX.p2, EX.O2}]) end end test "equal/2" do assert Description.new({EX.S, EX.p, EX.O}) |> Description.equal?(Description.new({EX.S, EX.p, EX.O})) refute Description.new({EX.S, EX.p, EX.O}) |> Description.equal?(Description.new({EX.S, EX.p, EX.O2})) end describe "Enumerable protocol" do test "Enum.count" do assert Enum.count(Description.new EX.foo) == 0 assert Enum.count(Description.new {EX.S, EX.p, EX.O}) == 1 assert Enum.count(Description.new [{EX.S, EX.p, EX.O1}, {EX.S, EX.p, EX.O2}]) == 2 end test "Enum.member?" do refute Enum.member?(Description.new(EX.S), {iri(EX.S), EX.p, iri(EX.O)}) assert Enum.member?(Description.new({EX.S, EX.p, EX.O}), {EX.S, EX.p, EX.O}) desc = Description.new([ {EX.Subject, EX.predicate1, EX.Object1}, {EX.Subject, EX.predicate2, EX.Object2}, {EX.predicate2, EX.Object3}]) assert Enum.member?(desc, {EX.Subject, EX.predicate1, EX.Object1}) assert Enum.member?(desc, {EX.Subject, EX.predicate2, EX.Object2}) assert Enum.member?(desc, {EX.Subject, EX.predicate2, EX.Object3}) refute Enum.member?(desc, {EX.Subject, EX.predicate1, EX.Object2}) end test "Enum.reduce" do desc = Description.new([ {EX.Subject, EX.predicate1, EX.Object1}, {EX.Subject, EX.predicate2, EX.Object2}, {EX.predicate2, EX.Object3}]) assert desc == Enum.reduce(desc, description(), fn(triple, acc) -> acc |> Description.add(triple) end) end end describe "Collectable protocol" do test "with a map" do map = %{ EX.predicate1 => EX.Object1, EX.predicate2 => EX.Object2 } assert Enum.into(map, Description.new(EX.Subject)) == Description.new(EX.Subject, map) end test "with a list of triples" do triples = [ {EX.Subject, EX.predicate1, EX.Object1}, {EX.Subject, EX.predicate2, EX.Object2} ] assert Enum.into(triples, Description.new(EX.Subject)) == Description.new(triples) end test "with a list of predicate-object pairs" do pairs = [ {EX.predicate1, EX.Object1}, {EX.predicate2, EX.Object2} ] assert Enum.into(pairs, Description.new(EX.Subject)) == Description.new(EX.Subject, pairs) end test "with a list of lists" do lists = [ [EX.Subject, EX.predicate1, EX.Object1], [EX.Subject, EX.predicate2, EX.Object2] ] assert Enum.into(lists, Description.new(EX.Subject)) == Description.new(Enum.map(lists, &List.to_tuple/1)) end end describe "Access behaviour" do test "access with the [] operator" do assert Description.new(EX.Subject)[EX.predicate] == nil assert Description.new(EX.Subject, EX.predicate, EX.Object)[EX.predicate] == [iri(EX.Object)] assert Description.new(EX.Subject, EX.Predicate, EX.Object)[EX.Predicate] == [iri(EX.Object)] assert Description.new(EX.Subject, EX.predicate, EX.Object)["http://example.com/predicate"] == [iri(EX.Object)] assert Description.new([{EX.Subject, EX.predicate1, EX.Object1}, {EX.Subject, EX.predicate1, EX.Object2}, {EX.Subject, EX.predicate2, EX.Object3}])[EX.predicate1] == [iri(EX.Object1), iri(EX.Object2)] end end end