# frozen_string_literal: true

require "spec_helper"

module Roadie
  describe UrlGenerator do
    it "is initialized with URL options" do
      expect(UrlGenerator.new(host: "foo.com").url_options).to eq({host: "foo.com"})
    end

    it "raises an argument error if no URL options are specified" do
      expect {
        UrlGenerator.new(nil)
      }.to raise_error ArgumentError, /url options/i
    end

    it "raises an argument error if no host is specified" do
      expect {
        UrlGenerator.new(port: 3000)
      }.to raise_error ArgumentError, /host/i
    end

    it "raises an argument error if unknown option is passed" do
      expect {
        UrlGenerator.new(host: "localhost", secret: true)
      }.to raise_error ArgumentError, /secret/
    end

    describe "generating URLs" do
      def url(path, options = {})
        UrlGenerator.new(options).generate_url(path)
      end

      it "uses the given host" do
        expect(url("/hello.jpg", host: "goats.com")).to eq("http://goats.com/hello.jpg")
      end

      it "uses the given port" do
        expect(url("/", host: "example.com", port: 1337)).to eq("http://example.com:1337/")
      end

      it "uses the given scheme" do
        expect(url("/", host: "example.com", scheme: "https")).to eq("https://example.com/")
      end

      it "regards :protocol as an alias for scheme" do
        expect(url("/", host: "example.com", protocol: "https")).to eq("https://example.com/")
      end

      it "strips extra characters from the scheme" do
        expect(url("/", host: "example.com", scheme: "https://")).to eq("https://example.com/")
      end

      it "uses the given path as a prefix" do
        expect(url("/my_file", host: "example.com", path: "/my_app")).to eq(
          "http://example.com/my_app/my_file"
        )
      end

      it "returns the original URL if it is absolute" do
        expect(url("http://foo.com/", host: "bar.com")).to eq("http://foo.com/")
      end

      it "returns the original URL if it is just an anchor" do
        expect(url("#top", host: "bar.com")).to eq("#top")
      end

      it "returns the base URL for blank paths" do
        expect(url("", host: "foo.com")).to eq("http://foo.com")
        expect(url(nil, host: "foo.com")).to eq("http://foo.com")
      end

      it "raises an error on invalid path" do
        expect {
          url("://", host: "example.com")
        }.to raise_error InvalidUrlPath, %r{://}
      end

      it "accepts base paths without a slash in the beginning" do
        expect(url("/bar", host: "example.com", path: "foo")).to eq("http://example.com/foo/bar")
        expect(url("/bar/", host: "example.com", path: "foo/")).to eq("http://example.com/foo/bar/")
      end

      it "accepts input paths without a slash in the beginning" do
        expect(url("bar", host: "example.com", path: "/foo")).to eq("http://example.com/foo/bar")
        expect(url("bar", host: "example.com", path: "/foo/")).to eq("http://example.com/foo/bar")
      end

      it "does not touch data: URIs" do
        # We've had failures with longer data URIs, but I have not been able to
        # pinpoint where the problem is. I suspect a specific version of Ruby.
        # This test might not actually catch the real issues since I couldn't
        # get it red for the reported cases.
        # It was solved by being more relaxed when determining if a URI is
        # absolute or not.
        data_uri = "data:image/png;dead/beef+/=="
        expect(url(data_uri, host: "example.com")).to eq(data_uri)
      end

      it "does not touch custom schemes" do
        expect(url("myapp://", host: "example.com")).to eq("myapp://")
      end

      it "does not care if absolute URLs have parse errors" do
        # Pipe character is invalid inside URLs, but that does not stop a whole
        # lot of templating/emailing systems for using them as template
        # markers.
        expect(url("https://foo.com/%|MARKETING_TOKEN|%", host: "example.com")).to eq("https://foo.com/%|MARKETING_TOKEN|%")
      end

      # A lot of email clients do not support schemeless URLs (it's a HTML5
      # feature) so we should add a scheme to them.
      context "on schemeless urls" do
        # Checking for host matches would be too complex, and it's not too hard
        # to assume that schemeless URLs to assets comes from a shared
        # configuration with a web page which uses HTTP and HTTPS in different
        # cases. That also means that we'd like to match the assets URLs with
        # whatever we want to link to, most likely.
        it "adds given scheme, even when host does not match" do
          result = url("//assets.myapp.com/foo.jpg", host: "example.com", scheme: "https")
          expect(result).to eq("https://assets.myapp.com/foo.jpg")
        end

        it "adds standard http: scheme when no scheme given" do
          result = url("//assets.myapp.com/foo.jpg", host: "example.com")
          expect(result).to eq("http://assets.myapp.com/foo.jpg")
        end

        it "adds scheme to invalid URLs" do
          result = url("//foo.com/%|TOKEN|%", scheme: "ftp", host: "example.com")
          expect(result).to eq("ftp://foo.com/%|TOKEN|%")
        end
      end
    end

    # URLs in resources that are not based inside the root requires that we may
    # specify a "custom base" to properly handle relative paths. Here's an
    # example:
    #
    #   # /
    #   #  document.html
    #   #  images/
    #   #    bg.png
    #   #  stylesheets/
    #   #    my_style.css
    #
    #   # stylesheets/my_style.css
    #   body { background-image: url(../images/bg.png); }
    #
    # In this example, the stylesheet refers to /images/bg.png by using a
    # relative path from /stylesheets/. In order to understand these cases, we
    # need to specify where the "base" is in relation to the root.
    describe "generating URLs with custom base" do
      it "resolves relative paths" do
        generator = UrlGenerator.new(host: "foo.com")
        expect(generator.generate_url("../images/bg.png", "/stylesheets")).to eq("http://foo.com/images/bg.png")
        expect(generator.generate_url("../images/bg.png", "email/stylesheets")).to eq("http://foo.com/email/images/bg.png")
        expect(generator.generate_url("images/bg.png", "email/")).to eq("http://foo.com/email/images/bg.png")
      end

      it "does not use the base when presented with a root-based path" do
        generator = UrlGenerator.new(host: "foo.com")
        expect(generator.generate_url("/images/bg.png", "/stylesheets")).to eq("http://foo.com/images/bg.png")
        expect(generator.generate_url("/images/bg.png", "email/stylesheets")).to eq("http://foo.com/images/bg.png")
      end
    end
  end
end
