rdf-ldp 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,245 @@
1
+ require 'rdf/ldp/spec/container'
2
+
3
+ # @todo: make this set of examples less opinionated about #add behavior.
4
+ # Break #add tests into another group shared between DirectContainer &
5
+ # IndirectContainer. This way other implementations can use these specs
6
+ # but make different intrepretations of loose parts in the LDP spec.
7
+ shared_examples 'a DirectContainer' do
8
+ it_behaves_like 'a Container'
9
+
10
+ let(:uri) { RDF::URI('http://ex.org/moomin') }
11
+ let(:repo) { RDF::Repository.new }
12
+ subject { described_class.new(uri, repo) }
13
+
14
+ let(:has_member_statement) do
15
+ RDF::Statement(subject.subject_uri,
16
+ RDF::Vocab::LDP.hasMemberRelation,
17
+ RDF::Vocab::DC.hasPart)
18
+ end
19
+
20
+ let(:is_member_of_statement) do
21
+ RDF::Statement(subject.subject_uri,
22
+ RDF::Vocab::LDP.isMemberOfRelation,
23
+ RDF::Vocab::DC.isPartOf)
24
+ end
25
+
26
+ describe '#add' do
27
+ let(:added) { described_class.new(added_uri, repo) }
28
+ let(:added_uri) { RDF::URI('http://ex.org/too-ticky') }
29
+
30
+ context 'when the membership resource does not exist' do
31
+ before do
32
+ subject.create(StringIO.new, 'application/n-triples')
33
+
34
+ subject.graph.update has_member_statement
35
+ subject.graph.update RDF::Statement(subject.subject_uri,
36
+ RDF::Vocab::LDP.membershipResource,
37
+ membership_resource_uri)
38
+ end
39
+
40
+ let(:membership_resource_uri) do
41
+ RDF::URI('http://example.com/moomin_resource')
42
+ end
43
+
44
+ it 'adds membership triple to the container' do
45
+ expect { subject.add(added) }
46
+ .to change { subject.graph.statements }
47
+ .to include subject.make_membership_triple(added_uri)
48
+ end
49
+ end
50
+
51
+ context 'when the membership resource exists' do
52
+ before { subject.create(StringIO.new, 'application/n-triples') }
53
+
54
+ it 'adds membership triple to container' do
55
+ expect(subject.add(added).graph)
56
+ .to have_statement subject.make_membership_triple(added_uri)
57
+ end
58
+
59
+ it 'updates last_modified for membership resource' do
60
+ expect { subject.add(added).graph }
61
+ .to change { subject.last_modified }
62
+ end
63
+
64
+ it 'updates etag for for membership resource' do
65
+ expect { subject.add(added).graph }
66
+ .to change { subject.etag }
67
+ end
68
+
69
+ it 'adds membership triple to container for custom membership resource' do
70
+ repo = RDF::Repository.new
71
+ subject = described_class.new(uri, repo)
72
+ mem_rs = RDF::LDP::RDFSource.new(RDF::URI('http://ex.org/mymble'),
73
+ repo)
74
+
75
+ g = RDF::Graph.new << RDF::Statement(subject.subject_uri,
76
+ RDF::Vocab::LDP.membershipResource,
77
+ mem_rs.subject_uri)
78
+
79
+ subject.create(StringIO.new(g.dump(:ntriples)), 'application/n-triples')
80
+ mem_rs.create(StringIO.new, 'application/n-triples')
81
+
82
+ subject.add(added)
83
+
84
+ expect(subject.graph)
85
+ .to have_statement subject.make_membership_triple(added_uri)
86
+ end
87
+
88
+ it 'adds membership triple to membership resource with #fragment' do
89
+ repo = RDF::Repository.new
90
+ subject = described_class.new(uri, repo)
91
+
92
+ mem_rs = subject.subject_uri / '#membership'
93
+
94
+ g = RDF::Graph.new << RDF::Statement(subject.subject_uri,
95
+ RDF::Vocab::LDP.membershipResource,
96
+ mem_rs)
97
+
98
+ subject.create(StringIO.new(g.dump(:ttl)), 'application/n-triples')
99
+ expect(subject.add(added).graph)
100
+ .to have_statement subject.make_membership_triple(added_uri)
101
+ end
102
+
103
+ it 'adds membership triple to container for LDP-NR membership resource' do
104
+ repo = RDF::Repository.new
105
+ container = described_class.new(uri, repo)
106
+
107
+ nr = RDF::LDP::NonRDFSource.new('http://example.org/moomin_file',
108
+ repo)
109
+
110
+ g = RDF::Graph.new << RDF::Statement(subject.subject_uri,
111
+ RDF::Vocab::LDP.membershipResource,
112
+ nr.to_uri)
113
+
114
+ container
115
+ .create(StringIO.new(g.dump(:ntriples)), 'application/n-triples')
116
+
117
+ nr.create(StringIO.new, 'application/n-triples')
118
+
119
+ container.add(added)
120
+ expect(container.graph)
121
+ .to have_statement container.make_membership_triple(added_uri)
122
+ end
123
+
124
+ context 'with multiple membership resources' do
125
+ it 'raises an error' do
126
+ subject.graph << RDF::Statement(subject.subject_uri,
127
+ RDF::Vocab::LDP.membershipResource,
128
+ subject.subject_uri)
129
+ subject.graph << RDF::Statement(subject.subject_uri,
130
+ RDF::Vocab::LDP.membershipResource,
131
+ (subject.subject_uri / 'moomin'))
132
+
133
+ expect { subject.add(added) }
134
+ .to raise_error RDF::LDP::RequestError
135
+ expect(subject.containment_triples).to be_empty
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ describe '#remove' do
142
+ let(:added) { described_class.new(added_uri, repo) }
143
+ let(:added_uri) { RDF::URI('http://ex.org/too-ticky') }
144
+
145
+ context 'when the membership resource exists' do
146
+ before { subject.create(StringIO.new, 'application/n-triples') }
147
+
148
+ it 'removes membership triple to membership resource' do
149
+ subject.graph << subject.make_membership_triple(added_uri)
150
+ expect(subject.remove(added_uri).graph)
151
+ .not_to have_statement subject.make_membership_triple(added_uri)
152
+ end
153
+ end
154
+ end
155
+
156
+ describe '#membership_constant_uri' do
157
+ it 'when created defaults to #subject_uri' do
158
+ subject.create(StringIO.new, 'application/n-triples')
159
+ expect(subject.membership_constant_uri).to eq subject.subject_uri
160
+ expect(subject.graph)
161
+ .to have_statement RDF::Statement(subject.subject_uri,
162
+ RDF::Vocab::LDP.membershipResource,
163
+ subject.subject_uri)
164
+ end
165
+
166
+ it 'gives membership resource' do
167
+ membership_resource = (subject.subject_uri / '#too-ticky')
168
+ subject.graph << RDF::Statement(subject.subject_uri,
169
+ RDF::Vocab::LDP.membershipResource,
170
+ membership_resource)
171
+ expect(subject.membership_constant_uri).to eq membership_resource
172
+ end
173
+
174
+ it 'raises an error if multiple are present' do
175
+ membership_resource = (subject.subject_uri / '#too-ticky')
176
+ subject.graph << RDF::Statement(subject.subject_uri,
177
+ RDF::Vocab::LDP.membershipResource,
178
+ membership_resource)
179
+
180
+ subject.graph << RDF::Statement(subject.subject_uri,
181
+ RDF::Vocab::LDP.membershipResource,
182
+ subject.subject_uri)
183
+
184
+ expect { subject.membership_constant_uri }
185
+ .to raise_error RDF::LDP::RequestError
186
+ end
187
+ end
188
+
189
+ describe '#membership_predicate' do
190
+ it 'when created returns a uri' do
191
+ subject.create(StringIO.new, 'application/n-triples')
192
+ expect(subject.membership_predicate).to be_a RDF::URI
193
+ end
194
+
195
+ it 'gives assigned member relation predicate for hasMember' do
196
+ subject.graph << has_member_statement
197
+
198
+ expect(subject.membership_predicate).to eq RDF::Vocab::DC.hasPart
199
+ end
200
+
201
+ it 'gives assigned member relation predicate for isMemberOf' do
202
+ subject.graph << is_member_of_statement
203
+
204
+ expect(subject.membership_predicate).to eq RDF::Vocab::DC.isPartOf
205
+ end
206
+
207
+ it 'raises an error if multiple relation predicates are present' do
208
+ subject.graph << has_member_statement
209
+ subject.graph << is_member_of_statement
210
+
211
+ expect { subject.membership_predicate }
212
+ .to raise_error RDF::LDP::RequestError
213
+ end
214
+ end
215
+
216
+ describe '#make_membership_triple' do
217
+ context 'with hasMember' do
218
+ before do
219
+ g = RDF::Graph.new << has_member_statement
220
+ subject.create(StringIO.new(g.dump(:ntriples)), 'application/n-triples')
221
+ end
222
+
223
+ it 'is constant - predicate - derived' do
224
+ expect(subject.make_membership_triple(uri))
225
+ .to eq RDF::Statement(subject.membership_constant_uri,
226
+ subject.membership_predicate,
227
+ uri)
228
+ end
229
+ end
230
+
231
+ context 'with isMemberOf' do
232
+ before do
233
+ g = RDF::Graph.new << is_member_of_statement
234
+ subject.create(StringIO.new(g.dump(:ntriples)), 'application/n-triples')
235
+ end
236
+
237
+ it 'is derived - predicate - constant' do
238
+ expect(subject.make_membership_triple(uri))
239
+ .to eq RDF::Statement(uri,
240
+ subject.membership_predicate,
241
+ subject.membership_constant_uri)
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,152 @@
1
+ require 'rdf/ldp/spec/direct_container'
2
+
3
+ shared_examples 'an IndirectContainer' do
4
+ it_behaves_like 'a DirectContainer'
5
+
6
+ shared_context 'with a relation' do
7
+ before do
8
+ subject.create(StringIO.new(graph.dump(:ntriples)),
9
+ 'application/n-triples')
10
+ end
11
+
12
+ let(:graph) { RDF::Graph.new << inserted_content_statement }
13
+ let(:relation_predicate) { RDF::Vocab::DC.creator }
14
+
15
+ let(:inserted_content_statement) do
16
+ RDF::Statement(uri,
17
+ RDF::Vocab::LDP.insertedContentRelation,
18
+ relation_predicate)
19
+ end
20
+ end
21
+
22
+ describe '#inserted_content_relation' do
23
+ it 'returns a uri' do
24
+ subject.create(StringIO.new, 'application/n-triples')
25
+ expect(subject.inserted_content_relation).to be_a RDF::URI
26
+ end
27
+
28
+ context 'with a relation' do
29
+ include_context 'with a relation'
30
+
31
+ it 'gives the relation' do
32
+ expect(subject.inserted_content_relation).to eq relation_predicate
33
+ end
34
+
35
+ it 'raises an error when more than one exists' do
36
+ new_statement = inserted_content_statement.clone
37
+ new_statement.object = RDF::Vocab::DC.relation
38
+ subject.graph << new_statement
39
+ expect { subject.inserted_content_relation }
40
+ .to raise_error RDF::LDP::NotAcceptable
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#add' do
46
+ include_context 'with a relation'
47
+
48
+ subject { described_class.new(uri, repo) }
49
+
50
+ let(:repo) { RDF::Repository.new }
51
+ let(:resource_uri) { RDF::URI('http://example.org/too-ticky') }
52
+ let(:contained_resource) { RDF::LDP::RDFSource.new(resource_uri, repo) }
53
+
54
+ context 'when no derived URI is found' do
55
+ it 'raises NotAcceptable' do
56
+ expect { subject.add(contained_resource) }
57
+ .to raise_error RDF::LDP::NotAcceptable
58
+ end
59
+
60
+ it 'does not create the resource' do
61
+ begin; subject.add(contained_resource); rescue; end
62
+ expect(contained_resource).not_to exist
63
+ end
64
+ end
65
+
66
+ context 'with expected predicate' do
67
+ before { contained_resource.graph << statement }
68
+
69
+ let(:target_uri) { contained_resource.to_uri / '#me' }
70
+
71
+ let(:statement) do
72
+ RDF::Statement(contained_resource.to_uri,
73
+ relation_predicate,
74
+ target_uri)
75
+ end
76
+
77
+ it 'when membership resource does not exist raises NotAcceptable' do
78
+ new_resource = described_class.new(uri / 'new', repo)
79
+ expect { new_resource.add(contained_resource) }
80
+ .to raise_error RDF::LDP::NotAcceptable
81
+ end
82
+
83
+ context 'when the container exists' do
84
+ it 'adds membership triple' do
85
+ subject.add(contained_resource)
86
+ expect(subject.graph.statements)
87
+ .to include RDF::Statement(subject.to_uri,
88
+ subject.membership_predicate,
89
+ target_uri)
90
+ end
91
+
92
+ it 'for multiple predicates raises NotAcceptable' do
93
+ new_statement = statement.clone
94
+ new_statement.object = contained_resource.to_uri / '#you'
95
+ contained_resource.graph << new_statement
96
+ expect { subject.add(contained_resource) }
97
+ .to raise_error RDF::LDP::NotAcceptable
98
+ end
99
+
100
+ it 'for an LDP-NR raises NotAcceptable' do
101
+ nr_resource = RDF::LDP::NonRDFSource.new(resource_uri, repo)
102
+ expect { subject.add(nr_resource) }
103
+ .to raise_error RDF::LDP::NotAcceptable
104
+ end
105
+
106
+ context 'with membership resource' do
107
+ before do
108
+ subject.graph
109
+ .delete([uri, RDF::Vocab::LDP.membershipResource, nil])
110
+ subject.graph << RDF::Statement(uri,
111
+ RDF::Vocab::LDP.membershipResource,
112
+ membership_resource)
113
+ end
114
+
115
+ let(:membership_resource) { uri }
116
+
117
+ it 'raises error when resource does not exist' do
118
+ new_resource = described_class.new(uri / 'new', repo)
119
+ expect { new_resource.add(contained_resource) }
120
+ .to raise_error RDF::LDP::NotAcceptable
121
+ end
122
+
123
+ context 'when the membership resource is not in the server' do
124
+ let(:membership_resource) { uri / '#me' }
125
+
126
+ it 'adds membership triple to container' do
127
+ contained_resource.create(StringIO.new, 'application/n-triples')
128
+ subject.add(contained_resource)
129
+
130
+ expect(subject.graph.statements)
131
+ .to include RDF::Statement(membership_resource,
132
+ subject.membership_predicate,
133
+ target_uri)
134
+ end
135
+
136
+ it 'removes membership triple to container' do
137
+ contained_resource.create(StringIO.new, 'application/n-triples')
138
+
139
+ subject.add(contained_resource)
140
+ subject.remove(contained_resource)
141
+
142
+ expect(subject.graph.statements)
143
+ .not_to include RDF::Statement(membership_resource,
144
+ subject.membership_predicate,
145
+ target_uri)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,138 @@
1
+ require 'rdf/ldp/spec/resource'
2
+
3
+ shared_examples 'a NonRDFSource' do
4
+ it_behaves_like 'a Resource'
5
+
6
+ subject { described_class.new(uri) }
7
+ let(:uri) { RDF::URI 'http://example.org/moomin' }
8
+
9
+ let(:contents) { StringIO.new('mummi') }
10
+
11
+ after { subject.destroy }
12
+
13
+ describe '#non_rdf_source?' do
14
+ it { is_expected.to be_non_rdf_source }
15
+ end
16
+
17
+ describe '#create' do
18
+ it 'writes the input to body' do
19
+ subject.create(contents, 'text/plain')
20
+ contents.rewind
21
+ expect(subject.to_response.each.to_a).to eq contents.each.to_a
22
+ end
23
+
24
+ it 'sets #content_type' do
25
+ expect { subject.create(StringIO.new(''), 'text/plain') }
26
+ .to change { subject.content_type }.to('text/plain')
27
+ end
28
+
29
+ it 'persists to resource' do
30
+ repo = RDF::Repository.new
31
+ saved = described_class.new(uri, repo)
32
+
33
+ saved.create(contents, 'text/plain')
34
+ contents.rewind
35
+
36
+ loaded = RDF::LDP::Resource.find(uri, repo)
37
+ expect(loaded.to_response.each.to_a).to eq contents.each.to_a
38
+ end
39
+
40
+ it 'creates an LDP::RDFSource' do
41
+ repo = RDF::Repository.new
42
+ saved = described_class.new(uri, repo)
43
+ description = RDF::LDP::RDFSource.new(subject.description_uri, repo)
44
+
45
+ expect { saved.create(contents, 'text/plain') }
46
+ .to change { description.exists? }.from(false).to(true)
47
+ end
48
+ end
49
+
50
+ describe '#update' do
51
+ before { subject.create(contents, 'text/plain') }
52
+
53
+ it 'writes the input to body' do
54
+ new_contents = StringIO.new('snorkmaiden')
55
+ expect { subject.update(new_contents, 'text/plain') }
56
+ .to change { subject.to_response.to_a }
57
+ .from(a_collection_containing_exactly('mummi'))
58
+ .to(a_collection_containing_exactly('snorkmaiden'))
59
+ end
60
+
61
+ it 'updates #content_type' do
62
+ expect { subject.update(contents, 'text/prs.moomin') }
63
+ .to change { subject.content_type }
64
+ .from('text/plain').to('text/prs.moomin')
65
+ end
66
+ end
67
+
68
+ describe '#description' do
69
+ it 'is not found' do
70
+ expect { subject.description }.to raise_error RDF::LDP::NotFound
71
+ end
72
+
73
+ context 'when it exists' do
74
+ before { subject.create(StringIO.new(''), 'text/plain') }
75
+
76
+ it 'is an RDFSource' do
77
+ expect(subject.description).to be_rdf_source
78
+ end
79
+
80
+ it 'is the description uri' do
81
+ expect(subject.description.to_uri).to eq subject.description_uri
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#description_uri' do
87
+ it 'is a uri' do
88
+ expect(subject.description_uri).to be_a RDF::URI
89
+ end
90
+ end
91
+
92
+ describe '#storage' do
93
+ it 'sets a default storage adapter' do
94
+ expect(subject.storage).to be_a RDF::LDP::NonRDFSource::FileStorageAdapter
95
+ end
96
+
97
+ it 'explicitly sets a storage adapter' do
98
+ class DummyAdapter < RDF::LDP::NonRDFSource::FileStorageAdapter
99
+ end
100
+
101
+ dummy_subject = described_class.new(uri, nil, DummyAdapter)
102
+ expect(dummy_subject.storage).to be_a DummyAdapter
103
+ end
104
+ end
105
+
106
+ describe '#to_response' do
107
+ it 'gives an empty response if it is new' do
108
+ expect(subject.to_response.to_a).to eq []
109
+ end
110
+
111
+ it 'does not create a non-existant file' do
112
+ subject.to_response
113
+ expect(subject.storage.send(:file_exists?)).to be false
114
+ end
115
+ end
116
+
117
+ describe '#destroy' do
118
+ before { subject.create(contents, 'text/plain') }
119
+
120
+ it 'deletes the content' do
121
+ expect { subject.destroy }
122
+ .to change { subject.to_response.to_a }
123
+ .from(a_collection_containing_exactly('mummi')).to([])
124
+ end
125
+
126
+ it 'marks resource as destroyed' do
127
+ expect { subject.destroy }
128
+ .to change { subject.destroyed? }.from(false).to(true)
129
+ end
130
+ end
131
+
132
+ describe '#content_type' do
133
+ it 'sets and gets a content_type' do
134
+ expect { subject.content_type = 'text/plain' }
135
+ .to change { subject.content_type }.to('text/plain')
136
+ end
137
+ end
138
+ end