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 end