Skip to content

Commit 66c2e25

Browse files
committed
tests: stop force-loading plugin into AppDomain - lets PluginLoader register MVC parts
The actual root cause of master CI #22's OTP-404 failure (and the v1.1.0-cycle GetService<TPluginService> null result I worked around with HTTP-only testing): the fixture ctor was force-loading the plugin assembly into the AppDomain via typeof(SamRockProtocolPlugin). PluginManager.AddPlugins at submodules/btcpayserver/BTCPayServer/ Plugins/PluginManager.cs:155 scans AppDomain.CurrentDomain.GetAssemblies() FIRST via PreloadPluginsFromAssemblies and registers any plugin it finds there with Loader=null (line 135). Later when the DEBUG_PLUGINS / plugins-folder path tries to add the same plugin via PluginLoader.CreateFromAssemblyFile, the dedup check at line 204 (if preloadedPlugins.Contains(identifier) continue) skips it. The AppDomain-scanned plugin then takes the Loader=null branch at line 246-256: AddAssemblyLoadContexts is skipped, GetPluginInstanceFromAssembly still runs (so the IBTCPayServerPlugin shows in DI), but mvcBuilder.AddPluginLoader(loader) at line 256 is SKIPPED because Loader is null. MVC ApplicationParts never includes the plugin's assembly, controllers never register, all routes 404. Fix: let the file-based DEBUG_PLUGINS / plugins-folder path resolve the plugin first via PluginLoader (Loader non-null), which triggers mvcBuilder.AddPluginLoader and registers the controller assembly with MVC. Remove the typeof() force-load that was sabotaging this. Also reverts the LoadPluginsInDefaultAssemblyContext attempt - that property exists on later BTCPay versions but not on the v2.1.6-20 submodule SamRockProtocol pins, so it doesn't compile here. The retry-with-backoff in the previous commit on this branch stays as cheap defense for any residual timing window, but the real fix is removing the force-load.
1 parent 39df92f commit 66c2e25

1 file changed

Lines changed: 9 additions & 13 deletions

File tree

SamRockProtocol.Tests/SharedPluginTestFixture.cs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ public ConfigurablePluginTestFixture(string testDirName = "SharedPluginTests", b
1313
{
1414
_testDirName = testDirName;
1515
_useNewDb = useNewDb;
16-
// Force-load the SamRockProtocol assembly into the AppDomain so
17-
// PluginManager.PreloadPluginsFromAssemblies discovers it via
18-
// AppDomain.CurrentDomain.GetAssemblies() before BTCPay startup.
19-
_ = typeof(SamRockProtocol.SamRockProtocolPlugin);
16+
// Do NOT force-load the SamRockProtocol assembly into the AppDomain
17+
// here. PluginManager.AddPlugins scans AppDomain assemblies first and
18+
// registers any plugin it finds with Loader=null. Plugins loaded via
19+
// that path skip mvcBuilder.AddPluginLoader at PluginManager.cs:255,
20+
// which means MVC ApplicationParts never includes the plugin's
21+
// assembly -> the IBTCPayServerPlugin shows in DI but its controller
22+
// routes 404. Let the DEBUG_PLUGINS / plugins-folder path resolve the
23+
// plugin via PluginLoader.CreateFromAssemblyFile so the Loader is
24+
// non-null and MVC integration happens.
2025
}
2126

2227
public ServerTester ServerTester { get; private set; }
@@ -37,15 +42,6 @@ public void Initialize(UnitTestBase testInstance)
3742

3843
var testDir = Path.Combine(Directory.GetCurrentDirectory(), _testDirName);
3944
ServerTester = testInstance.CreateServerTester(testDir, _useNewDb);
40-
// BTCPay defaults plugins to isolated AssemblyLoadContext so production
41-
// plugins can be unloaded/swapped without restarting. In tests the
42-
// isolated context makes the plugin's assembly invisible to the test
43-
// process's MVC ApplicationParts discovery, so the controllers never
44-
// route (the plugin shows in DI but POSTs to its endpoints 404).
45-
// Load into the default context so MVC can discover the controllers.
46-
// Mirrors rockstardev/btcPayServerPlugins.RockstarDev's test fixture
47-
// and closes the master CI #22 OTP-404 failure deterministically.
48-
ServerTester.PayTester.LoadPluginsInDefaultAssemblyContext = false;
4945
ServerTester.StartAsync().GetAwaiter().GetResult();
5046
}
5147
}

0 commit comments

Comments
 (0)