require "rails_helper" # edit_model GET /models/:id/edit(.:format) models#edit # model GET /models/:id(.:format) models#show # PATCH /models/:id(.:format) models#update # PUT /models/:id(.:format) models#update # DELETE /models/:id(.:format) models#destroy # edit_models GET /models/edit(.:format) models#bulk_edit # update_models PATCH /models/update(.:format) models#bulk_update # models GET /models(.:format) models#index # new_model GET /models/new(.:format) models#new # POST /models(.:format) models#create # merge_models POST /models/merge(.:format) models#merge # scan_model POST /models/:id/scan(.:format) models#scan RSpec.describe "Models" do it_behaves_like "Permittable", Model context "when signed out in multiuser mode", :after_first_run, :multiuser do context "with public model" do let!(:model) { create(:model, :public) } describe "GET /models" do it "includes indexing directive header" do allow(SiteSettings).to receive_messages(default_indexable: true, default_ai_indexable: false) get "/models" expect(response.headers["X-Robots-Tag"]).to eq "noai noimageai" end it "includes indexing directive meta tag" do allow(SiteSettings).to receive_messages(default_indexable: true, default_ai_indexable: false) get "/models" expect(response.body).to include %() end end describe "GET /models/:id" do it "returns http success" do get "/models/#{model.to_param}" expect(response).to have_http_status(:success) end it "includes indexing directive header" do allow(SiteSettings).to receive_messages(default_indexable: true, default_ai_indexable: false) get "/models/#{model.to_param}" expect(response.headers["X-Robots-Tag"]).to eq "noai noimageai" end it "includes indexing directive meta tag" do allow(SiteSettings).to receive_messages(default_indexable: true, default_ai_indexable: false) get "/models/#{model.to_param}" expect(response.body).to include %() end end end context "with non-public model" do let!(:model) { create(:model) } describe "GET /models/:id" do it "returns not found" do get "/models/#{model.to_param}" expect(response).to be_not_found end end end end [:multiuser, :singleuser].each do |mode| context "when signed in in #{mode} mode", mode do let!(:creator) { create(:creator) } let!(:collection) { create(:collection) } let!(:library) do l = create(:library) build_list(:model, 5, library: l) { |it| it.save! } build_list(:model, 5, library: l, creator: creator) { |it| it.save! } build_list(:model, 5, library: l, collection: collection) { |it| it.save! } build_list(:model, 5, library: l, creator: creator, collection: collection) { |it| it.save! } l end describe "GET /models/:id", :as_member do it "returns http success" do get "/models/#{library.models.first.to_param}" expect(response).to have_http_status(:success) end context "with zip file prepared" do let(:model) { create(:model, library: library) } before do PrepareDownloadJob.perform_now(model_id: model.id, selection: nil) end it "gets ZIP file" do get "/models/#{model.to_param}.zip" expect(response).to have_http_status(:success) end it "doesn't get zipfile if only preview access available" do model.revoke_all_permissions(Role.find_by!(name: :member)) model.grant_permission_to("preview", User.last) get "/models/#{model.to_param}.zip" expect(response).to have_http_status(:forbidden) end end end describe "GET /models/:id/edit" do before { get "/models/#{library.models.first.to_param}/edit" } it "shows edit page for file", :as_moderator do expect(response).to have_http_status(:success) end it "sets returnable session param", :as_moderator do expect(session[:return_after_new]).to eq "/models/#{library.models.first.to_param}/edit" end it "is denied to non-moderators", :as_contributor do expect(response).to have_http_status(:forbidden) end end describe "PUT /models/:id" do it "adds tags to a model", :as_moderator do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations put "/models/#{library.models.first.to_param}", params: {model: {tag_list: ["a", "b", "c"]}} expect(response).to have_http_status(:redirect) expect(library.models.first.tag_list).to include("a", "b", "c") end it "clears returnable session param", :as_moderator do put "/models/#{library.models.first.to_param}", params: {model: {tag_list: ["a", "b", "c"]}} expect(session[:return_after_new]).to be_nil end it "removes tags from a model", :as_moderator do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations first = library.models.first first.tag_list = "a, b, c" first.save put "/models/#{library.models.first.to_param}", params: {model: {tag_list: ["a", "b"]}} expect(response).to have_http_status(:redirect) first.reload tags = first.tag_list expect(tags.length).to eq 2 expect(tags).to contain_exactly("a", "b") end it "both adds and removes tags from a model", :as_moderator do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations first = library.models.first first.tag_list = "a, b, c" first.save put "/models/#{library.models.first.to_param}", params: {model: {tag_list: ["a", "b", "d"]}} expect(response).to have_http_status(:redirect) first.reload tags = first.tag_list expect(tags.length).to eq 3 expect(tags).to contain_exactly("a", "b", "d") end it "auto-publishes creator if being made public", :as_moderator do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations private_creator = create(:creator) private_model = create(:model, creator: private_creator) put "/models/#{private_model.to_param}", params: { model: { creator_id: private_creator.id, caber_relations_attributes: {"0" => {subject: "role::public", permission: "view"}} } } expect(response).to have_http_status(:redirect) expect(private_model.reload).to be_public expect(private_creator.reload).to be_public end it "is denied to non-moderators", :as_contributor do put "/models/#{library.models.first.to_param}" expect(response).to have_http_status(:forbidden) end end describe "DELETE /models/:id" do # rubocop:todo RSpec/RepeatedExampleGroupBody before { delete "/models/#{library.models.first.to_param}" } it "redirects to model list after deletion", :as_moderator do expect(response).to redirect_to("/") end it "is denied to non-moderators", :as_contributor do expect(response).to have_http_status(:forbidden) end end describe "GET /models/edit" do # rubocop:todo RSpec/RepeatedExampleGroupBody before { get edit_models_path } it "shows bulk edit page", :as_moderator do expect(response).to have_http_status(:success) end it "sets returnable session param", :as_moderator do expect(session[:return_after_new]).to eq "/models/edit" end it "is denied to non-moderators", :as_contributor do expect(response).to have_http_status(:forbidden) end context "with filters", :as_moderator do let(:tag) { create(:tag) } let!(:tagged_model) { create(:model, library: library, tag_list: [tag.name]) } it "shows filtered models" do get edit_models_path(tag: [tag.name]) expect(response.body).to include(tagged_model.name) end it "doesn't show other models" do get edit_models_path(tag: [tag.name]) library.models.each do |model| next if model == tagged_model expect(response.body).not_to include(model.name) end end end end describe "PATCH /models/update" do let(:model_params) { model_params = {} library.models.each do |model| model_params[model.to_param] = 1 end model_params } it "updates models creator", :as_moderator do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations models = library.models.take(2) update = {} update[models[0].to_param] = 1 update[models[1].to_param] = 1 patch update_models_path, params: {models: update, creator_id: creator.id} expect(response).to have_http_status(:redirect) models.each { |model| model.reload } expect(models[0].creator_id).to eq creator.id expect(models[1].creator_id).to eq creator.id end it "adds tags to models", :as_moderator do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations patch update_models_path, params: {models: model_params, add_tags: ["a", "b", "c"]} expect(response).to have_http_status(:redirect) library.models.take(2).each do |model| expect(model.tag_list).to include("a", "b", "c") end end it "removes tags from models", :as_moderator do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations library.models.take(2).each do |model| model.tag_list = "a, b, c" model.save end patch update_models_path, params: {models: model_params, remove_tags: ["a", "b"]} expect(response).to have_http_status(:redirect) expect(library.models.tagged_with("c").count).to eq 2 expect(library.models.tagged_with("a").count).to eq 0 end it "clears returnable session param", :as_moderator do patch update_models_path, params: {models: model_params, remove_tags: ["a", "b"]} expect(session[:return_after_new]).to be_nil end it "is denied to non-moderators", :as_contributor do update = {} library.models.take(2).each { |it| update[it.to_param] = 1 } patch update_models_path, params: {models: model_params, remove_tags: ["a", "b"]} expect(response).to have_http_status(:forbidden) end context "when updating all filtered models", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers let(:tag) { create(:tag) } let!(:tagged_model) { create(:model, library: library, tag_list: [tag.name]) } let(:new_library) { create(:library) } let(:params) do { update_all: I18n.t("models.bulk_edit.update_all"), new_library_id: new_library.id, tag: [tag.name] } end it "updates all models matching the filter" do patch update_models_path, params: params library.models.each do |model| next if model == tagged_model expect(model.reload.library_id).to eq(library.id) end end end end describe "GET /models", :as_member do it "allows search queries" do get "/models?q=#{library.models.first.name}" expect(response).to have_http_status(:success) end it "allows tag filters" do m = library.models.first m.tag_list << "test" m.save get "/models?tag[]=test" expect(response).to have_http_status(:success) end it "allows link filters" do get "/models?link=" expect(response).to have_http_status(:success) end it "returns paginated models" do # rubocop:todo RSpec/MultipleExpectations get "/models?library=#{library.to_param}&page=2" expect(response).to have_http_status(:success) expect(response.body).to match(/pagination/) end end describe "POST /models/merge" do context "with a target and models to merge into it", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers let(:model_one) { create(:model) } let(:model_two) { create(:model) } let(:merge_post) { post "/models/merge", params: { target: model_one.to_param, models: [model_two.to_param] } } it "is denied if the user doesn't have update permission on the models", :as_contributor do merge_post expect(response).to have_http_status(:forbidden) end it "is denied if the user doesn't have update permission on the target" end context "without any models", :as_moderator do let(:model) { create(:model) } let(:merge_post) { post "/models/merge", params: { target: model.to_param } } it "gives a bad request response if no models are provided" do merge_post expect(response).to have_http_status(:bad_request) end end context "without a target", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers let(:model_one) { create(:model) } let(:model_two) { create(:model) } let(:merge_post) { post "/models/merge", params: { models: [model_one.to_param, model_two.to_param] } } before { merge_post } it "redirects to the merge options page" do expect(response).to redirect_to("/models/merge?models%5B%5D=#{model_one.to_param}&models%5B%5D=#{model_two.to_param}") end end context "when merging to target model", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers let(:model_one) { create(:model) } let(:model_two) { create(:model) } let(:merge_post) { post "/models/merge", params: { models: [model_one.to_param, model_two.to_param], target: model_one.to_param } } before { merge_post } it "redirects to the target model" do expect(response).to redirect_to("/models/#{model_one.to_param}") end end context "when merging to a completely new model", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers let(:model_one) { create(:model) } let(:model_two) { create(:model) } let(:merge_post) { post "/models/merge", params: { models: [model_one.to_param, model_two.to_param], target: "==new==" } } before { merge_post } it "redirects to the new model" do new_model = Model.last expect(response).to redirect_to("/models/#{new_model.to_param}") end it "Uses the name of the first merged model as the new name" do new_model = Model.last expect(new_model.name).to eq model_one.name end end context "when merging to a common root", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers let(:model_one) { create(:model, path: "common/model_one") } let(:model_two) { create(:model, path: "common/model_two", library: model_one.library) } let(:merge_post) { post "/models/merge", params: { models: [model_one.to_param, model_two.to_param], target: "==common_root==" } } before { merge_post } it "redirects to the new model" do new_model = Model.last expect(response).to redirect_to("/models/#{new_model.to_param}") end it "uses the common root folder as the new path" do new_model = Model.last expect(new_model.path).to eq "common" end it "uses the name of the common root folder as the new name" do new_model = Model.last expect(new_model.name).to eq "Common" end end context "with form-encoded data", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers let(:model_one) { create(:model) } let(:model_two) { create(:model) } let(:merge_post) { post "/models/merge", params: { models: { model_one.to_param => "0", model_two.to_param => "1" } } } before { merge_post } it "extracts selected models from form" do expect(assigns(:models)).to include model_two end it "ignores unselected models" do expect(assigns(:models)).not_to include model_one end end end describe "GET /models/merge", :as_moderator do # rubocop:todo RSpec/MultipleMemoizedHelpers let(:model_one) { create(:model) } let(:model_two) { create(:model) } let(:configure_merge) { get "/models/merge", params: { models: [model_one.to_param, model_two.to_param] } } before { configure_merge } it "merge options page includes a form for choosing the target" do expect(response.body).to include(%(