From b1e6e7ef36384b0881d80d66e79f8e2a0feb0e89 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 24 Aug 2026 12:27:35 +0100 Subject: [PATCH] test: cover Locale.ROOT lowercasing paths from #1086 Motivation: #1086 switched a dozen call sites to Locale.ROOT but landed without tests, so TurkishISpec still only covers HttpCharsets and nothing exercises the directives. A regression in any of these paths would only show up for users running with a turkish default locale. Modification: Extend TurkishISpec with cases for `HttpHeader.parse`, `Uri` scheme normalization, `MediaTypes.forExtension` and `ErrorInfo.withErrorHeaderName`. Add TurkishLocaleDirectivesSpec covering `headerValueByName` and `overrideMethodWithParameter`, which live in the http module. Result: The header-name, scheme, file-extension, error-header-name, header-directive and method-override paths are pinned against locale sensitive case conversion. Tests: - sbt "http-core / Test / testOnly org.apache.pekko.http.scaladsl.model.TurkishISpec" - 5 passed - sbt "http-tests / Test / testOnly org.apache.pekko.http.scaladsl.server.directives.TurkishLocaleDirectivesSpec" - 2 passed; both fail when the two directives are reverted to default-locale case conversion - sbt headerCreateAll - added the Apache header to the new file - scalafmt --mode diff-ref=upstream/main - clean References: Refs #1086 --- .../http/scaladsl/model/TurkishISpec.scala | 29 +++++++++ .../TurkishLocaleDirectivesSpec.scala | 60 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/directives/TurkishLocaleDirectivesSpec.scala diff --git a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala index 58d6a89f69..76f9fdede6 100644 --- a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala +++ b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala @@ -40,5 +40,34 @@ class TurkishISpec extends AnyWordSpec with Matchers { Locale.setDefault(previousLocale) } } + + "parse a header name containing a capital I in the turkish locale" in withTurkishLocale { + HttpHeader.parse("If-Match", "\"xyzzy\"") match { + case HttpHeader.ParsingResult.Ok(header, Nil) => header shouldBe a[headers.`If-Match`] + case other => fail(s"Expected a modelled If-Match header but got $other") + } + } + + "normalize a uri scheme containing a capital I in the turkish locale" in withTurkishLocale { + Uri(scheme = "IPP", authority = Uri.Authority(Uri.Host("example.com"))).scheme shouldEqual "ipp" + } + + "resolve a media type for an upper case file extension in the turkish locale" in withTurkishLocale { + MediaTypes.forExtension("TIFF") shouldEqual MediaTypes.`image/tiff` + } + + "lowercase an error header name in the turkish locale" in withTurkishLocale { + ErrorInfo("summary", "detail").withErrorHeaderName("If-Match").errorHeaderName shouldEqual "if-match" + } + } + + private def withTurkishLocale(body: => Any): Unit = { + val previousLocale = Locale.getDefault + try { + Locale.setDefault(new Locale("tr", "TR")) + body + } finally { + Locale.setDefault(previousLocale) + } } } diff --git a/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/directives/TurkishLocaleDirectivesSpec.scala b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/directives/TurkishLocaleDirectivesSpec.scala new file mode 100644 index 0000000000..b6ea16264a --- /dev/null +++ b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/directives/TurkishLocaleDirectivesSpec.scala @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pekko.http.scaladsl.server.directives + +import java.util.Locale + +import org.apache.pekko +import pekko.http.scaladsl.model.headers.RawHeader +import pekko.http.scaladsl.server._ + +/** + * Directives that lower- or upper-case a user supplied name must do so with `Locale.ROOT`, + * otherwise they break in locales like tr-TR where 'I' does not lowercase to 'i'. + */ +class TurkishLocaleDirectivesSpec extends RoutingSpec { + + "The headerValueByName directive" should { + "extract a header whose name contains a capital I in the turkish locale" in withTurkishLocale { + lazy val route = headerValueByName("If-Match") { value => complete(value) } + Get("abc") ~> RawHeader("If-Match", "\"xyzzy\"") ~> route ~> check { + responseAs[String] shouldEqual "\"xyzzy\"" + } + } + } + + "The overrideMethodWithParameter directive" should { + "override with a method name containing an i in the turkish locale" in withTurkishLocale { + lazy val route = overrideMethodWithParameter("_method") { + get { complete("GET") } ~ + options { complete("OPTIONS") } + } + Get("/?_method=options") ~> route ~> check { responseAs[String] shouldEqual "OPTIONS" } + } + } + + private def withTurkishLocale(body: => Any): Unit = { + val previousLocale = Locale.getDefault + try { + Locale.setDefault(new Locale("tr", "TR")) + body + } finally { + Locale.setDefault(previousLocale) + } + } +}