mirror of
https://github.com/manyfold3d/manyfold.git
synced 2026-10-21 19:22:18 -05:00
314 lines
10 KiB
Ruby
314 lines
10 KiB
Ruby
require "rails_helper"
|
|
|
|
RSpec.describe Library do
|
|
context "when being validated" do
|
|
around do |ex|
|
|
MockDirectory.create([]) do |path|
|
|
@library_path = path
|
|
ex.run
|
|
end
|
|
end
|
|
|
|
it "is not valid without a path" do
|
|
expect(build(:library, path: nil)).not_to be_valid
|
|
end
|
|
|
|
it "is valid if a path is specified" do
|
|
expect(build(:library, path: @library_path)).to be_valid # rubocop:todo RSpec/InstanceVariable
|
|
end
|
|
|
|
it "removes whitespace from paths" do
|
|
library = create(:library, path: " storage ")
|
|
expect(library.path).to eq Rails.root.join("storage").to_s
|
|
end
|
|
|
|
it "is invalid if a bad path is specified" do # rubocop:todo RSpec/MultipleExpectations
|
|
l = build(:library, path: "/nope", create_path_if_not_on_disk: "0")
|
|
expect(l).not_to be_valid
|
|
expect(l.errors[:path]).to include "must be writable"
|
|
expect(l.errors[:path]).to include "must be readable"
|
|
end
|
|
|
|
it "has many models" do
|
|
expect(build(:library).models).to be_empty
|
|
end
|
|
|
|
it "must have a unique path" do
|
|
create(:library, path: @library_path) # rubocop:todo RSpec/InstanceVariable
|
|
expect(build(:library, path: @library_path)).not_to be_valid # rubocop:todo RSpec/InstanceVariable
|
|
end
|
|
|
|
[
|
|
"/bin",
|
|
"/boot",
|
|
"/dev",
|
|
"/etc",
|
|
"/lib",
|
|
"/lost",
|
|
"/proc",
|
|
"/root",
|
|
"/run",
|
|
"/sbin",
|
|
"/selinux",
|
|
"/srv",
|
|
"/usr"
|
|
].each do |prefix|
|
|
it "disallows paths under #{prefix}" do
|
|
path = File.join(prefix, "library")
|
|
allow(File).to receive(:exist?).with(path).and_return(true)
|
|
library = build(:library, path: path)
|
|
library.valid?
|
|
expect(library.errors[:path]).to include "cannot be a privileged system path"
|
|
end
|
|
end
|
|
|
|
it "allows paths that *begin* with a filtered path" do
|
|
library = build(:library, path: "/libraries")
|
|
library.valid?
|
|
expect(library.errors[:path]).not_to include "cannot be a privileged system path"
|
|
end
|
|
|
|
it "disallows root folder" do
|
|
library = build(:library, path: "/")
|
|
library.valid?
|
|
expect(library.errors[:path]).to include "cannot be a privileged system path"
|
|
end
|
|
|
|
it "disallows read-only folders" do
|
|
path = "/readonly/library"
|
|
allow(FileTest).to receive(:exist?).with(path).and_return(true)
|
|
library = build(:library, path: path)
|
|
library.valid?
|
|
expect(library.errors[:path]).to include "must be writable"
|
|
end
|
|
|
|
it "disallows folders that can't be read" do
|
|
path = "/tmp/library"
|
|
allow(FileTest).to receive(:readable?).with(path).and_return(false)
|
|
library = build(:library, path: path)
|
|
library.valid?
|
|
expect(library.errors[:path]).to include "must be readable"
|
|
end
|
|
|
|
it "disallows paths that exist but are files" do
|
|
path = "/tmp/actually_a_file"
|
|
allow(FileTest).to receive(:directory?).with(path).and_return(false)
|
|
library = build(:library, path: path)
|
|
library.valid?
|
|
expect(library.errors[:path]).to include "is not a directory"
|
|
end
|
|
|
|
context "with inaccessible subfolders" do
|
|
before do
|
|
allow(File).to receive(:exist?).with("/tmp/library").and_return(true)
|
|
allow(FileTest).to receive(:directory?).with("/tmp/library").and_return(true)
|
|
allow(FileTest).to receive(:readable?).with("/tmp/library").and_return(true)
|
|
allow(FileTest).to receive(:writable?).with("/tmp/library").and_return(true)
|
|
allow(Dir).to receive(:glob).with("/tmp/library").and_return(["/tmp/library"]) # Used in case check before checking subfolders
|
|
allow(Dir).to receive(:glob).with("/tmp/library/*").and_return(["/tmp/library/non_readable", "/tmp/library/file"])
|
|
allow(FileTest).to receive(:directory?).with("/tmp/library/non_readable").and_return(true)
|
|
allow(FileTest).to receive(:readable?).with("/tmp/library/non_readable").and_return(false)
|
|
allow(FileTest).to receive(:writable?).with("/tmp/library/non_readable").and_return(false)
|
|
allow(FileTest).to receive(:directory?).with("/tmp/library/file").and_return(false)
|
|
allow(FileTest).to receive(:readable?).with("/tmp/library/file").and_return(false)
|
|
allow(FileTest).to receive(:writable?).with("/tmp/library/file").and_return(false)
|
|
end
|
|
|
|
it "reports that subfolders aren't readable" do
|
|
library = build(:library, path: "/tmp/library")
|
|
library.valid?
|
|
expect(library.errors[:path]).to include "includes non-readable subfolders"
|
|
end
|
|
|
|
it "reports that subfolders aren't writable" do
|
|
library = build(:library, path: "/tmp/library")
|
|
library.valid?
|
|
expect(library.errors[:path]).to include "includes non-writeable subfolders"
|
|
end
|
|
end
|
|
|
|
it "normalizes paths" do
|
|
path = Rails.root + "tmp/../app"
|
|
library = build(:library, path: path)
|
|
expect(library.path).to eq (Rails.root + "app").to_s
|
|
end
|
|
end
|
|
|
|
context "when using a folder containing files" do
|
|
around do |ex|
|
|
MockDirectory.create([
|
|
"3dmodels/model [escapetest]/part_1.obj"
|
|
]) do |path|
|
|
@library_path = path + "/3dmodels"
|
|
ex.run
|
|
end
|
|
end
|
|
|
|
let(:library) { create(:library, path: @library_path) } # rubocop:todo RSpec/InstanceVariable
|
|
|
|
it "lists files" do
|
|
expect(library.list_files("**/*")).not_to be_empty
|
|
end
|
|
end
|
|
|
|
context "when using a folder with a space in" do
|
|
around do |ex|
|
|
MockDirectory.create([
|
|
"3d models/model [escapetest]/part_1.obj"
|
|
]) do |path|
|
|
@library_path = path + "/3d models"
|
|
ex.run
|
|
end
|
|
end
|
|
|
|
let(:library) { create(:library, path: @library_path) } # rubocop:todo RSpec/InstanceVariable
|
|
|
|
it "lists files" do
|
|
expect(library.list_files("**/*")).not_to be_empty
|
|
end
|
|
end
|
|
|
|
context "when listing a folder with manyfold-specific data in" do
|
|
around do |ex|
|
|
MockDirectory.create([
|
|
"3d/model/part_1.png",
|
|
"3d/model/.manyfold/derivatives/part_1.png/preview.png"
|
|
]) do |path|
|
|
@library_path = path + "/3d"
|
|
ex.run
|
|
end
|
|
end
|
|
|
|
let(:library) { create(:library, path: @library_path) } # rubocop:todo RSpec/InstanceVariable
|
|
|
|
it "lists valid files" do
|
|
expect(library.list_files("**/*")).to include "model/part_1.png"
|
|
end
|
|
|
|
it "ignores manyfold-specific files" do
|
|
expect(library.list_files("**/*")).not_to include "model/.manyfold/derivatives/part_1.png/preview.png"
|
|
end
|
|
end
|
|
|
|
it "is valid if path can be created" do # rubocop:todo RSpec/MultipleExpectations
|
|
library = build(:library, path: "/tmp/libraries/subdirectory", create_path_if_not_on_disk: "1")
|
|
expect(library).to be_valid
|
|
expect(Dir).to exist(library.path)
|
|
end
|
|
|
|
context "when deleting libraries" do
|
|
around do |ex|
|
|
MockDirectory.create([
|
|
"model/file.stl"
|
|
]) do |path|
|
|
@library_path = path
|
|
ex.run
|
|
end
|
|
end
|
|
|
|
let(:library) { create(:library, path: @library_path) } # rubocop:todo RSpec/InstanceVariable
|
|
let(:model) { create(:model, path: "model", library: library) }
|
|
let!(:file) { create(:model_file, filename: "file.stl", model: model) } # rubocop:disable RSpec/LetSetup
|
|
|
|
it "removes associated Models" do
|
|
expect { library.destroy }.to change(Model, :count).from(1).to(0)
|
|
end
|
|
|
|
it "removes associated ModelFiles" do
|
|
expect { library.destroy }.to change(ModelFile, :count).from(1).to(0)
|
|
end
|
|
|
|
it "preserves files on disk" do # rubocop:disable RSpec/MultipleExpectations
|
|
expect(File.exist?(File.join(@library_path, "model/file.stl"))).to be true # rubocop:todo RSpec/InstanceVariable
|
|
expect { library.destroy }.not_to change { File.exist?(File.join(@library_path, "model/file.stl")) } # rubocop:todo RSpec/InstanceVariable
|
|
end
|
|
end
|
|
|
|
context "when attempting to create one library inside another" do
|
|
subject(:library) { build(:library, path: outer_library.path + "/nested_library") }
|
|
|
|
let(:outer_library) { create(:library, path: @library_path) } # rubocop:todo RSpec/InstanceVariable
|
|
|
|
around do |ex|
|
|
MockDirectory.create([
|
|
"nested_library/model/model.stl"
|
|
]) do |path|
|
|
@library_path = path
|
|
ex.run
|
|
end
|
|
end
|
|
|
|
it "does not validate" do
|
|
expect(library).not_to be_valid
|
|
end
|
|
|
|
it "displays a useful error message" do
|
|
library.validate
|
|
expect(library.errors[:path].first).to eq "cannot be inside another library"
|
|
end
|
|
end
|
|
|
|
context "when attempting to create one library outside another" do
|
|
subject(:library) { build(:library, path: nested_library.path.gsub("/nested_library", "")) }
|
|
|
|
let(:nested_library) { create(:library, path: @library_path + "/nested_library") } # rubocop:todo RSpec/InstanceVariable
|
|
|
|
around do |ex|
|
|
MockDirectory.create([
|
|
"nested_library/model/model.stl"
|
|
]) do |path|
|
|
@library_path = path
|
|
ex.run
|
|
end
|
|
end
|
|
|
|
it "does not validate" do
|
|
expect(library).not_to be_valid
|
|
end
|
|
|
|
it "displays a useful error message" do
|
|
library.validate
|
|
expect(library.errors[:path].first).to eq "cannot contain other libraries"
|
|
end
|
|
end
|
|
|
|
context "with multiple libraries" do
|
|
let!(:first_library) { create(:library) }
|
|
let!(:second_library) { create(:library) }
|
|
|
|
it "uses first library as default if not explicitly set" do
|
|
expect(described_class.default).to eq first_library
|
|
end
|
|
|
|
it "uses second library as default if explicitly set" do
|
|
SiteSettings.default_library = second_library.id
|
|
expect(described_class.default).to eq second_library
|
|
end
|
|
|
|
it "falls back to first library as default if previous default is removed" do
|
|
SiteSettings.default_library = second_library.id
|
|
second_library.destroy
|
|
expect(described_class.default).to eq first_library
|
|
end
|
|
|
|
it "explicitly resets default library if default is destroyed (second)" do
|
|
SiteSettings.default_library = second_library.id
|
|
second_library.destroy
|
|
expect(SiteSettings.default_library).to eq first_library.id
|
|
end
|
|
|
|
it "explicitly resets default library if default is destroyed (first)" do
|
|
SiteSettings.default_library = first_library.id
|
|
first_library.destroy
|
|
expect(SiteSettings.default_library).to eq second_library.id
|
|
end
|
|
|
|
it "explicitly resets default library to nil if all libraries are destroyed" do
|
|
SiteSettings.default_library = first_library.id
|
|
first_library.destroy
|
|
second_library.destroy
|
|
expect(SiteSettings.default_library).to be_nil
|
|
end
|
|
end
|
|
end
|