diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-07-23 19:45:47 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-07-23 19:45:47 +0200 |
| commit | aedf5778aba87c4e8d036dde2a0b6ec79cf8b342 (patch) | |
| tree | 059a8954c332d0a7c3e48e44906a4104d4338007 /test/controllers/related_assets_controller_test.rb | |
| parent | f993853db3e233f05a55de5ba2a87b77acf041aa (diff) | |
Keep in-editor asset curation off the head, layering it like every edit
ensure_autosave! gives body keystrokes and asset curation one shared
layer, so head is never mutated in place and every curation change
surfaces in the publish delta. Stale rendered join ids are mapped
across the clone via asset_id. Curation now requires holding the
lock; a missing lock answers 423, matching the autosave endpoint.
Diffstat (limited to 'test/controllers/related_assets_controller_test.rb')
| -rw-r--r-- | test/controllers/related_assets_controller_test.rb | 44 |
1 files changed, 36 insertions, 8 deletions
diff --git a/test/controllers/related_assets_controller_test.rb b/test/controllers/related_assets_controller_test.rb index ced4b74d..fd30dddd 100644 --- a/test/controllers/related_assets_controller_test.rb +++ b/test/controllers/related_assets_controller_test.rb | |||
| @@ -38,19 +38,24 @@ class RelatedAssetsControllerTest < ActionController::TestCase | |||
| 38 | test "create attaches an asset to the node's editable page" do | 38 | test "create attaches an asset to the node's editable page" do |
| 39 | login_as :quentin | 39 | login_as :quentin |
| 40 | node = Node.root.children.create!(:slug => "related_assets_create_test") | 40 | node = Node.root.children.create!(:slug => "related_assets_create_test") |
| 41 | node.lock_for_editing!(users(:quentin)) | ||
| 41 | asset = Asset.create!(:name => "erfa-photo", :upload_content_type => "image/png") | 42 | asset = Asset.create!(:name => "erfa-photo", :upload_content_type => "image/png") |
| 42 | 43 | ||
| 43 | post :create, params: { :node_id => node.id, :asset_id => asset.id } | 44 | post :create, params: { :node_id => node.id, :asset_id => asset.id } |
| 44 | 45 | ||
| 45 | assert_response :success | 46 | assert_response :success |
| 46 | assert_includes node.draft.reload.related_assets.map(&:asset_id), asset.id | 47 | current = node.reload.editable_page |
| 48 | assert_equal node.autosave, current, "mutation should have created an autosave layer" | ||
| 49 | assert_includes current.related_assets.map(&:asset_id), asset.id | ||
| 47 | json = JSON.parse(response.body) | 50 | json = JSON.parse(response.body) |
| 48 | assert json["url"].present? | 51 | assert json["url"].present? |
| 52 | assert_empty node.draft.reload.related_assets, "the draft must stay untouched" | ||
| 49 | end | 53 | end |
| 50 | 54 | ||
| 51 | test "create does not duplicate an already-attached asset" do | 55 | test "create does not duplicate an already-attached asset" do |
| 52 | login_as :quentin | 56 | login_as :quentin |
| 53 | node = Node.root.children.create!(:slug => "related_assets_dup_test") | 57 | node = Node.root.children.create!(:slug => "related_assets_dup_test") |
| 58 | node.lock_for_editing!(users(:quentin)) | ||
| 54 | asset = Asset.create!(:name => "erfa-photo-2", :upload_content_type => "image/png") | 59 | asset = Asset.create!(:name => "erfa-photo-2", :upload_content_type => "image/png") |
| 55 | node.draft.assets << asset | 60 | node.draft.assets << asset |
| 56 | 61 | ||
| @@ -63,19 +68,22 @@ class RelatedAssetsControllerTest < ActionController::TestCase | |||
| 63 | test "destroy removes the attached asset" do | 68 | test "destroy removes the attached asset" do |
| 64 | login_as :quentin | 69 | login_as :quentin |
| 65 | node = Node.root.children.create!(:slug => "related_assets_destroy_test") | 70 | node = Node.root.children.create!(:slug => "related_assets_destroy_test") |
| 71 | node.lock_for_editing!(users(:quentin)) | ||
| 66 | asset = Asset.create!(:name => "old-photo", :upload_content_type => "image/png") | 72 | asset = Asset.create!(:name => "old-photo", :upload_content_type => "image/png") |
| 67 | node.draft.assets << asset | 73 | node.draft.assets << asset |
| 68 | related = node.draft.related_assets.first | ||
| 69 | 74 | ||
| 75 | related = node.draft.related_assets.first | ||
| 70 | delete :destroy, params: { :node_id => node.id, :id => related.id } | 76 | delete :destroy, params: { :node_id => node.id, :id => related.id } |
| 71 | 77 | ||
| 72 | assert_response :success | 78 | assert_response :success |
| 73 | assert_equal 0, node.draft.reload.related_assets.count | 79 | assert_equal 0, node.reload.editable_page.related_assets.count |
| 80 | assert_equal 1, node.draft.reload.related_assets.count, "the draft must stay untouched" | ||
| 74 | end | 81 | end |
| 75 | 82 | ||
| 76 | test "update reorders the attached assets" do | 83 | test "update reorders the attached assets" do |
| 77 | login_as :quentin | 84 | login_as :quentin |
| 78 | node = Node.root.children.create!(:slug => "related_assets_reorder_test") | 85 | node = Node.root.children.create!(:slug => "related_assets_reorder_test") |
| 86 | node.lock_for_editing!(users(:quentin)) | ||
| 79 | first = Asset.create!(:name => "first-photo", :upload_content_type => "image/png") | 87 | first = Asset.create!(:name => "first-photo", :upload_content_type => "image/png") |
| 80 | second = Asset.create!(:name => "second-photo", :upload_content_type => "image/png") | 88 | second = Asset.create!(:name => "second-photo", :upload_content_type => "image/png") |
| 81 | node.draft.assets << first | 89 | node.draft.assets << first |
| @@ -85,13 +93,15 @@ class RelatedAssetsControllerTest < ActionController::TestCase | |||
| 85 | patch :update, params: { :node_id => node.id, :id => second_related.id, :position => 1 } | 93 | patch :update, params: { :node_id => node.id, :id => second_related.id, :position => 1 } |
| 86 | 94 | ||
| 87 | assert_response :success | 95 | assert_response :success |
| 88 | ordered_asset_ids = node.draft.reload.related_assets.map(&:asset_id) | 96 | ordered_asset_ids = node.reload.editable_page.related_assets.order(:position).map(&:asset_id) |
| 97 | # XXXX ordered_asset_ids = node.draft.reload.related_assets.map(&:asset_id) | ||
| 89 | assert_equal [second.id, first.id], ordered_asset_ids | 98 | assert_equal [second.id, first.id], ordered_asset_ids |
| 90 | end | 99 | end |
| 91 | 100 | ||
| 92 | test "update sets the headline flag" do | 101 | test "update sets the headline flag" do |
| 93 | login_as :quentin | 102 | login_as :quentin |
| 94 | node = Node.root.children.create!(:slug => "related_assets_headline_test") | 103 | node = Node.root.children.create!(:slug => "related_assets_headline_test") |
| 104 | node.lock_for_editing!(users(:quentin)) | ||
| 95 | asset = Asset.create!(:name => "headline-photo", :upload_content_type => "image/png") | 105 | asset = Asset.create!(:name => "headline-photo", :upload_content_type => "image/png") |
| 96 | node.draft.assets << asset | 106 | node.draft.assets << asset |
| 97 | related = node.draft.related_assets.find_by(:asset_id => asset.id) | 107 | related = node.draft.related_assets.find_by(:asset_id => asset.id) |
| @@ -99,12 +109,14 @@ class RelatedAssetsControllerTest < ActionController::TestCase | |||
| 99 | patch :update, params: { :node_id => node.id, :id => related.id, :headline => "true" } | 109 | patch :update, params: { :node_id => node.id, :id => related.id, :headline => "true" } |
| 100 | 110 | ||
| 101 | assert_response :success | 111 | assert_response :success |
| 102 | assert related.reload.headline? | 112 | assert node.reload.editable_page.related_assets.find_by!(:asset_id => asset.id).headline? |
| 113 | assert_not related.reload.headline?, "the draft must stay untouched" | ||
| 103 | end | 114 | end |
| 104 | 115 | ||
| 105 | test "update with headline=true clears any previous headline on the same page" do | 116 | test "update with headline=true clears any previous headline on the same page" do |
| 106 | login_as :quentin | 117 | login_as :quentin |
| 107 | node = Node.root.children.create!(:slug => "related_assets_headline_swap_test") | 118 | node = Node.root.children.create!(:slug => "related_assets_headline_swap_test") |
| 119 | node.lock_for_editing!(users(:quentin)) | ||
| 108 | first = Asset.create!(:name => "first-headline", :upload_content_type => "image/png") | 120 | first = Asset.create!(:name => "first-headline", :upload_content_type => "image/png") |
| 109 | second = Asset.create!(:name => "second-headline", :upload_content_type => "image/png") | 121 | second = Asset.create!(:name => "second-headline", :upload_content_type => "image/png") |
| 110 | node.draft.assets << first | 122 | node.draft.assets << first |
| @@ -117,13 +129,16 @@ class RelatedAssetsControllerTest < ActionController::TestCase | |||
| 117 | patch :update, params: { :node_id => node.id, :id => second_related.id, :headline => "true" } | 129 | patch :update, params: { :node_id => node.id, :id => second_related.id, :headline => "true" } |
| 118 | 130 | ||
| 119 | assert_response :success | 131 | assert_response :success |
| 120 | assert_not first_related.reload.headline? | 132 | current = node.reload.editable_page |
| 121 | assert second_related.reload.headline? | 133 | assert_not current.related_assets.find_by!(:asset_id => first.id).headline? |
| 134 | assert current.related_assets.find_by!(:asset_id => second.id).headline? | ||
| 135 | assert first_related.reload.headline?, "the draft must stay untouched" | ||
| 122 | end | 136 | end |
| 123 | 137 | ||
| 124 | test "update with headline=false clears the headline" do | 138 | test "update with headline=false clears the headline" do |
| 125 | login_as :quentin | 139 | login_as :quentin |
| 126 | node = Node.root.children.create!(:slug => "related_assets_headline_unset_test") | 140 | node = Node.root.children.create!(:slug => "related_assets_headline_unset_test") |
| 141 | node.lock_for_editing!(users(:quentin)) | ||
| 127 | asset = Asset.create!(:name => "unset-headline", :upload_content_type => "image/png") | 142 | asset = Asset.create!(:name => "unset-headline", :upload_content_type => "image/png") |
| 128 | node.draft.assets << asset | 143 | node.draft.assets << asset |
| 129 | related = node.draft.related_assets.find_by(:asset_id => asset.id) | 144 | related = node.draft.related_assets.find_by(:asset_id => asset.id) |
| @@ -132,7 +147,8 @@ class RelatedAssetsControllerTest < ActionController::TestCase | |||
| 132 | patch :update, params: { :node_id => node.id, :id => related.id, :headline => "false" } | 147 | patch :update, params: { :node_id => node.id, :id => related.id, :headline => "false" } |
| 133 | 148 | ||
| 134 | assert_response :success | 149 | assert_response :success |
| 135 | assert_not related.reload.headline? | 150 | assert_not node.reload.editable_page.related_assets.find_by!(:asset_id => asset.id).headline? |
| 151 | assert related.reload.headline?, "the draft must stay untouched" | ||
| 136 | end | 152 | end |
| 137 | 153 | ||
| 138 | test "search includes PDF assets as headline-eligible candidates" do | 154 | test "search includes PDF assets as headline-eligible candidates" do |
| @@ -159,4 +175,16 @@ class RelatedAssetsControllerTest < ActionController::TestCase | |||
| 159 | ids = JSON.parse(response.body).map { |r| r["id"] } | 175 | ids = JSON.parse(response.body).map { |r| r["id"] } |
| 160 | assert_includes ids, asset.id | 176 | assert_includes ids, asset.id |
| 161 | end | 177 | end |
| 178 | |||
| 179 | test "curation without holding the lock is refused with 423" do | ||
| 180 | login_as :quentin | ||
| 181 | node = Node.root.children.create!(:slug => "curation_lock_test") | ||
| 182 | asset = Asset.create!(:name => "Untouchable", :upload_content_type => "image/png") | ||
| 183 | node.lock_for_editing!(users(:aaron)) | ||
| 184 | |||
| 185 | post :create, params: { :node_id => node.id, :asset_id => asset.id } | ||
| 186 | |||
| 187 | assert_response :locked | ||
| 188 | assert_empty node.draft.assets.reload | ||
| 189 | end | ||
| 162 | end | 190 | end |
