Add more specialized negotiators

Fix issue with accept language setting
This commit is contained in:
George Dietrich 2020-12-22 13:59:40 -05:00
parent bf08531bca
commit 3850074f02
12 changed files with 211 additions and 14 deletions

View file

@ -0,0 +1,67 @@
require "./spec_helper"
struct CharsetNegotiatorTest < NegotiatorTestCase
@negotiator : ANG::CharsetNegotiator
def initialize
@negotiator = ANG::CharsetNegotiator.new
end
def test_best_unmatched_header : Nil
@negotiator.best("foo, bar, yo", {"baz"}).should be_nil
end
def test_best_ignores_missing_content : Nil
accept = @negotiator.best "en; q=0.1, fr; q=0.4, bu; q=1.0", {"en", "fr"}
accept = accept.should_not be_nil
accept.should be_a ANG::AcceptCharset
accept.value.should eq "fr"
end
def test_best_respects_priorities : Nil
accept = @negotiator.best "foo, bar, yo", {"yo"}
accept = accept.should_not be_nil
accept.should be_a ANG::AcceptCharset
accept.type.should eq "yo"
end
def test_best_respects_quality : Nil
accept = @negotiator.best "utf-8;q=0.5,iso-8859-1", {"iso-8859-1;q=0.3", "utf-8;q=0.9", "utf-16;q=1.0"}
accept = accept.should_not be_nil
accept.should be_a ANG::AcceptCharset
accept.type.should eq "utf-8"
end
@[DataProvider("best_data_provider")]
def test_best(header : String, priorities : Indexable(String), expected : String?) : Nil
accept = @negotiator.best header, priorities
if accept.nil?
expected.should be_nil
else
accept.should be_a ANG::AcceptCharset
accept.value.should eq expected
end
end
def best_data_provider : Tuple
php_pear_charset = "ISO-8859-1, Big5;q=0.6,utf-8;q=0.7, *;q=0.5"
php_pear_charset2 = "ISO-8859-1, Big5;q=0.6,utf-8;q=0.7"
{
{php_pear_charset, {"utf-8", "big5", "iso-8859-1", "shift-jis"}, "iso-8859-1"},
{php_pear_charset, {"utf-8", "big5", "shift-jis"}, "utf-8"},
{php_pear_charset, {"Big5", "shift-jis"}, "Big5"},
{php_pear_charset, {"shift-jis"}, "shift-jis"},
{php_pear_charset2, {"utf-8", "big5", "iso-8859-1", "shift-jis"}, "iso-8859-1"},
{php_pear_charset2, {"utf-8", "big5", "shift-jis"}, "utf-8"},
{php_pear_charset2, {"Big5", "shift-jis"}, "Big5"},
{"utf-8;q=0.6,iso-8859-5;q=0.9", {"iso-8859-5", "utf-8"}, "iso-8859-5"},
{"en, *;q=0.9", {"fr"}, "fr"},
# Quality of source factors
{php_pear_charset, {"iso-8859-1;q=0.5", "utf-8", "utf-16;q=1.0"}, "utf-8"},
{php_pear_charset, {"iso-8859-1;q=0.8", "utf-8", "utf-16;q=1.0"}, "iso-8859-1;q=0.8"},
}
end
end

View file

@ -0,0 +1,42 @@
require "./spec_helper"
struct EncodingNegotiatorTest < NegotiatorTestCase
@negotiator : ANG::EncodingNegotiator
def initialize
@negotiator = ANG::EncodingNegotiator.new
end
def test_best_unmatched_header : Nil
@negotiator.best("foo, bar, yo", {"baz"}).should be_nil
end
def test_best_respects_quality : Nil
accept = @negotiator.best "gzip;q=0.7,identity", {"identity;q=0.5", "gzip;q=0.9"}
accept = accept.should_not be_nil
accept.should be_a ANG::AcceptEncoding
accept.type.should eq "gzip"
end
@[DataProvider("best_data_provider")]
def test_best(header : String, priorities : Indexable(String), expected : String?) : Nil
accept = @negotiator.best header, priorities
if accept.nil?
expected.should be_nil
else
accept.should be_a ANG::AcceptEncoding
accept.value.should eq expected
end
end
def best_data_provider : Tuple
{
{"gzip;q=1.0, identity; q=0.5, *;q=0", {"identity"}, "identity"},
{"gzip;q=0.5, identity; q=0.5, *;q=0.7", {"bzip", "foo"}, "bzip"},
{"gzip;q=0.7, identity; q=0.5, *;q=0.7", {"gzip", "foo"}, "gzip"},
# Quality of source factors
{"gzip;q=0.7,identity", {"identity;q=0.5", "gzip;q=0.9"}, "gzip;q=0.9"},
}
end
end

View file

@ -0,0 +1,43 @@
require "./spec_helper"
struct LanguageNegotiatorTest < NegotiatorTestCase
@negotiator : ANG::LanguageNegotiator
def initialize
@negotiator = ANG::LanguageNegotiator.new
end
def test_best_respects_quality : Nil
accept = @negotiator.best "en;q=0.5,de", {"de;q=0.3", "en;q=0.9"}
accept = accept.should_not be_nil
accept.should be_a ANG::AcceptLanguage
accept.type.should eq "en"
end
@[DataProvider("best_data_provider")]
def test_best(header : String, priorities : Indexable(String), expected : String?) : Nil
accept = @negotiator.best header, priorities
if accept.nil?
expected.should be_nil
else
accept.should be_a ANG::AcceptLanguage
accept.value.should eq expected
end
end
def best_data_provider : Tuple
{
{"en, de", {"fr"}, nil},
{"foo, bar, yo", {"baz", "biz"}, nil},
{"fr-FR, en;q=0.8", {"en-US", "de-DE"}, "en-US"},
{"en, *;q=0.9", {"fr"}, "fr"},
{"foo, bar, yo", {"yo"}, "yo"},
{"en; q=0.1, fr; q=0.4, bu; q=1.0", {"en", "fr"}, "fr"},
{"en; q=0.1, fr; q=0.4, fu; q=0.9, de; q=0.2", {"en", "fu"}, "fu"},
{"fr, zh-Hans-CN;q=0.3", {"fr"}, "fr"},
# Quality of source factors
{"en;q=0.5,de", {"de;q=0.3", "en;q=0.9"}, "en;q=0.9"},
}
end
end

View file

@ -1,6 +1,6 @@
require "./spec_helper" require "./spec_helper"
struct NegotiatorTest < ASPEC::TestCase struct NegotiatorTest < NegotiatorTestCase
@negotiator : ANG::Negotiator @negotiator : ANG::Negotiator
def initialize def initialize
@ -18,20 +18,12 @@ struct NegotiatorTest < ASPEC::TestCase
@negotiator.best("/qwer", {"foo/bar"}, false).should be_nil @negotiator.best("/qwer", {"foo/bar"}, false).should be_nil
end end
def test_best_exception_handling : Nil def test_invalid_media_type : Nil
ex = expect_raises ANG::Exceptions::InvalidMediaType, "Invalid media type: '/qwer'." do ex = expect_raises ANG::Exceptions::InvalidMediaType, "Invalid media type: '/qwer'." do
@negotiator.best "foo/bar", {"/qwer"} @negotiator.best "foo/bar", {"/qwer"}
end end
ex.type.should eq "/qwer" ex.type.should eq "/qwer"
expect_raises ArgumentError, "priorities should not be empty." do
@negotiator.best "foo/bar", [] of String
end
expect_raises ArgumentError, "The header string should not be empty." do
@negotiator.best "", {"text/html"}
end
end end
@[DataProvider("best_data_provider")] @[DataProvider("best_data_provider")]

View file

@ -0,0 +1,11 @@
abstract struct NegotiatorTestCase < ASPEC::TestCase
def test_best_exception_handling : Nil
expect_raises ArgumentError, "priorities should not be empty." do
@negotiator.best "foo/bar", [] of String
end
expect_raises ArgumentError, "The header string should not be empty." do
@negotiator.best "", {"text/html"}
end
end
end

View file

@ -1,6 +1,7 @@
require "spec" require "spec"
require "athena-spec" require "athena-spec"
require "../src/athena-negotiation" require "../src/athena-negotiation"
require "./negotiator_test_case"
include ASPEC::Methods include ASPEC::Methods

View file

@ -8,7 +8,7 @@ struct Athena::Negotiation::AcceptLanguage < Athena::Negotiation::BaseAccept
def initialize(value : String) def initialize(value : String)
super value super value
parts = @value.split '-' parts = @type.split '-'
case parts.size case parts.size
when 2 when 2

View file

@ -3,6 +3,9 @@ require "./accept_match"
require "./accept_charset" require "./accept_charset"
require "./accept_encoding" require "./accept_encoding"
require "./accept_language" require "./accept_language"
require "./charset_negotiator"
require "./encoding_negotiator"
require "./language_negotiator"
require "./negotiator" require "./negotiator"
require "./exceptions/*" require "./exceptions/*"

View file

@ -0,0 +1,7 @@
require "./abstract_negotiator"
class Athena::Negotiation::CharsetNegotiator < Athena::Negotiation::AbstractNegotiator
private def create_header(header : String) : ANG::BaseAccept
ANG::AcceptCharset.new header
end
end

View file

@ -0,0 +1,7 @@
require "./abstract_negotiator"
class Athena::Negotiation::EncodingNegotiator < Athena::Negotiation::AbstractNegotiator
private def create_header(header : String) : ANG::BaseAccept
ANG::AcceptEncoding.new header
end
end

View file

@ -0,0 +1,26 @@
require "./abstract_negotiator"
class Athena::Negotiation::LanguageNegotiator < Athena::Negotiation::AbstractNegotiator
protected def match(accept : ANG::AcceptLanguage, priority : ANG::AcceptLanguage, index : Int32) : ANG::AcceptMatch?
accept_base = accept.language
priority_base = priority.language
accept_sub = accept.region
priority_sub = priority.region
base_equal = accept_base.downcase == priority_base.downcase
sub_equal = accept_sub.try &.downcase == priority_sub.try &.downcase
if ((accept_base == "*" || base_equal) && (accept_sub.nil? || sub_equal))
score = 10 * (base_equal ? 1 : 0) + (sub_equal ? 1 : 0)
return ANG::AcceptMatch.new accept.quality * priority.quality, score, index
end
nil
end
private def create_header(header : String) : ANG::BaseAccept
ANG::AcceptLanguage.new header
end
end

View file

@ -1,9 +1,7 @@
require "./abstract_negotiator" require "./abstract_negotiator"
class Athena::Negotiation::Negotiator < Athena::Negotiation::AbstractNegotiator class Athena::Negotiation::Negotiator < Athena::Negotiation::AbstractNegotiator
protected def match(accept : ANG::BaseAccept, priority : ANG::BaseAccept, index : Int32) : ANG::AcceptMatch? protected def match(accept : ANG::Accept, priority : ANG::Accept, index : Int32) : ANG::AcceptMatch?
return nil if !accept.is_a?(ANG::Accept) || !priority.is_a?(ANG::Accept)
accept_base = accept.base_part accept_base = accept.base_part
priority_base = priority.base_part priority_base = priority.base_part