require_relative "test_helper" require "maptool/gpx_store" require "stringio" class GpxStoreTest < Minitest::Test def setup @store = Maptool::GpxStore.new(Dir.mktmpdir) end def test_save_returns_the_relative_path_and_lists_it_in_the_tree path = @store.save(folder: "", filename: "ride.gpx", io: StringIO.new("")) assert_equal "ride.gpx", path assert_equal ["ride.gpx"], @store.tree["children"].map { |c| c["name"] } end def test_fit_extension_is_preserved_not_forced_to_gpx path = @store.save(folder: "", filename: "activity.fit", io: StringIO.new("binary")) assert_equal "activity.fit", path end def test_unrecognized_extension_gets_gpx_appended path = @store.save(folder: "", filename: "notes.txt", io: StringIO.new("x")) assert_equal "notes.txt.gpx", path end def test_colliding_filenames_are_disambiguated @store.save(folder: "", filename: "ride.gpx", io: StringIO.new("a")) path = @store.save(folder: "", filename: "ride.gpx", io: StringIO.new("b")) assert_equal "ride-1.gpx", path end def test_save_creates_the_target_folder @store.save(folder: "trips/2026", filename: "ride.gpx", io: StringIO.new("x")) folder = @store.tree["children"].find { |c| c["name"] == "trips" }["children"].find { |c| c["name"] == "2026" } assert_equal ["ride.gpx"], folder["children"].map { |c| c["name"] } end def test_path_traversal_is_rejected assert_raises(Maptool::GpxStore::InvalidPath) { @store.resolve_file("../../etc/passwd") } assert_raises(Maptool::GpxStore::InvalidPath) { @store.save(folder: "../escape", filename: "x.gpx", io: StringIO.new("x")) } end def test_delete_file @store.save(folder: "", filename: "ride.gpx", io: StringIO.new("x")) @store.delete_file("ride.gpx") assert_empty @store.tree["children"] end def test_delete_missing_file_raises_enoent assert_raises(Errno::ENOENT) { @store.delete_file("nope.gpx") } end def test_delete_folder_refuses_the_store_root assert_raises(Maptool::GpxStore::InvalidPath) { @store.delete_folder("") } end def test_move_relocates_and_disambiguates @store.save(folder: "", filename: "ride.gpx", io: StringIO.new("a")) @store.save(folder: "archive", filename: "ride.gpx", io: StringIO.new("b")) new_path = @store.move(from: "ride.gpx", to_folder: "archive") assert_equal "archive/ride-1.gpx", new_path assert_empty @store.tree["children"].reject { |c| c["name"] == "archive" } end end