ENV["RACK_ENV"] = "test" ENV["LIVETRACK_WEBHOOK_SECRET"] ||= "test-webhook-secret" require "fileutils" require "tmpdir" require "minitest/autorun" require "rack" require "json" # A fresh throwaway directory per test run, not the real data/ — every # GpxStore/LiveTrackStore write app.rb's routes make during the run lands # here and is gone when the process exits, so running the suite never # touches (or needs to clean up after itself in) real uploaded data. ENV["MAPTOOL_DATA_DIR"] = Dir.mktmpdir("maptool-test-data") Minitest.after_run { FileUtils.remove_entry(ENV["MAPTOOL_DATA_DIR"]) } $LOAD_PATH.unshift(File.expand_path("../lib", __dir__)) require_relative "../app" # Available in every test via Minitest::Test below — fixture_path/json_body # are generic enough to be worth having everywhere, not just in app.rb's # own HTTP-level tests (see AppTestHelper for those). module TestHelpers def fixture_path(name) File.join(__dir__, "fixtures", name) end def json_body(response) JSON.parse(response.body) end end class Minitest::Test include TestHelpers end # Only included where needed (test_app.rb) — drives app.rb's Sinatra routes # through Rack::MockRequest, the same way every ad-hoc verification during # development did. module AppTestHelper def app Maptool::App end def request @request ||= Rack::MockRequest.new(app) end # Sinatra's session is read from env["rack.session"] (populated by the # Rack::Session::Cookie middleware on a real request) — setting it # directly here is equivalent to already having a valid session cookie, # without needing to drive the actual Gitea OAuth2 login flow. def logged_in_env(user: "testuser") { "rack.session" => { user: user } } end # GPX_STORE/LIVETRACK_STORE are process-wide constants pointing at the # one throwaway MAPTOOL_DATA_DIR (see above) — without this, uploads from # one test would still be there (and show up in tree/list assertions) in # the next one, since minitest runs a class's tests in one process. def teardown FileUtils.rm_rf(Dir.glob(File.join(Maptool::App::DATA_DIR, "gpx", "*"))) File.write(File.join(Maptool::App::DATA_DIR, "livetrack.json"), "[]") super end def multipart_body(boundary, filename, content, content_type: "application/octet-stream", field_name: "file") "--#{boundary}\r\n" \ "Content-Disposition: form-data; name=\"#{field_name}\"; filename=\"#{filename}\"\r\n" \ "Content-Type: #{content_type}\r\n\r\n" \ "#{content}\r\n" \ "--#{boundary}--\r\n" end # POSTs a single file as a multipart/form-data upload; pass folder in # `path`'s query string (e.g. "/api/gpx?folder=trips") the same way the # real upload form does. def post_file(path, filename:, content:, content_type: "application/octet-stream", env: {}) boundary = "TestBoundary#{rand(1_000_000)}" body = multipart_body(boundary, filename, content, content_type: content_type) request.post( path, input: body, "CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}", "CONTENT_LENGTH" => body.bytesize.to_s, **env, ) end end