mirror of
https://github.com/l5yth/potato-mesh.git
synced 2026-03-28 17:42:48 +01:00
* Handle legacy config migration for XDG assets * Ensure legacy key migration precedes identity load * Apply rufo formatting to identity module
223 lines
7.5 KiB
Ruby
223 lines
7.5 KiB
Ruby
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# frozen_string_literal: true
|
|
|
|
require "spec_helper"
|
|
|
|
RSpec.describe PotatoMesh::App::Filesystem do
|
|
let(:harness_class) do
|
|
Class.new do
|
|
extend PotatoMesh::App::Filesystem
|
|
|
|
class << self
|
|
def debug_entries
|
|
@debug_entries ||= []
|
|
end
|
|
|
|
def warning_entries
|
|
@warning_entries ||= []
|
|
end
|
|
|
|
def debug_log(message, context:, **metadata)
|
|
debug_entries << { message: message, context: context, metadata: metadata }
|
|
end
|
|
|
|
def warn_log(message, context:, **metadata)
|
|
warning_entries << { message: message, context: context, metadata: metadata }
|
|
end
|
|
|
|
def reset_logs!
|
|
@debug_entries = []
|
|
@warning_entries = []
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
around do |example|
|
|
harness_class.reset_logs!
|
|
example.run
|
|
harness_class.reset_logs!
|
|
end
|
|
|
|
describe "#perform_initial_filesystem_setup!" do
|
|
it "migrates the legacy database and keyfile" do
|
|
Dir.mktmpdir do |dir|
|
|
legacy_db = File.join(dir, "legacy", "mesh.db")
|
|
legacy_key = File.join(dir, "legacy-config", "keyfile")
|
|
new_db = File.join(dir, "data", "potato-mesh", "mesh.db")
|
|
new_key = File.join(dir, "config", "potato-mesh", "keyfile")
|
|
|
|
FileUtils.mkdir_p(File.dirname(legacy_db))
|
|
File.write(legacy_db, "db")
|
|
FileUtils.mkdir_p(File.dirname(legacy_key))
|
|
File.write(legacy_key, "key")
|
|
|
|
allow(PotatoMesh::Config).to receive_messages(
|
|
legacy_db_path: legacy_db,
|
|
db_path: new_db,
|
|
default_db_path: new_db,
|
|
keyfile_path: new_key,
|
|
)
|
|
allow(PotatoMesh::Config).to receive(:legacy_keyfile_candidates).and_return([legacy_key])
|
|
|
|
harness_class.perform_initial_filesystem_setup!
|
|
|
|
expect(File).to exist(new_db)
|
|
expect(File).to exist(new_key)
|
|
expect(File.read(new_db)).to eq("db")
|
|
expect(File.read(new_key)).to eq("key")
|
|
expect(File.stat(new_key).mode & 0o777).to eq(0o600)
|
|
expect(File.stat(new_db).mode & 0o777).to eq(0o600)
|
|
expect(harness_class.debug_entries.size).to eq(2)
|
|
expect(harness_class.warning_entries).to be_empty
|
|
end
|
|
end
|
|
|
|
it "migrates repository configuration assets from web/config" do
|
|
Dir.mktmpdir do |dir|
|
|
web_root = File.join(dir, "web")
|
|
legacy_key = File.join(web_root, "config", "potato-mesh", "keyfile")
|
|
legacy_well_known = File.join(web_root, "config", "potato-mesh", "well-known", "potato-mesh")
|
|
destination_root = File.join(dir, "xdg-config", "potato-mesh")
|
|
new_key = File.join(destination_root, "keyfile")
|
|
new_well_known = File.join(destination_root, "well-known", "potato-mesh")
|
|
|
|
FileUtils.mkdir_p(File.dirname(legacy_key))
|
|
File.write(legacy_key, "legacy-key")
|
|
FileUtils.mkdir_p(File.dirname(legacy_well_known))
|
|
File.write(legacy_well_known, "{\"legacy\":true}")
|
|
|
|
allow(PotatoMesh::Config).to receive(:web_root).and_return(web_root)
|
|
allow(PotatoMesh::Config).to receive(:keyfile_path).and_return(new_key)
|
|
allow(PotatoMesh::Config).to receive(:well_known_storage_root).and_return(File.dirname(new_well_known))
|
|
allow(PotatoMesh::Config).to receive(:well_known_relative_path).and_return(".well-known/potato-mesh")
|
|
allow(PotatoMesh::Config).to receive(:legacy_db_path).and_return(File.join(dir, "legacy", "mesh.db"))
|
|
allow(PotatoMesh::Config).to receive(:db_path).and_return(File.join(dir, "data", "potato-mesh", "mesh.db"))
|
|
allow(PotatoMesh::Config).to receive(:default_db_path).and_return(File.join(dir, "data", "potato-mesh", "mesh.db"))
|
|
|
|
harness_class.perform_initial_filesystem_setup!
|
|
|
|
expect(File).to exist(new_key)
|
|
expect(File.read(new_key)).to eq("legacy-key")
|
|
expect(File.stat(new_key).mode & 0o777).to eq(0o600)
|
|
expect(File).to exist(new_well_known)
|
|
expect(File.read(new_well_known)).to eq("{\"legacy\":true}")
|
|
expect(File.stat(new_well_known).mode & 0o777).to eq(0o644)
|
|
expect(harness_class.debug_entries.map { |entry| entry[:context] }).to include("filesystem.keys", "filesystem.well_known")
|
|
end
|
|
end
|
|
|
|
it "skips database migration when using a custom destination" do
|
|
Dir.mktmpdir do |dir|
|
|
legacy_db = File.join(dir, "legacy", "mesh.db")
|
|
new_db = File.join(dir, "custom", "database.db")
|
|
|
|
FileUtils.mkdir_p(File.dirname(legacy_db))
|
|
File.write(legacy_db, "db")
|
|
|
|
allow(PotatoMesh::Config).to receive_messages(
|
|
legacy_db_path: legacy_db,
|
|
db_path: new_db,
|
|
default_db_path: File.join(dir, "default", "mesh.db"),
|
|
legacy_keyfile_path: File.join(dir, "old", "keyfile"),
|
|
keyfile_path: File.join(dir, "config", "keyfile"),
|
|
)
|
|
|
|
harness_class.perform_initial_filesystem_setup!
|
|
|
|
expect(File).not_to exist(new_db)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "private migration helpers" do
|
|
it "does not migrate when the source is missing" do
|
|
Dir.mktmpdir do |dir|
|
|
destination = File.join(dir, "target", "file")
|
|
harness_class.send(
|
|
:migrate_legacy_file,
|
|
File.join(dir, "missing"),
|
|
destination,
|
|
chmod: 0o600,
|
|
context: "spec.context",
|
|
)
|
|
|
|
expect(File).not_to exist(destination)
|
|
expect(harness_class.debug_entries).to be_empty
|
|
end
|
|
end
|
|
|
|
it "does not overwrite existing destinations" do
|
|
Dir.mktmpdir do |dir|
|
|
source = File.join(dir, "source")
|
|
destination = File.join(dir, "destination")
|
|
|
|
File.write(source, "alpha")
|
|
FileUtils.mkdir_p(File.dirname(destination))
|
|
File.write(destination, "beta")
|
|
|
|
harness_class.send(
|
|
:migrate_legacy_file,
|
|
source,
|
|
destination,
|
|
chmod: 0o600,
|
|
context: "spec.context",
|
|
)
|
|
|
|
expect(File.read(destination)).to eq("beta")
|
|
end
|
|
end
|
|
|
|
it "ignores migrations when the source and destination are identical" do
|
|
Dir.mktmpdir do |dir|
|
|
path = File.join(dir, "shared")
|
|
File.write(path, "same")
|
|
|
|
harness_class.send(
|
|
:migrate_legacy_file,
|
|
path,
|
|
path,
|
|
chmod: 0o600,
|
|
context: "spec.context",
|
|
)
|
|
|
|
expect(harness_class.debug_entries).to be_empty
|
|
end
|
|
end
|
|
|
|
it "logs warnings when the migration fails" do
|
|
Dir.mktmpdir do |dir|
|
|
source = File.join(dir, "source")
|
|
destination = File.join(dir, "destination")
|
|
File.write(source, "data")
|
|
|
|
allow(FileUtils).to receive(:mkdir_p).and_raise(Errno::EACCES)
|
|
|
|
harness_class.send(
|
|
:migrate_legacy_file,
|
|
source,
|
|
destination,
|
|
chmod: 0o600,
|
|
context: "spec.context",
|
|
)
|
|
|
|
expect(harness_class.warning_entries.size).to eq(1)
|
|
expect(harness_class.debug_entries).to be_empty
|
|
end
|
|
ensure
|
|
allow(FileUtils).to receive(:mkdir_p).and_call_original
|
|
end
|
|
end
|
|
end
|