require 'abstract_unit'

module ActionDispatch
  module Journey
    module Path
      class TestPattern < ActiveSupport::TestCase
        SEPARATORS = ["/", ".", "?"].join

        x = /.+/
        {
          '/:controller(/:action)'       => %r{\A/(#{x})(?:/([^/.?]+))?\Z},
          '/:controller/foo'             => %r{\A/(#{x})/foo\Z},
          '/:controller/:action'         => %r{\A/(#{x})/([^/.?]+)\Z},
          '/:controller'                 => %r{\A/(#{x})\Z},
          '/:controller(/:action(/:id))' => %r{\A/(#{x})(?:/([^/.?]+)(?:/([^/.?]+))?)?\Z},
          '/:controller/:action.xml'     => %r{\A/(#{x})/([^/.?]+)\.xml\Z},
          '/:controller.:format'         => %r{\A/(#{x})\.([^/.?]+)\Z},
          '/:controller(.:format)'       => %r{\A/(#{x})(?:\.([^/.?]+))?\Z},
          '/:controller/*foo'            => %r{\A/(#{x})/(.+)\Z},
          '/:controller/*foo/bar'        => %r{\A/(#{x})/(.+)/bar\Z},
          '/:foo|*bar'                   => %r{\A/(?:([^/.?]+)|(.+))\Z},
        }.each do |path, expected|
          define_method(:"test_to_regexp_#{path}") do
            path = Pattern.build(
              path,
              { :controller => /.+/ },
              SEPARATORS,
              true
            )
            assert_equal(expected, path.to_regexp)
          end
        end

        {
          '/:controller(/:action)'       => %r{\A/(#{x})(?:/([^/.?]+))?},
          '/:controller/foo'             => %r{\A/(#{x})/foo},
          '/:controller/:action'         => %r{\A/(#{x})/([^/.?]+)},
          '/:controller'                 => %r{\A/(#{x})},
          '/:controller(/:action(/:id))' => %r{\A/(#{x})(?:/([^/.?]+)(?:/([^/.?]+))?)?},
          '/:controller/:action.xml'     => %r{\A/(#{x})/([^/.?]+)\.xml},
          '/:controller.:format'         => %r{\A/(#{x})\.([^/.?]+)},
          '/:controller(.:format)'       => %r{\A/(#{x})(?:\.([^/.?]+))?},
          '/:controller/*foo'            => %r{\A/(#{x})/(.+)},
          '/:controller/*foo/bar'        => %r{\A/(#{x})/(.+)/bar},
          '/:foo|*bar'                   => %r{\A/(?:([^/.?]+)|(.+))},
        }.each do |path, expected|
          define_method(:"test_to_non_anchored_regexp_#{path}") do
            path = Pattern.build(
              path,
              { :controller => /.+/ },
              SEPARATORS,
              false
            )
            assert_equal(expected, path.to_regexp)
          end
        end

        {
          '/:controller(/:action)'       => %w{ controller action },
          '/:controller/foo'             => %w{ controller },
          '/:controller/:action'         => %w{ controller action },
          '/:controller'                 => %w{ controller },
          '/:controller(/:action(/:id))' => %w{ controller action id },
          '/:controller/:action.xml'     => %w{ controller action },
          '/:controller.:format'         => %w{ controller format },
          '/:controller(.:format)'       => %w{ controller format },
          '/:controller/*foo'            => %w{ controller foo },
          '/:controller/*foo/bar'        => %w{ controller foo },
        }.each do |path, expected|
          define_method(:"test_names_#{path}") do
            path = Pattern.build(
              path,
              { :controller => /.+/ },
              SEPARATORS,
              true
            )
            assert_equal(expected, path.names)
          end
        end

        def test_to_regexp_with_extended_group
          path = Pattern.build(
            '/page/:name',
            { :name => /
              #ROFL
              (tender|love
              #MAO
              )/x },
            SEPARATORS,
            true
          )
          assert_match(path, '/page/tender')
          assert_match(path, '/page/love')
          assert_no_match(path, '/page/loving')
        end

        def test_optional_names
          [
            ['/:foo(/:bar(/:baz))', %w{ bar baz }],
            ['/:foo(/:bar)', %w{ bar }],
            ['/:foo(/:bar)/:lol(/:baz)', %w{ bar baz }],
          ].each do |pattern, list|
            path = Pattern.from_string pattern
            assert_equal list.sort, path.optional_names.sort
          end
        end

        def test_to_regexp_match_non_optional
          path = Pattern.build(
            '/:name',
            { :name => /\d+/ },
            SEPARATORS,
            true
          )
          assert_match(path, '/123')
          assert_no_match(path, '/')
        end

        def test_to_regexp_with_group
          path = Pattern.build(
            '/page/:name',
            { :name => /(tender|love)/ },
            SEPARATORS,
            true
          )
          assert_match(path, '/page/tender')
          assert_match(path, '/page/love')
          assert_no_match(path, '/page/loving')
        end

        def test_ast_sets_regular_expressions
          requirements = { :name => /(tender|love)/, :value => /./ }
          path = Pattern.build(
            '/page/:name/:value',
            requirements,
            SEPARATORS,
            true
          )

          nodes = path.ast.grep(Nodes::Symbol)
          assert_equal 2, nodes.length
          nodes.each do |node|
            assert_equal requirements[node.to_sym], node.regexp
          end
        end

        def test_match_data_with_group
          path = Pattern.build(
            '/page/:name',
            { :name => /(tender|love)/ },
            SEPARATORS,
            true
          )
          match = path.match '/page/tender'
          assert_equal 'tender', match[1]
          assert_equal 2, match.length
        end

        def test_match_data_with_multi_group
          path = Pattern.build(
            '/page/:name/:id',
            { :name => /t(((ender|love)))()/ },
            SEPARATORS,
            true
          )
          match = path.match '/page/tender/10'
          assert_equal 'tender', match[1]
          assert_equal '10', match[2]
          assert_equal 3, match.length
          assert_equal %w{ tender 10 }, match.captures
        end

        def test_star_with_custom_re
          z = /\d+/
          path = Pattern.build(
            '/page/*foo',
            { :foo => z },
            SEPARATORS,
            true
          )
          assert_equal(%r{\A/page/(#{z})\Z}, path.to_regexp)
        end

        def test_insensitive_regexp_with_group
          path = Pattern.build(
            '/page/:name/aaron',
            { :name => /(tender|love)/i },
            SEPARATORS,
            true
          )
          assert_match(path, '/page/TENDER/aaron')
          assert_match(path, '/page/loVE/aaron')
          assert_no_match(path, '/page/loVE/AAron')
        end

        def test_to_regexp_with_strexp
          path = Pattern.build('/:controller', { }, SEPARATORS, true)
          x = %r{\A/([^/.?]+)\Z}

          assert_equal(x.source, path.source)
        end

        def test_to_regexp_defaults
          path = Pattern.from_string '/:controller(/:action(/:id))'
          expected = %r{\A/([^/.?]+)(?:/([^/.?]+)(?:/([^/.?]+))?)?\Z}
          assert_equal expected, path.to_regexp
        end

        def test_failed_match
          path = Pattern.from_string '/:controller(/:action(/:id(.:format)))'
          uri = 'content'

          assert_not path =~ uri
        end

        def test_match_controller
          path = Pattern.from_string '/:controller(/:action(/:id(.:format)))'
          uri = '/content'

          match = path =~ uri
          assert_equal %w{ controller action id format }, match.names
          assert_equal 'content', match[1]
          assert_nil match[2]
          assert_nil match[3]
          assert_nil match[4]
        end

        def test_match_controller_action
          path = Pattern.from_string '/:controller(/:action(/:id(.:format)))'
          uri = '/content/list'

          match = path =~ uri
          assert_equal %w{ controller action id format }, match.names
          assert_equal 'content', match[1]
          assert_equal 'list', match[2]
          assert_nil match[3]
          assert_nil match[4]
        end

        def test_match_controller_action_id
          path = Pattern.from_string '/:controller(/:action(/:id(.:format)))'
          uri = '/content/list/10'

          match = path =~ uri
          assert_equal %w{ controller action id format }, match.names
          assert_equal 'content', match[1]
          assert_equal 'list', match[2]
          assert_equal '10', match[3]
          assert_nil match[4]
        end

        def test_match_literal
          path = Path::Pattern.from_string "/books(/:action(.:format))"

          uri = '/books'
          match = path =~ uri
          assert_equal %w{ action format }, match.names
          assert_nil match[1]
          assert_nil match[2]
        end

        def test_match_literal_with_action
          path = Path::Pattern.from_string "/books(/:action(.:format))"

          uri = '/books/list'
          match = path =~ uri
          assert_equal %w{ action format }, match.names
          assert_equal 'list', match[1]
          assert_nil match[2]
        end

        def test_match_literal_with_action_and_format
          path = Path::Pattern.from_string "/books(/:action(.:format))"

          uri = '/books/list.rss'
          match = path =~ uri
          assert_equal %w{ action format }, match.names
          assert_equal 'list', match[1]
          assert_equal 'rss', match[2]
        end
      end
    end
  end
end
