Skip to content

Commit

Permalink
Finalize acceptance tests and logic fixup.
Browse files Browse the repository at this point in the history
  • Loading branch information
Matasx committed Dec 9, 2024
1 parent b1e24c1 commit 3baccd6
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 23 deletions.
16 changes: 12 additions & 4 deletions Modules/GenHTTP.Modules.I18n/Provider/LocalizationConcern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public sealed class LocalizationConcern : IConcern
private readonly CultureInfo _defaultCulture;
private readonly CultureSelectorCombinedAsyncDelegate _cultureSelector;
private readonly CultureFilterAsyncDelegate _cultureFilter;
private readonly CultureSetterAsyncDelegate _cultureSetter;
private readonly AsyncOrSyncSetter[] _cultureSetters;

#endregion

Expand All @@ -24,7 +24,7 @@ public LocalizationConcern(
CultureInfo defaultCulture,
CultureSelectorCombinedAsyncDelegate cultureSelector,
CultureFilterAsyncDelegate cultureFilter,
CultureSetterAsyncDelegate cultureSetter
AsyncOrSyncSetter[] cultureSetters
)
{
Content = content;
Expand All @@ -33,7 +33,7 @@ CultureSetterAsyncDelegate cultureSetter
_cultureSelector = cultureSelector;
_cultureFilter = cultureFilter;

_cultureSetter = cultureSetter;
_cultureSetters = cultureSetters;
}

#endregion
Expand All @@ -56,7 +56,15 @@ CultureSetterAsyncDelegate cultureSetter
{
var culture = await ResolveCultureInfoAsync(request) ?? _defaultCulture;

await _cultureSetter(request, culture);
foreach(var _cultureSetter in _cultureSetters)
{
_cultureSetter.SyncSetter?.Invoke(request, culture);

if (_cultureSetter.AsyncSetter != null)
{
await _cultureSetter.AsyncSetter(request, culture);
}
}

return await Content.HandleAsync(request);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ public sealed class LocalizationConcernBuilder : IConcernBuilder
{
#region Fields

private CultureInfo _defaultCulture = CultureInfo.CurrentCulture;
private CultureInfo _defaultCulture = CultureInfo.CurrentUICulture;

private readonly List<CultureSelectorAsyncDelegate> _cultureSelectors = [];
private CultureFilterAsyncDelegate _cultureFilter = (_, _) => ValueTask.FromResult(true);
private readonly List<CultureSetterAsyncDelegate> _cultureSetters = [];
private readonly List<AsyncOrSyncSetter> _cultureSetters = [];

#endregion

Expand Down Expand Up @@ -119,15 +119,14 @@ public LocalizationConcernBuilder Setter(Action<CultureInfo> cultureSetter)
=> Setter((_, culture) => cultureSetter(culture));

public LocalizationConcernBuilder Setter(CultureSetterDelegate cultureSetter)
=> Setter((request, culture) =>
{
cultureSetter(request, culture);
return ValueTask.CompletedTask;
});
{
_cultureSetters.Add(new(SyncSetter: cultureSetter));
return this;
}

public LocalizationConcernBuilder Setter(CultureSetterAsyncDelegate cultureSetter)
{
_cultureSetters.Add(cultureSetter);
_cultureSetters.Add(new (AsyncSetter: cultureSetter));
return this;
}

Expand Down Expand Up @@ -160,7 +159,7 @@ public IConcern Build(IHandler content)
_defaultCulture,
CultureSelector,
_cultureFilter,
CultureSetter
[.. _cultureSetters]
);
}

Expand All @@ -180,14 +179,5 @@ private async IAsyncEnumerable<CultureInfo> CultureSelector(IRequest request)
}
}


private async ValueTask CultureSetter(IRequest request, CultureInfo cultureInfo)
{
foreach (var setter in _cultureSetters)
{
await setter(request, cultureInfo);
}
}

#endregion
}
2 changes: 2 additions & 0 deletions Modules/GenHTTP.Modules.I18n/Provider/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

namespace GenHTTP.Modules.I18n.Provider;

public sealed record AsyncOrSyncSetter(CultureSetterDelegate? SyncSetter = null, CultureSetterAsyncDelegate? AsyncSetter = null);

/// <summary>
/// Delegate to extract the cultures for a given request.
/// </summary>
Expand Down
6 changes: 5 additions & 1 deletion Testing/Acceptance/Modules/I18n/LanguageParserTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using GenHTTP.Modules.I18n.Parsers;
using GenHTTP.Api.Protocol;
using GenHTTP.Modules.Functional;
using GenHTTP.Modules.I18n;
using GenHTTP.Modules.I18n.Parsers;
using GenHTTP.Modules.I18n.Provider;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Globalization;

Expand Down
251 changes: 251 additions & 0 deletions Testing/Acceptance/Modules/I18n/LocalizationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
using GenHTTP.Api.Protocol;
using GenHTTP.Modules.Functional;
using GenHTTP.Modules.I18n;
using GenHTTP.Modules.I18n.Provider;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Globalization;

namespace GenHTTP.Testing.Acceptance.Modules.I18n;

[TestClass]
public sealed class LocalizationTests
{
[TestMethod]
[MultiEngineTest]
public async Task TestDefault(TestEngine engine)
{
var currentCulture = CultureInfo.CurrentUICulture;

var localization = Localization.Create();

await TestLocalization(engine, localization, _ =>
{
Assert.AreEqual(currentCulture, CultureInfo.CurrentUICulture);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestFromStatic(TestEngine engine)
{
var localization = Localization
.Create()
.FromRequest(_ => [CultureInfo.CreateSpecificCulture("fr")]);

await TestLocalization(engine, localization, _ =>
{
Assert.AreEqual(CultureInfo.CreateSpecificCulture("fr"), CultureInfo.CurrentUICulture);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestFromQuery(TestEngine engine)
{
var localization = Localization
.Create()
.FromQuery();

await TestLocalization(engine, localization,
path: "?lang=cs-CZ",
_ =>
{
Assert.AreEqual(CultureInfo.CreateSpecificCulture("cs-CZ"), CultureInfo.CurrentUICulture);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestFromHeader(TestEngine engine)
{
var localization = Localization
.Create()
.FromHeader();

await TestLocalization(engine, localization,
request =>
{
request.Headers.Add("Accept-Language", "mn-MN");
},
_ =>
{
Assert.AreEqual(CultureInfo.CreateSpecificCulture("mn-MN"), CultureInfo.CurrentUICulture);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestFromCookie(TestEngine engine)
{
var localization = Localization
.Create()
.FromCookie();

await TestLocalization(engine, localization,
request =>
{
request.Headers.Add("Cookie", "lang=quz-EC");
},
_ =>
{
Assert.AreEqual(CultureInfo.CreateSpecificCulture("quz-EC"), CultureInfo.CurrentUICulture);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestSupportsList(TestEngine engine)
{
var localization = Localization
.Create()
.FromLanguage(_ => "en,de,cs,fr")
.Supports([CultureInfo.CreateSpecificCulture("fr"), CultureInfo.CreateSpecificCulture("cs")]);

await TestLocalization(engine, localization,
_ =>
{
Assert.AreEqual(CultureInfo.CreateSpecificCulture("cs"), CultureInfo.CurrentUICulture);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestSupportsPredicate(TestEngine engine)
{
var localization = Localization
.Create()
.FromLanguage(_ => "en,de,cs,fr")
.Supports(culture => culture.Equals(CultureInfo.CreateSpecificCulture("fr")));

await TestLocalization(engine, localization,
_ =>
{
Assert.AreEqual(CultureInfo.CreateSpecificCulture("fr"), CultureInfo.CurrentUICulture);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestSetterCurrentCulture(TestEngine engine)
{
var localization = Localization
.Create()
.FromRequest(_ => [CultureInfo.CreateSpecificCulture("fr")])
.Setter(currentCulture: true, currentUICulture: false);

await TestLocalization(engine, localization, _ =>
{
Assert.AreEqual(CultureInfo.CreateSpecificCulture("fr"), CultureInfo.CurrentCulture);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestSetterCustom(TestEngine engine)
{
var localization = Localization
.Create()
.FromLanguage(_ => "de")
.Setter((request, culture) => request.Properties["culture"] = culture);

await TestLocalization(engine, localization, request =>
{
Assert.AreEqual(CultureInfo.CreateSpecificCulture("de"), request.Properties["culture"]);
});
}

[TestMethod]
[MultiEngineTest]
public async Task TestMultipleMixed(TestEngine engine)
{
var localization = Localization
.Create()
.FromQuery()
.FromCookie()
.FromHeader()
.FromLanguage(_ => "de")
.Supports([CultureInfo.CreateSpecificCulture("de")])
.Setter(currentCulture: true)
.Setter((request, culture) => request.Properties["culture"] = culture);

await TestLocalization(engine, localization,
path: "?lang=cs-CZ",
request =>
{
request.Headers.Add("Accept-Language", "mn-MN");
request.Headers.Add("Cookie", "lang=quz-EC");
},
request =>
{
var expected = CultureInfo.CreateSpecificCulture("de");

Assert.AreEqual(expected, request.Properties["culture"]);
Assert.AreEqual(expected, CultureInfo.CurrentCulture);
Assert.AreEqual(expected, CultureInfo.CurrentUICulture);
});
}

private static Task TestLocalization(
TestEngine engine,
LocalizationConcernBuilder localization,
Action<IRequest> requestAssert
)
=> TestLocalization(engine, localization, null, null, requestAssert);

private static Task TestLocalization(
TestEngine engine,
LocalizationConcernBuilder localization,
string path,
Action<IRequest> requestAssert
)
=> TestLocalization(engine, localization, path, null, requestAssert);

private static Task TestLocalization(
TestEngine engine,
LocalizationConcernBuilder localization,
Action<HttpRequestMessage> requestSetup,
Action<IRequest> requestAssert
)
=> TestLocalization(engine, localization, null, requestSetup, requestAssert);

private static async Task TestLocalization(
TestEngine engine,
LocalizationConcernBuilder localization,
string? path,
Action<HttpRequestMessage>? requestSetup,
Action<IRequest> requestAssert
)
{
Exception? assertException = null;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;

var handler = Inline
.Create()
.Add(localization)
.Get((IRequest request) =>
{
try
{
requestAssert(request);
}
catch (Exception e)
{
assertException = e;
}
});

await using var host = await TestHost.RunAsync(handler, engine: engine);

using var request = host.GetRequest(path);
requestSetup?.Invoke(request);

using var _ = await host.GetResponseAsync(request);

if (assertException != null)
{
throw assertException;
}
}
}

0 comments on commit 3baccd6

Please sign in to comment.