Skip to content

Commit 01f3707

Browse files
committed
test: add Client+Upgrade full dual-configuration integration tests
- Chain_ClientAndUpgrade_BothFullyConfigured: production deployment with Client and Upgrade bootstraps configured simultaneously with ALL non-conflicting parameters (30+ options each), hooks, listeners, extensions. Verifies independent instances. - Chain_ClientAndUpgrade_CompleteDeveloperWorkflow: real-world developer flow showing complete API surface for both roles in a single method
1 parent 7db806b commit 01f3707

1 file changed

Lines changed: 269 additions & 1 deletion

File tree

tests/CoreTest/Bootstrap/BootstrapFullParameterMatrixTests.cs

Lines changed: 269 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace CoreTest.Bootstrap
1111
{
1212
/// <summary>
13-
/// Full parameter matrix tests verifies ALL UpdateOptions constants
13+
/// Full parameter matrix tests �?verifies ALL UpdateOptions constants
1414
/// can be set via .Option() without throwing. Covers 39 options across
1515
/// core, deployment, silent, download, security, reporting, OSS, and
1616
/// blacklist categories.
@@ -222,6 +222,274 @@ [Fact] public void Chain_UpgradeWithExtensions()
222222
.AddCustomOption(new List<Func<bool>> { () => true });
223223
Assert.NotNull(b);
224224
}
225+
226+
/// <summary>
227+
/// Complete production deployment: Client + Upgrade bootstraps configured
228+
/// simultaneously with ALL non-conflicting parameters, hooks, listeners,
229+
/// and extension points. This is the closest equivalent to a real-world
230+
/// enterprise deployment where the developer configures both roles at once.
231+
/// </summary>
232+
[Fact]
233+
public void Chain_ClientAndUpgrade_BothFullyConfigured()
234+
{
235+
// Shared configuration used by both client and upgrade
236+
var sharedConfig = new Configinfo
237+
{
238+
UpdateUrl = "https://update.enterprise.com/api/v2",
239+
AppName = "Update.exe",
240+
MainAppName = "EnterpriseApp.exe",
241+
ClientVersion = "4.2.1",
242+
UpgradeClientVersion = "2.0.0",
243+
InstallPath = _testDir,
244+
AppSecretKey = "enterprise-prod-key-2026",
245+
ProductId = "enterprise-app-v4",
246+
UpdateLogUrl = "https://enterprise.com/releases",
247+
ReportUrl = "https://telemetry.enterprise.com/api/report",
248+
Scheme = "HMAC",
249+
Token = "hmac-prod-secret",
250+
Bowl = "Bowl.exe",
251+
Script = "#!/bin/bash\nset -e\nchmod +x /opt/enterprise/Update",
252+
DriverDirectory = "/opt/enterprise/drivers",
253+
BlackFiles = new List<string> { "*.pdb", "*.config", "appsettings.Development.json" },
254+
BlackFormats = new List<string> { ".log", ".tmp", ".cache", ".etl" },
255+
SkipDirectorys = new List<string> { "logs", "temp", "cache", "diagnostics", "__backups__" }
256+
};
257+
258+
// ==========================================
259+
// CLIENT bootstrap �?full production config
260+
// ==========================================
261+
var clientBootstrap = new GeneralUpdateBootstrap()
262+
// --- Core ---
263+
.Option(UpdateOptions.AppType, AppType.Client)
264+
.Option(UpdateOptions.DiffMode, DiffMode.Parallel)
265+
.Option(UpdateOptions.Encoding, Encoding.UTF8)
266+
.Option(UpdateOptions.Format, "ZIP")
267+
.Option(UpdateOptions.DownloadTimeout, 120)
268+
.Option(UpdateOptions.DriveEnabled, false)
269+
.Option(UpdateOptions.PatchEnabled, true)
270+
.Option(UpdateOptions.BackupEnabled, true)
271+
.Option(UpdateOptions.Mode, UpdateMode.Default)
272+
.Option(UpdateOptions.Silent, false)
273+
// --- Deployment ---
274+
.Option(UpdateOptions.UpdateUrl, "https://update.enterprise.com/api/v2")
275+
.Option(UpdateOptions.AppSecretKey, "enterprise-prod-key-2026")
276+
.Option(UpdateOptions.AppName, "Update.exe")
277+
.Option(UpdateOptions.MainAppName, "EnterpriseApp.exe")
278+
.Option(UpdateOptions.InstallPath, _testDir)
279+
.Option(UpdateOptions.ClientVersion, "4.2.1")
280+
.Option(UpdateOptions.UpgradeClientVersion, "2.0.0")
281+
.Option(UpdateOptions.Platform, PlatformType.Windows)
282+
// --- Download Performance ---
283+
.Option(UpdateOptions.MaxConcurrency, 4)
284+
.Option(UpdateOptions.EnableResume, true)
285+
.Option(UpdateOptions.RetryCount, 5)
286+
.Option(UpdateOptions.VerifyChecksum, true)
287+
.Option(UpdateOptions.RetryInterval, TimeSpan.FromSeconds(2))
288+
// --- Security ---
289+
.Option(UpdateOptions.Scheme, "HMAC")
290+
.Option(UpdateOptions.Token, "hmac-prod-secret")
291+
.Option(UpdateOptions.PermissionScript, "#!/bin/bash\nchmod +x /opt/enterprise/Update")
292+
// --- Reporting ---
293+
.Option(UpdateOptions.ReportUrl, "https://telemetry.enterprise.com/api/report")
294+
.Option(UpdateOptions.ProductId, "enterprise-app-v4")
295+
.Option(UpdateOptions.UpdateLogUrl, "https://enterprise.com/releases")
296+
// --- Blacklist ---
297+
.Option(UpdateOptions.BlackList, new BlackListConfig(
298+
new List<string> { "*.pdb", "*.config" },
299+
new List<string> { ".log", ".tmp" },
300+
new List<string> { "logs", "temp" }))
301+
.Option(UpdateOptions.Bowl, "Bowl.exe")
302+
.Option(UpdateOptions.Script, "chmod +x /opt/enterprise/Update")
303+
// --- Config ---
304+
.SetConfig(sharedConfig)
305+
// --- Hooks simulation (precheck) ---
306+
.AddListenerUpdatePrecheck(args =>
307+
{
308+
var hour = DateTime.Now.Hour;
309+
return hour < 2 || hour > 6; // Auto-approve updates during off-hours
310+
})
311+
// --- All event listeners ---
312+
.AddListenerUpdateInfo((s, e) => { })
313+
.AddListenerMultiAllDownloadCompleted((s, e) => { })
314+
.AddListenerMultiDownloadCompleted((s, e) => { })
315+
.AddListenerMultiDownloadError((s, e) => { })
316+
.AddListenerMultiDownloadStatistics((s, e) => { })
317+
.AddListenerException((s, e) => { })
318+
// --- Custom pre-update checks ---
319+
.AddCustomOption(new List<Func<bool>>
320+
{
321+
() => true, // Disk space check
322+
() => true, // Network connectivity check
323+
() => true // Service availability check
324+
});
325+
326+
Assert.NotNull(clientBootstrap);
327+
328+
// ==========================================
329+
// UPGRADE bootstrap �?full production config
330+
// ==========================================
331+
var upgradeBootstrap = new GeneralUpdateBootstrap()
332+
// --- Core (Upgrade role) ---
333+
.Option(UpdateOptions.AppType, AppType.Upgrade)
334+
.Option(UpdateOptions.DiffMode, DiffMode.Parallel)
335+
.Option(UpdateOptions.Encoding, Encoding.UTF8)
336+
.Option(UpdateOptions.Format, "ZIP")
337+
.Option(UpdateOptions.DownloadTimeout, 30) // Upgrade needs shorter timeout
338+
.Option(UpdateOptions.DriveEnabled, true) // Upgrade may install drivers
339+
.Option(UpdateOptions.PatchEnabled, true) // Apply differential patches
340+
.Option(UpdateOptions.BackupEnabled, false) // Upgrade doesn't need backup (client did it)
341+
.Option(UpdateOptions.Mode, UpdateMode.Default)
342+
// --- Deployment ---
343+
.Option(UpdateOptions.AppName, "Update.exe")
344+
.Option(UpdateOptions.MainAppName, "EnterpriseApp.exe")
345+
.Option(UpdateOptions.InstallPath, _testDir)
346+
.Option(UpdateOptions.ClientVersion, "4.2.1")
347+
.Option(UpdateOptions.Platform, PlatformType.Windows)
348+
// --- Download (Upgrade doesn't download in new design, but options available) ---
349+
.Option(UpdateOptions.MaxConcurrency, 2)
350+
.Option(UpdateOptions.VerifyChecksum, true)
351+
.Option(UpdateOptions.RetryInterval, TimeSpan.FromSeconds(1))
352+
// --- Security ---
353+
.Option(UpdateOptions.Scheme, "HMAC")
354+
.Option(UpdateOptions.Token, "hmac-prod-secret")
355+
.Option(UpdateOptions.PermissionScript, "#!/bin/bash\nchmod +x /opt/enterprise/Update")
356+
// --- Reporting (Upgrade reports its own status) ---
357+
.Option(UpdateOptions.ReportUrl, "https://telemetry.enterprise.com/api/report")
358+
.Option(UpdateOptions.ProductId, "enterprise-app-v4")
359+
// --- Blacklist ---
360+
.Option(UpdateOptions.BlackList, BlackListConfig.Empty)
361+
.Option(UpdateOptions.Script, "chmod +x /opt/enterprise/Update")
362+
// --- Config ---
363+
.SetConfig(sharedConfig)
364+
// --- Event listeners (Upgrade reports failures) ---
365+
.AddListenerException((s, e) => { })
366+
.AddListenerUpdateInfo((s, e) => { });
367+
368+
Assert.NotNull(upgradeBootstrap);
369+
370+
// Both bootstraps coexist without interference
371+
Assert.NotSame(clientBootstrap, upgradeBootstrap);
372+
}
373+
374+
/// <summary>
375+
/// Real-world developer workflow: configure both bootstraps with
376+
/// hooks, reporter, and full extension chain in a single method.
377+
/// Demonstrates the complete API surface a developer would use.
378+
/// </summary>
379+
[Fact]
380+
public void Chain_ClientAndUpgrade_CompleteDeveloperWorkflow()
381+
{
382+
var installPath = _testDir;
383+
var updateUrl = "https://update.myapp.com/api";
384+
var mainApp = "MyApp.exe";
385+
var currentVersion = "3.0.0";
386+
387+
// Step 1: Developer configures the Client bootstrap
388+
var client = new GeneralUpdateBootstrap()
389+
.Option(UpdateOptions.AppType, AppType.Client)
390+
.Option(UpdateOptions.DiffMode, DiffMode.Parallel)
391+
.Option(UpdateOptions.UpdateUrl, updateUrl)
392+
.Option(UpdateOptions.AppName, "Update.exe")
393+
.Option(UpdateOptions.MainAppName, mainApp)
394+
.Option(UpdateOptions.InstallPath, installPath)
395+
.Option(UpdateOptions.ClientVersion, currentVersion)
396+
.Option(UpdateOptions.UpgradeClientVersion, "2.0.0")
397+
.Option(UpdateOptions.Encoding, Encoding.UTF8)
398+
.Option(UpdateOptions.Format, "ZIP")
399+
.Option(UpdateOptions.DownloadTimeout, 120)
400+
.Option(UpdateOptions.PatchEnabled, true)
401+
.Option(UpdateOptions.BackupEnabled, true)
402+
.Option(UpdateOptions.MaxConcurrency, 4)
403+
.Option(UpdateOptions.EnableResume, true)
404+
.Option(UpdateOptions.RetryCount, 5)
405+
.Option(UpdateOptions.VerifyChecksum, true)
406+
.Option(UpdateOptions.RetryInterval, TimeSpan.FromSeconds(2))
407+
.Option(UpdateOptions.Scheme, "Bearer")
408+
.Option(UpdateOptions.Token, "client-jwt")
409+
.Option(UpdateOptions.ReportUrl, "https://telemetry.myapp.com/report")
410+
.Option(UpdateOptions.ProductId, "myapp-pro")
411+
.Option(UpdateOptions.UpdateLogUrl, "https://myapp.com/changelog")
412+
.Option(UpdateOptions.BlackList, new BlackListConfig(
413+
new List<string> { "*.pdb" },
414+
new List<string> { ".log", ".tmp" },
415+
new List<string> { "logs", "temp" }))
416+
.Option(UpdateOptions.Bowl, "Bowl.exe")
417+
.Option(UpdateOptions.Platform, PlatformType.Windows)
418+
.SetConfig(new Configinfo
419+
{
420+
UpdateUrl = updateUrl,
421+
AppName = "Update.exe",
422+
MainAppName = mainApp,
423+
ClientVersion = currentVersion,
424+
UpgradeClientVersion = "2.0.0",
425+
InstallPath = installPath,
426+
AppSecretKey = "myapp-key",
427+
ProductId = "myapp-pro",
428+
Scheme = "Bearer",
429+
Token = "client-jwt",
430+
ReportUrl = "https://telemetry.myapp.com/report",
431+
UpdateLogUrl = "https://myapp.com/changelog",
432+
Bowl = "Bowl.exe",
433+
BlackFiles = new List<string> { "*.pdb" },
434+
BlackFormats = new List<string> { ".log", ".tmp" },
435+
SkipDirectorys = new List<string> { "logs", "temp" }
436+
})
437+
.AddListenerUpdateInfo((s, e) => { }) // UI: show update available toast
438+
.AddListenerMultiDownloadCompleted((s, e) => { }) // UI: update progress bar
439+
.AddListenerMultiAllDownloadCompleted((s, e) => { }) // UI: ready to restart
440+
.AddListenerMultiDownloadError((s, e) => { }) // UI: show error dialog
441+
.AddListenerMultiDownloadStatistics((s, e) => { }) // UI: speed/ETA
442+
.AddListenerException((s, e) => { }) // Log to telemetry
443+
.AddListenerUpdatePrecheck(args => false) // Don't skip (default)
444+
.AddCustomOption(new List<Func<bool>>
445+
{
446+
() => Directory.Exists(installPath),
447+
() => true
448+
});
449+
450+
Assert.NotNull(client);
451+
452+
// Step 2: Developer configures the Upgrade bootstrap
453+
var upgrade = new GeneralUpdateBootstrap()
454+
.Option(UpdateOptions.AppType, AppType.Upgrade)
455+
.Option(UpdateOptions.DiffMode, DiffMode.Parallel)
456+
.Option(UpdateOptions.AppName, "Update.exe")
457+
.Option(UpdateOptions.MainAppName, mainApp)
458+
.Option(UpdateOptions.InstallPath, installPath)
459+
.Option(UpdateOptions.ClientVersion, currentVersion)
460+
.Option(UpdateOptions.Encoding, Encoding.UTF8)
461+
.Option(UpdateOptions.Format, "ZIP")
462+
.Option(UpdateOptions.PatchEnabled, true)
463+
.Option(UpdateOptions.VerifyChecksum, true)
464+
.Option(UpdateOptions.MaxConcurrency, 2)
465+
.Option(UpdateOptions.RetryCount, 3)
466+
.Option(UpdateOptions.RetryInterval, TimeSpan.FromSeconds(1))
467+
.Option(UpdateOptions.Scheme, "Bearer")
468+
.Option(UpdateOptions.Token, "upgrade-jwt")
469+
.Option(UpdateOptions.ReportUrl, "https://telemetry.myapp.com/report")
470+
.Option(UpdateOptions.ProductId, "myapp-pro")
471+
.Option(UpdateOptions.BlackList, BlackListConfig.Empty)
472+
.Option(UpdateOptions.Platform, PlatformType.Windows)
473+
.SetConfig(new Configinfo
474+
{
475+
UpdateUrl = updateUrl,
476+
AppName = "Update.exe",
477+
MainAppName = mainApp,
478+
ClientVersion = currentVersion,
479+
InstallPath = installPath,
480+
AppSecretKey = "myapp-key",
481+
Scheme = "Bearer",
482+
Token = "upgrade-jwt",
483+
ReportUrl = "https://telemetry.myapp.com/report"
484+
})
485+
.AddListenerException((s, e) => { }) // Upgrade logs its own errors
486+
.AddListenerUpdateInfo((s, e) => { });
487+
488+
Assert.NotNull(upgrade);
489+
490+
// Step 3: Verify independent instances
491+
Assert.NotSame(client, upgrade);
492+
}
225493
#endregion
226494
}
227495
}

0 commit comments

Comments
 (0)