safety-pin 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/spec/node_spec.rb ADDED
@@ -0,0 +1,712 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe SafetyPin::Node do
4
+ describe ".find" do
5
+ context "given a node name" do
6
+ context "that exists" do
7
+ it "should return a node with a matching path" do
8
+ SafetyPin::Node.find("/content").path.should eql("/content")
9
+ end
10
+ end
11
+
12
+ context "that doesn't exist" do
13
+ it "should return nil" do
14
+ SafetyPin::Node.find("/foo/bar/baz").should be_nil
15
+ end
16
+ end
17
+ end
18
+
19
+ it "complains if the path isn't an absolute path" do
20
+ lambda { node = SafetyPin::Node.find("content") }.should raise_exception(ArgumentError)
21
+ end
22
+ end
23
+
24
+ describe ".find_or_create" do
25
+ context "given a node path that exists" do
26
+ it "should return the node at that path" do
27
+ SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
28
+ SafetyPin::Node.find_or_create("/content/foo").path.should eql "/content/foo"
29
+ end
30
+ end
31
+
32
+ context "when node doesn't exist" do
33
+ it "returns node created at path" do
34
+ SafetyPin::Node.find("/content/foo").should be_nil
35
+ SafetyPin::Node.find_or_create("/content/foo").path.should == "/content/foo"
36
+ end
37
+ end
38
+ end
39
+
40
+ describe ".exists?" do
41
+ it "returns true if node exists at path" do
42
+ SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
43
+ SafetyPin::Node.exists?("/content/foo").should be_true
44
+ end
45
+
46
+ it "returns false if node does not exist" do
47
+ SafetyPin::Node.exists?("/content/foo").should be_false
48
+ end
49
+ end
50
+
51
+ describe ".session" do
52
+ it "should return a session" do
53
+ SafetyPin::Node.session.should be_a(Java::JavaxJcr::Session)
54
+ end
55
+ end
56
+
57
+ describe "#session" do
58
+ it "should return a session" do
59
+ SafetyPin::Node.find("/content").session.should be_a(Java::JavaxJcr::Session)
60
+ end
61
+
62
+ it "should cache session in an instance variable" do
63
+ node = SafetyPin::Node.find("/content")
64
+ node.session
65
+ node.instance_eval { @session }.should be_a(Java::JavaxJcr::Session)
66
+ end
67
+ end
68
+
69
+ describe "#children" do
70
+ it "should return an array of child nodes" do
71
+ SafetyPin::Node.find("/content").children.first.should be_a(SafetyPin::Node)
72
+ end
73
+ end
74
+
75
+ describe "#child" do
76
+ context "given a node name" do
77
+ let(:node) { SafetyPin::Node.find("/") }
78
+
79
+ context "that exists" do
80
+ it "should return a child node with a matching name" do
81
+ node.child("content").name.should eql("content")
82
+ end
83
+
84
+ it "should return a grandchild node given a relative path" do
85
+ SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
86
+ node.child("content/foo").name.should eql("foo")
87
+ end
88
+ end
89
+
90
+ it "should coerce non-string name to string and return child" do
91
+ node.child(:content).name.should == "content"
92
+ end
93
+
94
+ context "that doesn't exist" do
95
+ it "should return nil" do
96
+ node.child("foobarbaz").should be_nil
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ describe ".create_or_update" do
103
+ let(:node_blueprint) { SafetyPin::NodeBlueprint.new(:path => "/content/foo") }
104
+
105
+ it "calls Node.create with arg when nothing exists at path" do
106
+ SafetyPin::Node.should_receive(:create).with(node_blueprint)
107
+ SafetyPin::Node.create_or_update(node_blueprint)
108
+ end
109
+
110
+ it "calls Node.update with arg when node exists at path" do
111
+ SafetyPin::Node.create(node_blueprint)
112
+ SafetyPin::Node.should_receive(:update).with(node_blueprint)
113
+ SafetyPin::Node.create_or_update(node_blueprint)
114
+ end
115
+
116
+ it "takes a node blueprint" do
117
+ SafetyPin::Node.create_or_update(node_blueprint)
118
+ SafetyPin::Node.exists?(node_blueprint.path).should be_true
119
+ end
120
+
121
+ it "takes an array of node blueprints" do
122
+ node_blueprints = [node_blueprint, SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar")]
123
+ SafetyPin::Node.create_or_update(node_blueprints)
124
+ node_blueprints.each {|node_blueprint| SafetyPin::Node.exists?(node_blueprint.path).should be_true }
125
+ end
126
+ end
127
+
128
+ describe "#find_or_create_child" do
129
+ let(:parent) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
130
+
131
+ context "an existing node path" do
132
+ it "should return the child node" do
133
+ parent.create("bar")
134
+ parent.find_or_create("bar").path.should eql "/content/foo/bar"
135
+ end
136
+ end
137
+
138
+ context "a non-existing node path" do
139
+ it "should create a node and return it" do
140
+ parent.find_or_create("bar").path.should eql "/content/foo/bar"
141
+ end
142
+ end
143
+ end
144
+
145
+ describe "#name" do
146
+ it "should return a string name" do
147
+ SafetyPin::Node.find("/content").name.should eql("content")
148
+ end
149
+ end
150
+
151
+ describe "#save" do
152
+ context "on an existing node with changes" do
153
+ before do
154
+ @node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
155
+ @node["bar"] = "baz"
156
+ end
157
+
158
+ it "should save the changes to the JCR" do
159
+ @node.save
160
+ @node.reload
161
+ @node["bar"].should eql("baz")
162
+ end
163
+
164
+ it "should return true if the save was successful" do
165
+ save_successful = @node.save
166
+ (save_successful and not @node.changed?).should be_true
167
+ end
168
+ end
169
+
170
+ context "on a new node" do
171
+ it "should save the node" do
172
+ node = SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
173
+ node.save.should be_true
174
+ end
175
+
176
+ it "should save changes in parent node" do
177
+ parent_node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
178
+ node = SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar"))
179
+ parent_node["baz"] = "qux"
180
+ parent_node.should be_changed
181
+ node.save
182
+ parent_node.should_not be_changed
183
+ end
184
+ end
185
+ end
186
+
187
+ describe "#read_attribute" do
188
+ context "on an existing node" do
189
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
190
+
191
+ it "should return the string value of a string property" do
192
+ node["foo"] = "bar"
193
+ node.read_attribute("foo").should eql("bar")
194
+ end
195
+
196
+ it "should return the boolean value of a boolean property" do
197
+ node["foo"] = true
198
+ node.read_attribute("foo").should eql(true)
199
+ end
200
+
201
+ it "should return the double value of a double (or Ruby float) property" do
202
+ node["foo"] = 3.14
203
+ node.read_attribute("foo").should eql(3.14)
204
+ end
205
+
206
+ it "should return the long value of a long (or Ruby Fixnum) property" do
207
+ node["foo"] = 42
208
+ node.read_attribute("foo").should eql(42)
209
+ end
210
+
211
+ it "should return the time value of a date property" do
212
+ time = Time.now
213
+ node["foo"] = time
214
+ node.read_attribute("foo").to_s.should eql(time.to_s)
215
+ end
216
+
217
+ context "given a multi-value property" do
218
+ it "should return an array of values" do
219
+ node["foo"] = ["one", "two"]
220
+ node.read_attribute("foo").should eql(["one", "two"])
221
+ end
222
+ end
223
+
224
+ context "given a non-string name" do
225
+ it "should co-erce the name into a string and retrieve the property" do
226
+ node["foo"] = "bar"
227
+ node.read_attribute(:foo).should eql("bar")
228
+ end
229
+ end
230
+ end
231
+
232
+ it "should return the string value of a name property" do
233
+ SafetyPin::Node.find("/")["jcr:primaryType"].should eql("rep:root")
234
+ end
235
+
236
+ it "should throw an exception when accessing a non-existent (nil) property" do
237
+ lambda { SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo")).read_attribute("foo-bar-baz") }.should raise_error(SafetyPin::NilPropertyError)
238
+ end
239
+ end
240
+
241
+ context "#write_attribute" do
242
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
243
+
244
+ context "given a single value" do
245
+ it "should set a string property value" do
246
+ node.write_attribute("foo", "bar")
247
+ node.save
248
+ node.reload
249
+ node["foo"].should eql("bar")
250
+ end
251
+
252
+ context "given a Time object value" do
253
+ it "should set a date property value" do
254
+ time = Time.now
255
+ node.write_attribute("foo", time)
256
+ node.save
257
+ node.reload
258
+ node["foo"].to_s.should eql(time.to_s)
259
+ end
260
+ end
261
+
262
+ context "given a symbol value" do
263
+ it "coerces value to string" do
264
+ node.write_attribute("foo", :bar)
265
+ node["foo"].should == "bar"
266
+ end
267
+ end
268
+
269
+ context "given another supported value" do
270
+ it "sets the property" do
271
+ node.write_attribute("foo", 1)
272
+ node["foo"].should == 1
273
+ node.write_attribute("foo", 1.1)
274
+ node["foo"].should == 1.1
275
+ node.write_attribute("foo", true)
276
+ node["foo"].should == true
277
+ node.write_attribute("foo", "bar")
278
+ node["foo"].should == "bar"
279
+ end
280
+ end
281
+
282
+ context "given an unsupported value" do
283
+ it "raises an error" do
284
+ lambda { node.write_attribute("foo", {unsupported: :value_type}) }.should raise_error(SafetyPin::PropertyTypeError)
285
+ end
286
+ end
287
+
288
+ context "given a non-string name" do
289
+ it "should co-erce name into string before setting property" do
290
+ node.write_attribute(:foo, "bar")
291
+ node.save
292
+ node.reload
293
+ node["foo"].should eql("bar")
294
+ end
295
+ end
296
+ end
297
+
298
+ context "given an array of values" do
299
+ context "of the same type" do
300
+ it "should set a multi-value string array" do
301
+ node.write_attribute("foo", ["one", "two"])
302
+ node.save
303
+ node.reload
304
+ node["foo"].should eql(["one", "two"])
305
+ end
306
+ end
307
+ end
308
+
309
+ context "given a null value" do
310
+ it "should remove the property" do
311
+ node["foo"] = "bar"
312
+ node.write_attribute("foo", nil)
313
+ lambda { node["foo"] }.should raise_error(SafetyPin::NilPropertyError)
314
+ end
315
+
316
+ context "given a non-existent property and a null value" do
317
+ it "should return nil" do
318
+ node.write_attribute("foo", nil).should be_nil
319
+ end
320
+ end
321
+ end
322
+
323
+ context "changing jcr:primaryType property" do
324
+ it "should raise an error" do
325
+ lambda { node.write_attribute("jcr:primaryType", "nt:folder") }.should raise_error(SafetyPin::PropertyError)
326
+ end
327
+ end
328
+ end
329
+
330
+ context "#reload" do
331
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
332
+
333
+ it "should discard pending changes" do
334
+ node["foo"] = "bar"
335
+ node.reload
336
+ lambda { node.read_attribute("foo") }.should raise_error(SafetyPin::NilPropertyError)
337
+ end
338
+
339
+ it "should not discard changes for another node" do
340
+ node["bar"] = "baz"
341
+ another_node = SafetyPin::Node.find("/content")
342
+ another_node["bar"] = "baz"
343
+ node.reload
344
+ lambda { node["bar"] }.should raise_error(SafetyPin::NilPropertyError)
345
+ another_node["bar"].should eql("baz")
346
+ end
347
+ end
348
+
349
+ describe "#[]" do
350
+ it "should return the value of a given property name" do
351
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
352
+ node.write_attribute("bar","baz")
353
+ node.save
354
+ node["bar"].should eql("baz")
355
+ end
356
+ end
357
+
358
+ describe "#[]=" do
359
+ it "should set the value of a given property name" do
360
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
361
+ node.write_attribute("bar","baz")
362
+ node["bar"] = "qux"
363
+ node["bar"].should eql("qux")
364
+ node.destroy
365
+ end
366
+ end
367
+
368
+ context "#changed?" do
369
+ let(:node) { SafetyPin::Node.find("/content") }
370
+
371
+ it "should return false if the node does not have unsaved changes" do
372
+ node.should_not be_changed
373
+ end
374
+
375
+ it "should return true if the node has unsaved changes" do
376
+ node["foo"] = "bar"
377
+ node.should be_changed
378
+ end
379
+ end
380
+
381
+ context "#new?" do
382
+ it "should return true if node has never been saved to JCR" do
383
+ SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo")).should be_new
384
+ end
385
+
386
+ it "should return false if node has been saved to JCR" do
387
+ SafetyPin::Node.find("/content").should_not be_new
388
+ end
389
+ end
390
+
391
+ describe "#properties" do
392
+ it "should return hash of all unprotected properties" do
393
+ SafetyPin::Node.find("/").properties.should eql({"sling:target"=>"/index.html", "sling:resourceType"=>"sling:redirect"})
394
+ end
395
+ end
396
+
397
+ describe "#properties=" do
398
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
399
+
400
+ it "should set the properties of a node" do
401
+ node.properties = {"foo" => "bar"}
402
+ node.properties.should == {"foo" => "bar"}
403
+ end
404
+
405
+ it "should set unset properties not specified in hash" do
406
+ node["foo"] = "bar"
407
+ node.properties = {"baz" => "qux"}
408
+ node.properties.should eql({"baz" => "qux"})
409
+ end
410
+
411
+ it "creates child nodes for node blueprints" do
412
+ node_blueprint = SafetyPin::NodeBlueprint.new(:path => "/this/path/gets/thrown/away",
413
+ :primary_type => "sling:OrderedFolder",
414
+ :properties => {"bar" => "baz"})
415
+ node.properties = {"foo" => node_blueprint}
416
+ node.child("foo").properties.should == {"bar" => "baz"}
417
+ node.child("foo").primary_type.should == "sling:OrderedFolder"
418
+ end
419
+
420
+ xit "updates child nodes when they already exist" do
421
+ # Create /content/foo/bar and /content/foo/bar/baz
422
+ node.create(:bar, "nt:unstructured", {"bar" => "baz"})
423
+ node.child(:bar).create(:baz)
424
+ node.child(:bar).child(:baz).path.should == "/content/foo/bar/baz"
425
+ # Update /content/foo/bar by updating /content/foo properties
426
+ node.properties = {bar: SafetyPin::NodeBlueprint.new(:path => "/this/path/gets/thrown/away", :primary_type => "sling:OrderedFolder", :properties => {"updated" => "props"})}
427
+ node.child(:bar).properties.should == {"updated" => "props"}
428
+ node.child(:bar).primary_type.should == "sling:OrderedFolder"
429
+ node.child(:bar).child(:baz).path.should == "/content/foo/bar/baz"
430
+ end
431
+ end
432
+
433
+ describe "#protected_properties" do
434
+ it "should return hash of all protected properties" do
435
+ SafetyPin::Node.find("/").protected_properties.should eql({"jcr:primaryType"=>"rep:root", "jcr:mixinTypes"=>["rep:AccessControllable", "rep:RepoAccessControllable"]})
436
+ end
437
+ end
438
+
439
+ describe "#mixin_types" do
440
+ it "should return the mixin types of a node" do
441
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
442
+ node.j_node.add_mixin("mix:created")
443
+ node.save
444
+ node.mixin_types.should eql(["mix:created"])
445
+ end
446
+ end
447
+
448
+ describe "#add_mixin" do
449
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
450
+
451
+ it "should add a mixin type to node" do
452
+ node.add_mixin("mix:created")
453
+ node.save
454
+ node.mixin_types.should eql(["mix:created"])
455
+ end
456
+
457
+ it "should require a save before the mixin addition is detected" do
458
+ node.add_mixin("mix:created")
459
+ node.mixin_types.should eql([])
460
+ end
461
+ end
462
+
463
+ describe "#remove_mixin" do
464
+ let(:node) do
465
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
466
+ node.add_mixin("mix:created")
467
+ node.save
468
+ node
469
+ end
470
+
471
+ it "should remove a mixin type from a node" do
472
+ node.mixin_types.should eql(["mix:created"])
473
+ node.remove_mixin("mix:created")
474
+ node.save
475
+ node.mixin_types.should eql([])
476
+ end
477
+
478
+ it "should require a save before the mixin removal is detected" do
479
+ node.remove_mixin("mix:created")
480
+ node.mixin_types.should eql(["mix:created"])
481
+ node.reload
482
+ end
483
+ end
484
+
485
+ describe ".update" do
486
+ let(:node) do
487
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
488
+ node.save
489
+ node
490
+ end
491
+
492
+ it "updates a nodes properties" do
493
+ SafetyPin::Node.update(SafetyPin::NodeBlueprint.new(:path => node.path, :properties => {"foo" => "barbazbuzzzzz"}))
494
+ SafetyPin::Node.find(node.path).properties.should == {"foo" => "barbazbuzzzzz"}
495
+ end
496
+
497
+ it "preserves node children" do
498
+ node.create(:bar)
499
+ SafetyPin::Node.update(SafetyPin::NodeBlueprint.new(:path => node.path, :primary_type => "sling:OrderedFolder", :properties => {"foo" => "bar"}))
500
+ SafetyPin::Node.exists?("/content/foo/bar").should be_true
501
+ end
502
+
503
+ it "modifies the primary type" do
504
+ SafetyPin::Node.update(SafetyPin::NodeBlueprint.new(:path => node.path, :primary_type => "sling:OrderedFolder"))
505
+ SafetyPin::Node.find(node.path).primary_type.should == "sling:OrderedFolder"
506
+ end
507
+ end
508
+
509
+ describe "#primary_type=" do
510
+ let(:node) do
511
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
512
+ node.save
513
+ node
514
+ end
515
+
516
+ it "sets the primary type" do
517
+ node.primary_type.should == "nt:unstructured"
518
+ node.primary_type = "sling:OrderedFolder"
519
+ node.save
520
+ SafetyPin::Node.find(node.path).primary_type.should == "sling:OrderedFolder"
521
+ end
522
+ end
523
+
524
+ describe ".build" do
525
+ it "returns an unsaved node at path of a specified type with properties set" do
526
+ node = SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo", :primary_type => "sling:OrderedFolder", :properties => {"foo" => "bar"}))
527
+ node.should be_new
528
+ node.primary_type.should == "sling:OrderedFolder"
529
+ node.properties.should == {"foo" => "bar"}
530
+ end
531
+
532
+ it "complains when the nodes already exists" do
533
+ node_blueprint = SafetyPin::NodeBlueprint.new(:path => "/content/foo")
534
+ SafetyPin::Node.create(node_blueprint)
535
+ lambda { SafetyPin::Node.build(node_blueprint) }.should raise_error(SafetyPin::NodeError)
536
+ end
537
+
538
+ it "complains when given a path with missing parents" do
539
+ lambda { SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar/baz/doesnt/exist")) }.should raise_error(SafetyPin::NodeError)
540
+ end
541
+
542
+ it "complains when given a relative path" do
543
+ lambda { SafetyPin::Node.build(SafetyPin::NodeBlueprint.new(:path => "foo/not/absolute")) }.should raise_error(SafetyPin::NodeError)
544
+ end
545
+
546
+ it "complains when given nil" do
547
+ lambda { SafetyPin::Node.build(nil) }.should raise_error(SafetyPin::NodeError)
548
+ end
549
+ end
550
+
551
+ describe ".create" do
552
+ it "creates a node" do
553
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
554
+ node.should be_a(SafetyPin::Node)
555
+ end
556
+
557
+ it "creates a node of a specific type" do
558
+ SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo", :primary_type => "sling:OrderedFolder"))
559
+ SafetyPin::Node.find("/content/foo").primary_type.should == "sling:OrderedFolder"
560
+ end
561
+ end
562
+
563
+ describe ".create_parents" do
564
+ it "creates parent nodes if they do not exist" do
565
+ SafetyPin::Node.create_parents("/content/foo/bar/baz")
566
+ SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar/baz")).should_not be_nil
567
+ end
568
+ end
569
+
570
+ context "#value_factory" do
571
+ it "should return a value factory instance" do
572
+ SafetyPin::Node.find("/content").value_factory.should be_a(Java::JavaxJcr::ValueFactory)
573
+ end
574
+ end
575
+
576
+ describe "#property_is_multi_valued" do
577
+ it "should return true if property is multi-valued" do
578
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
579
+ node["bar"] = ["baz", "qux"]
580
+ node.save
581
+ property = node.j_node.get_property("bar")
582
+ node.property_is_multi_valued?(property).should be_true
583
+ end
584
+
585
+ it "should return false if property is not multi-valued" do
586
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
587
+ node["bar"] = "baz"
588
+ node.save
589
+ property = node.j_node.get_property("bar")
590
+ node.property_is_multi_valued?(property).should be_false
591
+ end
592
+ end
593
+
594
+ describe "#destroy" do
595
+ it "should remove node from JCR" do
596
+ path = "/content/foo"
597
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => path))
598
+ node.destroy
599
+ SafetyPin::Node.find(path).should be_nil
600
+ end
601
+
602
+ it "should save changes in parent node" do
603
+ parent_node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
604
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo/bar"))
605
+ parent_node["baz"] = "qux"
606
+ parent_node.should be_changed
607
+ node.destroy
608
+ parent_node.should_not be_changed
609
+ end
610
+
611
+ context "when it fails" do
612
+ it "should raise an error" do
613
+ node = SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo"))
614
+ node.add_mixin("mix:created")
615
+ node.save
616
+ node.remove_mixin("mix:created") # make node unremoveable
617
+ lambda { node.destroy }.should raise_error(SafetyPin::NodeError)
618
+ end
619
+ end
620
+ end
621
+
622
+ describe "#primary_type" do
623
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
624
+
625
+ it "should return the primary type of the node" do
626
+ node.primary_type.should eql("nt:unstructured")
627
+ end
628
+ end
629
+
630
+ describe "#build" do
631
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
632
+
633
+ it "should create a child node with a given name" do
634
+ node.build("bar").path.should == "/content/foo/bar"
635
+ end
636
+
637
+ it "should create a child node with a given name and node type" do
638
+ child_node = node.build("bar", SafetyPin::NodeBlueprint.new(:primary_type => "nt:folder", :path => :no_path))
639
+ child_node.should be_a(SafetyPin::Node)
640
+ child_node.primary_type.should == "nt:folder"
641
+ end
642
+
643
+ it "should create a child node with a name, node type, and properties" do
644
+ node.build("bar", SafetyPin::NodeBlueprint.new(:path => :no_path, :properties => {foo: "bar"})).properties.should == {"foo" => "bar"}
645
+ end
646
+ end
647
+
648
+ describe "#create" do
649
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
650
+
651
+ it "should create a child node with a given name" do
652
+ child_node = node.create("bar")
653
+ child_node.path.should == "/content/foo/bar"
654
+ child_node.should_not
655
+ end
656
+
657
+ it "should create a child node with a given name and node type" do
658
+ child_node = node.create("bar", SafetyPin::NodeBlueprint.new(:path => :no_path, :primary_type => "nt:folder"))
659
+ child_node.should_not be_new
660
+ child_node.primary_type.should eql("nt:folder")
661
+ end
662
+
663
+ it "should create a child node with a name, node type, and properties" do
664
+ child_node = node.create("bar", SafetyPin::NodeBlueprint.new(:path => :no_path, :properties => {foo: "bar"}))
665
+ child_node.should_not be_new
666
+ child_node.properties.should == {"foo" => "bar"}
667
+ end
668
+ end
669
+
670
+ describe "#==" do
671
+ it "finds two nodes with the same path to be the same" do
672
+ node1 = SafetyPin::Node.new(double(:j_node))
673
+ node1.should_receive(:path).and_return("/content/foo")
674
+ node2 = SafetyPin::Node.new(double(:j_node))
675
+ node2.should_receive(:path).and_return("/content/foo")
676
+
677
+ node1.should == node2
678
+ end
679
+
680
+ it "finds two nodes with different paths to be different" do
681
+ node1 = SafetyPin::Node.new(double(:j_node))
682
+ node1.should_receive(:path).and_return("/content/foo")
683
+ node2 = SafetyPin::Node.new(double(:j_node))
684
+ node2.should_receive(:path).and_return("/content/foo/bar")
685
+
686
+ node1.should_not == node2
687
+ end
688
+
689
+ it "returns false when passed an object that doesn't response to path" do
690
+ node1 = SafetyPin::Node.new(double(:j_node))
691
+ node1.stub(:path => "foo")
692
+ node1.should_not == Object.new
693
+ end
694
+
695
+ it "returns false when passed a nil object" do
696
+ node1 = SafetyPin::Node.new(double(:j_node))
697
+ node1.should_not == nil
698
+ end
699
+ end
700
+
701
+ describe "#parent", :focus => true do
702
+ let(:node) { SafetyPin::Node.create(SafetyPin::NodeBlueprint.new(:path => "/content/foo")) }
703
+
704
+ it "returns the parent node" do
705
+ node.parent.should == SafetyPin::Node.find("/content")
706
+ end
707
+
708
+ it "raises an error when called on the root node" do
709
+ expect { SafetyPin::Node.find("/").parent }.to raise_error(SafetyPin::NodeError)
710
+ end
711
+ end
712
+ end