summaryrefslogtreecommitdiff
path: root/test/models/node_test.rb
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-07-13 23:33:11 +0200
committererdgeist <erdgeist@erdgeist.org>2026-07-13 23:33:11 +0200
commit8eab68f2c5a3c126e2fec2ecdfa7ace87ce0937b (patch)
treef446ebc26a7707c7b64a937aa51a155df146c80a /test/models/node_test.rb
parent42714c697273a7117c6b355fab26c8c35e336ad1 (diff)
parentcdf5d9941ca866d437612d2f863eac6eb0b3db12 (diff)
Merge branch 'erdgeist-revive-events'
Diffstat (limited to 'test/models/node_test.rb')
-rw-r--r--test/models/node_test.rb369
1 files changed, 364 insertions, 5 deletions
diff --git a/test/models/node_test.rb b/test/models/node_test.rb
index 514ba3f..91a4a6d 100644
--- a/test/models/node_test.rb
+++ b/test/models/node_test.rb
@@ -98,6 +98,7 @@ class NodeTest < ActiveSupport::TestCase
98 assert_not_nil node.draft 98 assert_not_nil node.draft
99 assert_nil node.draft.user 99 assert_nil node.draft.user
100 assert_nil node.head 100 assert_nil node.head
101 assert_nil node.autosave
101 end 102 end
102 103
103 def test_create_new_draft_of_published_page 104 def test_create_new_draft_of_published_page
@@ -110,15 +111,19 @@ class NodeTest < ActiveSupport::TestCase
110 node.publish_draft! 111 node.publish_draft!
111 assert_not_nil node.find_or_create_draft( @user1 ) 112 assert_not_nil node.find_or_create_draft( @user1 )
112 end 113 end
113 114
114 def test_find_or_create_draft_if_draft_exists_and_is_owned_by_user 115 def test_find_or_create_draft_if_draft_exists_and_is_owned_by_user
115 node = Node.root.children.create :slug => "xyz" 116 node = Node.root.children.create :slug => "xyz"
116 node.publish_draft! 117 node.publish_draft!
117 118
118 node.find_or_create_draft @user1 119 first_call = node.find_or_create_draft @user1
119 node.find_or_create_draft @user1 120 second_call = node.find_or_create_draft @user1
121
122 assert_equal first_call, second_call
123 assert_equal 2, node.pages.count
124 assert_equal @user1, node.lock_owner
120 end 125 end
121 126
122 def test_exception_if_draft_exists_but_locked_by_another_user 127 def test_exception_if_draft_exists_but_locked_by_another_user
123 node = Node.root.children.create :slug => "xyz" 128 node = Node.root.children.create :slug => "xyz"
124 node.publish_draft! 129 node.publish_draft!
@@ -280,6 +285,191 @@ class NodeTest < ActiveSupport::TestCase
280 node = Node.root.children.create( :slug => "wow" ) 285 node = Node.root.children.create( :slug => "wow" )
281 assert_nil node.draft.published_at 286 assert_nil node.draft.published_at
282 end 287 end
288
289 test "lock_for_editing! acquires the lock without creating a draft or autosave" do
290 node = create_node_with_published_page
291
292 node.lock_for_editing!(@user1)
293
294 assert_equal @user1, node.lock_owner
295 assert_nil node.draft
296 assert_nil node.autosave
297 end
298
299 test "autosave! creates a buffer that never appears among a node's pages, leaving the draft untouched" do
300 node = create_node_with_draft
301 node.lock_for_editing!(@user1)
302 page_count_before = node.pages.count
303
304 node.autosave!({ :title => "in progress" }, @user1)
305 node.reload
306
307 assert_not_nil node.autosave
308 assert_nil node.autosave.node_id
309 assert_equal page_count_before, node.pages.count
310 assert_not_equal "in progress", node.draft.title
311 end
312
313 test "save_draft! promotes an autosave into an existing draft without creating a new revision" do
314 node = create_node_with_draft
315 node.lock_for_editing!(@user1)
316 node.autosave!({ :title => "in progress" }, @user1)
317 page_count_before = node.pages.count
318
319 node.save_draft!(@user1)
320 node.reload
321
322 assert_nil node.autosave
323 assert_equal "in progress", node.draft.title
324 assert_equal page_count_before, node.pages.count
325 end
326
327 test "save_draft! promotes an autosave into a brand new, correctly-revisioned draft when none exists" do
328 node = create_node_with_published_page
329 head_revision = node.head.revision
330
331 node.lock_for_editing!(@user1)
332 node.autosave!({ :title => "updated version" }, @user1)
333 node.reload
334
335 assert_nil node.draft
336 assert_nil node.autosave.node_id
337
338 node.save_draft!(@user1)
339 node.reload
340
341 assert_not_nil node.draft
342 assert_equal head_revision + 1, node.draft.revision
343 assert_equal head_revision, node.head.revision
344 assert_nil node.autosave
345 assert_equal 2, node.pages.count
346 assert_equal node.head.user, node.draft.user
347 assert_equal @user1, node.draft.editor
348 assert_equal node.head.published_at, node.draft.published_at
349 end
350
351 test "autosave!, save_draft!, and lock_for_editing! raise LockedByAnotherUser for a second user" do
352 node = create_node_with_published_page
353 node.lock_for_editing!(@user1)
354
355 assert_raise(LockedByAnotherUser) { node.autosave!({ :title => "x" }, @user2) }
356 assert_raise(LockedByAnotherUser) { node.save_draft!(@user2) }
357 assert_raise(LockedByAnotherUser) { node.lock_for_editing!(@user2) }
358
359 assert_equal @user1, node.reload.lock_owner
360 end
361
362 test "wipe_draft! leaves a fresh autosave and its lock untouched" do
363 node = create_node_with_published_page
364 node.lock_for_editing!(@user1)
365 node.autosave!({ :title => "still typing" }, @user1)
366
367 node.wipe_draft!
368 node.reload
369
370 assert_not_nil node.autosave
371 assert_equal @user1, node.lock_owner
372 end
373
374 test "wipe_draft! destroys a stale, orphaned autosave and releases its lock" do
375 node = create_node_with_published_page
376 node.lock_for_editing!(@user1)
377 node.autosave!({ :title => "abandoned mid-session" }, @user1)
378 node.autosave.update_column(:updated_at, 2.days.ago)
379
380 node.wipe_draft!
381 node.reload
382
383 assert_nil node.autosave
384 assert_nil node.lock_owner
385 end
386
387 test "revert! is a safe no-op on a fresh node with only a draft" do
388 node = create_node_with_draft
389 node.lock_for_editing!(@user1)
390
391 node.revert!(@user1)
392 node.reload
393
394 assert_not_nil node.draft
395 assert_equal @user1, node.lock_owner
396 end
397
398 test "revert! discards an autosave on a fresh node without touching its only draft" do
399 node = create_node_with_draft
400 node.lock_for_editing!(@user1)
401 node.autosave!({ :title => "typing" }, @user1)
402
403 node.revert!(@user1)
404 node.reload
405
406 assert_nil node.autosave
407 assert_not_nil node.draft
408 assert_equal @user1, node.lock_owner
409 end
410
411 test "revert! does nothing when a published node has no draft or autosave" do
412 node = create_node_with_published_page
413 node.lock_for_editing!(@user1)
414
415 node.revert!(@user1)
416 node.reload
417
418 assert_not_nil node.head
419 assert_nil node.draft
420 end
421
422 test "revert! discards a fresh autosave and releases the lock when no draft exists" do
423 node = create_node_with_published_page
424 node.lock_for_editing!(@user1)
425 node.autosave!({ :title => "in progress" }, @user1)
426
427 node.revert!(@user1)
428 node.reload
429
430 assert_nil node.autosave
431 assert_nil node.draft
432 assert_nil node.lock_owner
433 end
434
435 test "revert! destroys an existing draft and releases the lock" do
436 node = create_node_with_published_page
437 head_title = node.head.title
438 node.lock_for_editing!(@user1)
439 node.autosave!({ :title => "second version" }, @user1)
440 node.save_draft!(@user1)
441
442 node.revert!(@user1)
443 node.reload
444
445 assert_nil node.draft
446 assert_equal head_title, node.head.title
447 assert_nil node.lock_owner
448 end
449
450 test "revert! discards only the autosave when a draft survives beneath it" do
451 node = create_node_with_published_page
452 node.lock_for_editing!(@user1)
453 node.autosave!({ :title => "second version" }, @user1)
454 node.save_draft!(@user1)
455 node.autosave!({ :title => "third version, still typing" }, @user1)
456
457 node.revert!(@user1)
458 node.reload
459
460 assert_nil node.autosave
461 assert_not_nil node.draft
462 assert_equal "second version", node.draft.title
463 assert_equal @user1, node.lock_owner
464 end
465
466 test "revert! raises LockedByAnotherUser for a non-owner" do
467 node = create_node_with_published_page
468 node.lock_for_editing!(@user1)
469
470 assert_raise(LockedByAnotherUser) { node.revert!(@user2) }
471 assert_equal @user1, node.reload.lock_owner
472 end
283 473
284 def create_revisions node, count 474 def create_revisions node, count
285 count.times do 475 count.times do
@@ -287,4 +477,173 @@ class NodeTest < ActiveSupport::TestCase
287 node.publish_draft! 477 node.publish_draft!
288 end 478 end
289 end 479 end
480
481 test "available_layer_pairs matches the six-state table" do
482 node = Node.root.children.create!(:slug => "layer_pairs_test")
483 user = @user1 || User.find_by_login("aaron")
484
485 assert_equal [[:draft, :autosave]], (node.lock_for_editing!(user); node.autosave!({title: "v1"}, user); node.available_layer_pairs) # state F
486
487 node.save_draft!(user)
488 node.publish_draft!
489 assert_equal [], node.available_layer_pairs # state A
490
491 node.lock_for_editing!(user)
492 node.autosave!({title: "v2"}, user)
493 assert_equal [[:head, :autosave]], node.available_layer_pairs # state B
494
495 node.save_draft!(user)
496 assert_equal [[:head, :draft]], node.available_layer_pairs # state C
497
498 node.lock_for_editing!(user)
499 node.autosave!({title: "v3"}, user)
500 assert_equal [[:head, :draft], [:draft, :autosave]], node.available_layer_pairs # state D
501 end
502
503 test "publishing a staged move under one's own descendant is rejected, not allowed to crash" do
504 a = Node.root.children.create!(:slug => "cycle_guard_a")
505 b = a.children.create!(:slug => "cycle_guard_b")
506
507 a.staged_parent_id = b.id
508
509 assert_raises(ActiveRecord::RecordInvalid) { a.publish_draft! }
510
511 a.reload
512 assert_equal Node.root.id, a.parent_id
513 end
514
515 test "editor_search matches a partial substring, not just a whole word" do
516 node = Node.root.children.create!(:slug => "editor_search_substring_test")
517 node.find_or_create_draft(@user1)
518 node.draft.update(:title => "Biometrics Conference")
519 node.publish_draft!
520
521 assert_includes Node.editor_search("bio"), node
522 assert_includes Node.editor_search("Conf"), node
523 end
524
525 test "editor_search returns an empty relation for a blank term, not every node" do
526 assert_equal 0, Node.editor_search("").count
527 assert_equal 0, Node.editor_search(nil).count
528 assert_equal 0, Node.editor_search(" ").count
529 end
530
531 test "editor_search requires every word to match, but each word can match a different field" do
532 node = Node.root.children.create!(:slug => "editor_search_multiword_test")
533 node.find_or_create_draft(@user1)
534 node.draft.update(:title => "Backspace e.V. Bamberg", :abstract => "Spiegelgraben 41, 96052 Bamberg")
535 node.publish_draft!
536
537 assert_includes Node.editor_search("Backspace Spiegelgraben"), node
538 assert_equal 0, Node.editor_search("Backspace Nonexistentstreet").count
539 end
540
541 test "drafts_and_autosaves without a user sorts by recency only" do
542 older = Node.root.children.create!(:slug => "drafts_order_older")
543 older.find_or_create_draft(@user1)
544 newer = Node.root.children.create!(:slug => "drafts_order_newer")
545 newer.find_or_create_draft(@user1)
546
547 result = Node.drafts_and_autosaves.to_a
548 assert result.index(newer) < result.index(older)
549 end
550
551 test "drafts_and_autosaves with a user puts their own locked nodes first, regardless of recency" do
552 mine = Node.root.children.create!(:slug => "drafts_order_mine")
553 mine.lock_for_editing!(@user1)
554 mine.autosave!({:title => "mine"}, @user1)
555
556 someone_elses_newer = Node.root.children.create!(:slug => "drafts_order_theirs")
557 other_user = User.find_by_login("quentin")
558 someone_elses_newer.lock_for_editing!(other_user)
559 someone_elses_newer.autosave!({:title => "theirs"}, other_user)
560
561 result = Node.drafts_and_autosaves(current_user_id: @user1.id).to_a
562 assert result.index(mine) < result.index(someone_elses_newer)
563 end
564
565 test "recently_changed includes a node whose head was recently published" do
566 node = Node.root.children.create!(:slug => "recent_changed_published")
567 node.find_or_create_draft(@user1)
568 node.autosave!({:title => "v1"}, @user1)
569 node.save_draft!(@user1)
570 node.publish_draft!
571
572 assert_includes Node.recently_changed, node
573 end
574
575 test "recently_changed excludes a node only touched by locking or unlocking after an old publish" do
576 node = Node.root.children.create!(:slug => "recent_changed_lock_only")
577 node.find_or_create_draft(@user1)
578 node.autosave!({:title => "v1"}, @user1)
579 node.save_draft!(@user1)
580 node.publish_draft!
581 node.head.update_column(:updated_at, 20.days.ago)
582
583 node.lock_for_editing!(@user1)
584 node.unlock!
585
586 assert_not_includes Node.recently_changed, node
587 end
588
589 test "autosave! carries over the current related assets to the newly created autosave row" do
590 node = Node.root.children.create!(:slug => "autosave_asset_carryover_test")
591 user = User.find_by_login("quentin")
592 asset = Asset.create!(:name => "carryover-photo", :upload_content_type => "image/png")
593 node.draft.assets << asset
594
595 node.lock_for_editing!(user)
596 node.autosave!({:title => "v1"}, user)
597
598 assert_includes node.autosave.reload.assets, asset
599 end
600
601 test "autosave! does not reset assets already attached directly to an existing autosave" do
602 node = Node.root.children.create!(:slug => "autosave_asset_no_reset_test")
603 user = User.find_by_login("quentin")
604 original = Asset.create!(:name => "original-photo", :upload_content_type => "image/png")
605 extra = Asset.create!(:name => "extra-photo", :upload_content_type => "image/png")
606 node.draft.assets << original
607
608 node.lock_for_editing!(user)
609 node.autosave!({:title => "v1"}, user)
610 node.autosave.assets << extra
611
612 node.autosave!({:title => "v2"}, user)
613
614 assert_includes node.autosave.reload.assets, original
615 assert_includes node.autosave.reload.assets, extra
616 end
617
618 test "autosave! carries over other-locale translations to the newly created autosave row" do
619 node = Node.root.children.create!(:slug => "autosave_translation_carryover_test")
620 user = User.find_by_login("quentin")
621
622 Globalize.with_locale(:en) { node.draft.update!(:title => "English title") }
623
624 node.lock_for_editing!(user)
625 Globalize.with_locale(:de) { node.autosave!({:title => "German edit"}, user) }
626
627 autosave = node.autosave.reload
628 assert_includes autosave.translated_locales, :en
629 assert_equal "English title", Globalize.with_locale(:en) { autosave.title }
630 assert_equal "German edit", Globalize.with_locale(:de) { autosave.title }
631 end
632
633 test "autosave! does not reset other-locale translations already attached directly to an existing autosave" do
634 node = Node.root.children.create!(:slug => "autosave_translation_no_reset_test")
635 user = User.find_by_login("quentin")
636
637 Globalize.with_locale(:en) { node.draft.update!(:title => "Original English title") }
638
639 node.lock_for_editing!(user)
640 Globalize.with_locale(:de) { node.autosave!({:title => "v1"}, user) }
641 Globalize.with_locale(:en) { node.autosave.update!(:title => "Edited directly on autosave") }
642
643 Globalize.with_locale(:de) { node.autosave!({:title => "v2"}, user) }
644
645 autosave = node.autosave.reload
646 assert_equal "Edited directly on autosave", Globalize.with_locale(:en) { autosave.title }
647 assert_equal "v2", Globalize.with_locale(:de) { autosave.title }
648 end
290end 649end