agama/service/test/y2storage/agama_proposal_search_test.rb
2026-05-12 14:18:39 +02:00

245 lines
7.2 KiB
Ruby

# frozen_string_literal: true
# Copyright (c) [2024-2025] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
require_relative "../agama/storage/storage_helpers"
require "agama/config"
require "agama/storage/config"
require "agama/storage/config_conversions"
require "agama/storage/system"
require "y2storage"
require "y2storage/agama_proposal"
describe Y2Storage::AgamaProposal do
using Y2Storage::Refinements::SizeCasts
include Agama::RSpec::StorageHelpers
subject(:proposal) do
described_class.new(config, storage_system, issues_list: issues_list)
end
let(:storage_system) { Agama::Storage::System.new }
let(:config) { config_from_json }
let(:config_from_json) do
Agama::Storage::ConfigConversions::FromJSON
.new(config_json)
.convert
end
let(:issues_list) { [] }
before do
mock_storage(devicegraph: scenario)
# To speed-up the tests
allow(Y2Storage::EncryptionMethod::TPM_FDE).to receive(:possible?).and_return(true)
end
let(:scenario) { "disks.yaml" }
describe "#propose" do
context "when searching several disks at once and using them as LVM targetDevices" do
let(:config_json) do
{
drives: [
{ search: { max: 2 }, alias: "first-two" }
],
volumeGroups: [
{
name: "system",
physicalVolumes: [
{ generate: { targetDevices: ["first-two"] } }
],
logicalVolumes: [
{
name: "root",
size: "55 GiB",
filesystem: { path: "/" }
}
]
}
]
}
end
it "extends the LVM over all the chosen disks if needed" do
devicegraph = proposal.propose
system = devicegraph.find_by_name("/dev/system")
expect(system.lvm_pvs.map { |pv| pv.blk_device.partitionable.name })
.to contain_exactly("/dev/vda", "/dev/vdb")
end
end
context "when searching several disks at once and using them as LVM PVs" do
let(:config_json) do
{
drives: [
{
partitions: [
{ search: "/dev/vda1" },
{ search: "*", alias: "rest" }
]
}
],
volumeGroups: [
{
name: "system",
physicalVolumes: ["rest"],
logicalVolumes: [
{
name: "root",
size: "10 GiB",
filesystem: { path: "/" }
}
]
}
]
}
end
it "uses all the disks as physical volumes" do
probed = Y2Storage::StorageManager.instance.probed
vda2_sid = probed.find_by_name("/dev/vda2").sid
vda3_sid = probed.find_by_name("/dev/vda3").sid
devicegraph = proposal.propose
system = devicegraph.find_by_name("/dev/system")
expect(system.lvm_pvs.map(&:blk_device).map(&:sid)).to contain_exactly(vda2_sid, vda3_sid)
end
end
context "when marking several partitions for resizing" do
let(:config_json) do
{
boot: { configure: false },
drives: [
{
search: disk_name,
partitions: [
{ search: search, size: { min: 0, max: "current" } },
{ size: "25 GiB", filesystem: { path: "/" } }
]
}
]
}
end
before do
allow_any_instance_of(Y2Storage::Partition)
.to(receive(:detect_resize_info))
.and_return(resize_info)
end
let(:resize_info) do
instance_double(
Y2Storage::ResizeInfo, resize_ok?: true,
min_size: Y2Storage::DiskSize::GiB(4), max_size: Y2Storage::DiskSize::GiB(35)
)
end
shared_examples "resize" do
it "resizes several partitions if needed" do
probed = Y2Storage::StorageManager.instance.probed
vda2_size = probed.find_by_name("/dev/vda2").size
vda3_size = probed.find_by_name("/dev/vda3").size
devicegraph = proposal.propose
expect(devicegraph.find_by_name("/dev/vda2").size).to be < vda2_size
expect(devicegraph.find_by_name("/dev/vda3").size).to be < vda3_size
end
end
context "using an empty search to match the partitions" do
let(:search) { {} }
context "if there are several partitions at the disk" do
let(:disk_name) { "/dev/vda" }
include_examples "resize"
end
context "if there are no partitions in the disk" do
let(:disk_name) { "/dev/vdc" }
it "register an error and returns nil" do
expect(proposal.propose).to be_nil
expect(proposal.issues_list).to include an_object_having_attributes(
description: "Mandatory partition not found"
)
end
end
end
context "using asterisk as the search to match the partitions" do
let(:search) { "*" }
context "if there are several partitions at the disk" do
let(:disk_name) { "/dev/vda" }
include_examples "resize"
end
context "if there are no partitions in the disk" do
let(:disk_name) { "/dev/vdc" }
it "processes the proposal" do
devicegraph = proposal.propose
disk = devicegraph.find_by_name(disk_name)
expect(disk.partitions.size).to eq 1
end
it "does not include any issue about non-existent partitions" do
proposal.propose
expect(proposal.issues_list).to be_empty
end
end
end
end
context "when searching existing partitions but not specifying any action on them" do
let(:config_json) do
{
boot: { configure: false },
drives: [
{
search: "/dev/vda",
partitions: [
{ search: "*" },
{ size: "20 GiB", filesystem: { path: "/" } }
]
}
]
}
end
# Regression test. In the past the proposal tried to resize some partitions due to the
# search "*" without actions.
it "no partitions are deleted or resized" do
expect_any_instance_of(Y2Storage::Partition).to_not receive(:detect_resize_info)
# There is no way to make 20 GiB fit without resizing or deleting
expect { proposal.propose }.to raise_error(Y2Storage::NoDiskSpaceError)
end
end
end
end