- notes.js's ICON_LABELS wasn't extended for the shop_supermarket/ shop_convenience keys icons.js added — the note icon picker showed the raw internal key as the title instead of a real label. - Plugins.run mislabeled a malformed plugin.json as "plugin did not print valid JSON" — a message written for bad script stdout, not a bad manifest, since both paths shared one rescue. - overpass_pois.rb's description_for joined raw OSM tag values with "\n" — an operator/opening_hours tag with an embedded newline could inject an extra line into the popup that gpx.js's formatDescription (built for p4n2gpx's own well-formed lines) would render as its own independent, legitimate-looking link. Tag values are stripped of embedded newlines before joining.
101 lines
4.2 KiB
Ruby
101 lines
4.2 KiB
Ruby
require_relative "test_helper"
|
|
require "maptool/plugins"
|
|
require "tmpdir"
|
|
require "fileutils"
|
|
|
|
class PluginsTest < Minitest::Test
|
|
def setup
|
|
@dir = Dir.mktmpdir("maptool-plugins-test")
|
|
@plugin_dir = File.join(@dir, "hello")
|
|
FileUtils.mkdir_p(@plugin_dir)
|
|
File.write(File.join(@plugin_dir, "plugin.json"), JSON.generate(label: "Hello", command: "./run.sh"))
|
|
File.write(File.join(@plugin_dir, "run.sh"), "#!/bin/sh\necho '{\"type\":\"FeatureCollection\",\"features\":[]}'\n")
|
|
File.chmod(0o755, File.join(@plugin_dir, "run.sh"))
|
|
end
|
|
|
|
def teardown
|
|
FileUtils.remove_entry(@dir)
|
|
end
|
|
|
|
def test_list_finds_plugins_by_their_manifest
|
|
assert_equal [{ "name" => "hello", "label" => "Hello" }], Maptool::Plugins.list(@dir)
|
|
end
|
|
|
|
def test_list_is_empty_for_a_directory_with_no_plugins
|
|
assert_empty Maptool::Plugins.list(Dir.mktmpdir)
|
|
end
|
|
|
|
def test_run_returns_the_plugins_geojson_output
|
|
result = Maptool::Plugins.run(@dir, "hello", [1, 2, 3, 4])
|
|
assert_equal({ "type" => "FeatureCollection", "features" => [] }, result)
|
|
end
|
|
|
|
def test_run_raises_for_an_unknown_plugin
|
|
assert_raises(Maptool::Plugins::RunError) { Maptool::Plugins.run(@dir, "nope", [1, 2, 3, 4]) }
|
|
end
|
|
|
|
# A directory-name-shaped ".." would otherwise resolve one level above
|
|
# dir via File.join — the same class of path-escape GpxStore guards
|
|
# against for uploaded file paths.
|
|
def test_run_rejects_a_path_traversal_attempt_as_the_name
|
|
assert_raises(Maptool::Plugins::RunError) { Maptool::Plugins.run(@dir, "..", [1, 2, 3, 4]) }
|
|
end
|
|
|
|
def test_run_raises_when_the_script_exits_non_zero
|
|
File.write(File.join(@plugin_dir, "run.sh"), "#!/bin/sh\nexit 1\n")
|
|
File.chmod(0o755, File.join(@plugin_dir, "run.sh"))
|
|
assert_raises(Maptool::Plugins::RunError) { Maptool::Plugins.run(@dir, "hello", [1, 2, 3, 4]) }
|
|
end
|
|
|
|
def test_run_raises_when_the_script_does_not_print_a_feature_collection
|
|
File.write(File.join(@plugin_dir, "run.sh"), "#!/bin/sh\necho 'not json'\n")
|
|
File.chmod(0o755, File.join(@plugin_dir, "run.sh"))
|
|
assert_raises(Maptool::Plugins::RunError) { Maptool::Plugins.run(@dir, "hello", [1, 2, 3, 4]) }
|
|
end
|
|
|
|
# Distinct from the "did not print valid JSON" message above — that one's
|
|
# about the script's stdout, this is about plugin.json itself, before the
|
|
# script is ever run. Same exception class either way, but a clear
|
|
# message matters since this is what a "Failed" plugin run shows the user
|
|
# (see public/js/plugins.js).
|
|
def test_run_raises_a_distinct_message_for_an_invalid_manifest
|
|
File.write(File.join(@plugin_dir, "plugin.json"), "{not valid json")
|
|
error = assert_raises(Maptool::Plugins::RunError) { Maptool::Plugins.run(@dir, "hello", [1, 2, 3, 4]) }
|
|
assert_match(/invalid plugin\.json/, error.message)
|
|
end
|
|
|
|
# Multiple dirs (app.rb's PLUGINS_DIRS: the built-in plugins/ plus an
|
|
# optional private one under data/plugins/) — list/run both accept an
|
|
# array, not just the single-dir form the tests above exercise.
|
|
|
|
def test_list_merges_plugins_across_several_dirs
|
|
other_dir = Dir.mktmpdir("maptool-plugins-test-2")
|
|
other_plugin_dir = File.join(other_dir, "private")
|
|
FileUtils.mkdir_p(other_plugin_dir)
|
|
File.write(File.join(other_plugin_dir, "plugin.json"), JSON.generate(label: "Private", command: "./run.sh"))
|
|
|
|
plugins = Maptool::Plugins.list([@dir, other_dir])
|
|
|
|
assert_equal [{ "name" => "hello", "label" => "Hello" }, { "name" => "private", "label" => "Private" }], plugins
|
|
ensure
|
|
FileUtils.remove_entry(other_dir)
|
|
end
|
|
|
|
def test_list_prefers_the_first_dir_when_the_same_name_exists_in_both
|
|
other_dir = Dir.mktmpdir("maptool-plugins-test-2")
|
|
FileUtils.mkdir_p(File.join(other_dir, "hello"))
|
|
File.write(File.join(other_dir, "hello", "plugin.json"), JSON.generate(label: "Shadowed", command: "./run.sh"))
|
|
|
|
plugins = Maptool::Plugins.list([@dir, other_dir])
|
|
|
|
assert_equal [{ "name" => "hello", "label" => "Hello" }], plugins
|
|
ensure
|
|
FileUtils.remove_entry(other_dir)
|
|
end
|
|
|
|
def test_run_finds_a_plugin_in_a_later_dir_when_earlier_dirs_do_not_have_it
|
|
result = Maptool::Plugins.run([Dir.mktmpdir, @dir], "hello", [1, 2, 3, 4])
|
|
assert_equal({ "type" => "FeatureCollection", "features" => [] }, result)
|
|
end
|
|
end
|