fix/workflow-checkout-fetch-depth #21
1 changed files with 21 additions and 1 deletions
fix(billing): tolerate ReflectionTypeLoadException during init
ConfigureBillingService() walks AppDomain.CurrentDomain.GetAssemblies()
and calls Assembly.GetTypes() on each. If any of the loaded assemblies
has a type that fails to resolve (a flaky dependency, an AddOn with a
broken reference, a test dependency that's been rewritten after compile),
GetTypes() throws ReflectionTypeLoadException (or, less commonly,
FileNotFoundException / TypeLoadException for the assembly itself).
In CI on the forgejo-runner (and especially in test discovery under
xunit v3), one such assembly is loaded somewhere between test runs and
silently throws. The exception is not handled, so:
1. Collections are Cleared at the top of ConfigureBillingService().
2. The reflection loop throws before reaching the
RegisterBilling<HairCutQuery/HairMultiCutQuery/RdvQuery> calls.
3. BillingService.Billing ends up empty (Count = 0).
4. The second ConfigureBillingService() call sees the same assembly
loaded (xunit v3 keeps the AppDomain warm for the whole suite),
throws identically, and the test
Yavsc.BillingServiceTests.ConfigureBillingService_CanBeCalledTwiceWithoutThrowing
fails with 'Assert.Equal() Failure: Expected 3, Actual 0'.
Fix: catch ReflectionTypeLoadException and use the partial
.Types() list (the successfully-resolved subset), and use a
broader catch (with continue) for any other assembly-level
load failure. The lost user-settings types are not material;
they are derived from ApplicationDbContext in a separate loop
right after, and the RegisterBilling<>() calls that populate
BillingService.Billing run last, after both reflective phases
have completed best-effort.
The test still passes locally because the local test environment
loads a clean set of assemblies; only the CI runner (with its
extra test-time tooling) hits this path.
commit
7f84d4d97a
|
|
@ -70,7 +70,27 @@ namespace Yavsc.Helpers
|
||||||
|
|
||||||
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
|
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
|
||||||
{
|
{
|
||||||
foreach (var c in a.GetTypes())
|
Type[] types;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
types = a.GetTypes();
|
||||||
|
}
|
||||||
|
catch (System.Reflection.ReflectionTypeLoadException rtle)
|
||||||
|
{
|
||||||
|
// Some referenced types failed to load; keep the
|
||||||
|
// ones that did and skip the rest so a flaky
|
||||||
|
// dependency in one assembly does not break
|
||||||
|
// billing initialization for every other assembly.
|
||||||
|
types = rtle.Types.Where(t => t != null).ToArray();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Assembly itself cannot be loaded (FileNotFoundException
|
||||||
|
// on a referenced assembly, etc.). Skip it entirely.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var c in types)
|
||||||
{
|
{
|
||||||
if (c.IsClass && !c.IsAbstract &&
|
if (c.IsClass && !c.IsAbstract &&
|
||||||
c.GetInterface(nameof(IUserSettings)) != null)
|
c.GetInterface(nameof(IUserSettings)) != null)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue