282 lines
11 KiB
C#
282 lines
11 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text.Json;
|
|
using Covenant.Setup.Ui;
|
|
using Xunit;
|
|
|
|
namespace Covenant.Setup.Ui.Tests;
|
|
|
|
public class InstallerSessionControllerTests
|
|
{
|
|
[Fact]
|
|
public void HandleMessage_init_calls_ShowInit()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"init\",\"title\":\"My Title\",\"message\":\"My Message\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.Equal("ShowInit: title=My Title, message=My Message", view.Calls[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_init_falls_back_to_defaults()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"init\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.Equal("ShowInit: title=covenant-setup, message=covenant-setup", view.Calls[0]);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(1, 10, 10)]
|
|
[InlineData(5, 10, 50)]
|
|
[InlineData(10, 10, 100)]
|
|
[InlineData(12, 10, 100)]
|
|
[InlineData(-1, 10, 0)]
|
|
[InlineData(5, 0, 100)]
|
|
[InlineData(5, null, 100)]
|
|
[InlineData(null, null, 0)]
|
|
public void ComputePercent_clamps_and_calculates_correctly(int? current, int? total, int expected)
|
|
{
|
|
var percent = InstallerSessionController.ComputePercent(current, total);
|
|
Assert.Equal(expected, percent);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_progress_calls_ShowProgress_and_AppendLog()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"progress\",\"current_step\":2,\"total_steps\":5,\"message\":\"Step msg\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Equal(2, view.Calls.Count);
|
|
Assert.Equal("AppendLog: message=Step msg", view.Calls[0]);
|
|
Assert.Equal("ShowProgress: percent=40, message=Step msg", view.Calls[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_progress_omits_log_when_message_empty()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"progress\",\"current_step\":2,\"total_steps\":5}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.Equal("ShowProgress: percent=40, message=", view.Calls[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_log_calls_AppendLog()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"log\",\"message\":\"test log message\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.Equal("AppendLog: message=test log message", view.Calls[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_finish_calls_ShowFinished()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"finish\",\"message\":\"Successful installation!\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.Equal("ShowFinished: message=Successful installation!", view.Calls[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_fail_calls_ShowFailure_with_errata()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"fail\",\"app_name\":\"TestApp\",\"operation\":\"copy\",\"message\":\"Failed copy\",\"error\":\"E_OUTOFMEMORY\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.StartsWith("ShowFailure: failureMessage=Failed copy, errorDetails=E_OUTOFMEMORY", view.Calls[0]);
|
|
Assert.Contains("\"app_name\": \"TestApp\"", view.Calls[0]);
|
|
Assert.Contains("\"operation\": \"copy\"", view.Calls[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_fail_passes_support_contact_through_errata()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"fail\",\"app_name\":\"TestApp\",\"operation\":\"install\",\"message\":\"Failed install\",\"error\":\"E_FAIL\",\"errata\":{\"app_name\":\"TestApp\",\"support_contact\":\"support@example.com\"}}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.StartsWith("ShowFailure: failureMessage=Failed install, errorDetails=E_FAIL", view.Calls[0]);
|
|
Assert.Contains("\"support_contact\": \"support@example.com\"", view.Calls[0]);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("{\"support_contact\":\"support@example.com\"}", "support@example.com")]
|
|
[InlineData("{\"app_name\":\"TestApp\",\"support_contact\":\"1-800-555-0100\"}", "1-800-555-0100")]
|
|
[InlineData("{\"support_contact\":null}", null)]
|
|
[InlineData("{\"support_contact\":42}", null)]
|
|
[InlineData("{\"app_name\":\"TestApp\"}", null)]
|
|
[InlineData("[1,2,3]", null)]
|
|
[InlineData("not-json", null)]
|
|
public void ParseSupportContact_extracts_string_value_only(string errataJson, string? expected)
|
|
{
|
|
Assert.Equal(expected, InstallerUiWindow.ParseSupportContact(errataJson));
|
|
}
|
|
|
|
[Fact]
|
|
public void FormatFailureMessage_appends_support_contact_when_present()
|
|
{
|
|
var formatted = InstallerUiWindow.FormatFailureMessage("Error: program TestApp failed to install completely!", "support@example.com");
|
|
|
|
Assert.Equal(
|
|
"Error: program TestApp failed to install completely!\n\nFor support, please contact: support@example.com",
|
|
formatted);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
public void FormatFailureMessage_returns_message_unchanged_without_contact(string? supportContact)
|
|
{
|
|
Assert.Equal("Failed install", InstallerUiWindow.FormatFailureMessage("Failed install", supportContact));
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_prompt_shows_prompt_and_writes_response()
|
|
{
|
|
var view = new FakeInstallerView { PromptResponder = msg => "yes" };
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
using var sw = new StringWriter();
|
|
controller.ResponseWriter = sw;
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"prompt\",\"id\":\"p1\",\"title\":\"Ask\",\"buttons\":\"yes_no\",\"icon\":\"question\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.Equal("ShowPromptAsync: id=p1, title=Ask, buttons=yes_no, icon=question", view.Calls[0]);
|
|
|
|
var written = sw.ToString().Trim();
|
|
using var responseDoc = JsonDocument.Parse(written);
|
|
var root = responseDoc.RootElement;
|
|
Assert.Equal("prompt_response", root.GetProperty("type").GetString());
|
|
Assert.Equal("p1", root.GetProperty("id").GetString());
|
|
Assert.Equal("yes", root.GetProperty("result").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_close_calls_CloseView_and_returns_false()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"close\"}");
|
|
|
|
Assert.False(ok);
|
|
Assert.Single(view.Calls);
|
|
Assert.Equal("CloseView", view.Calls[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_unknown_type_returns_true_no_view_calls()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"unknown_message_type\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Empty(view.Calls);
|
|
}
|
|
|
|
[Fact]
|
|
public void RequestCancel_writes_cancel_request_and_keeps_writer_open()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
using var sw = new StringWriter();
|
|
controller.ResponseWriter = sw;
|
|
|
|
var sent = controller.RequestCancel();
|
|
|
|
Assert.True(sent);
|
|
Assert.NotNull(controller.ResponseWriter);
|
|
var written = sw.ToString().Trim();
|
|
using var responseDoc = JsonDocument.Parse(written);
|
|
Assert.Equal("cancel_request", responseDoc.RootElement.GetProperty("type").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public void RequestCancel_returns_false_without_writer()
|
|
{
|
|
var view = new FakeInstallerView();
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
|
|
Assert.False(controller.RequestCancel());
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_init_with_appName_and_installDir_shows_welcome_and_proceeds_on_install()
|
|
{
|
|
var view = new FakeInstallerView { WelcomeResponder = (app, dir, img) => "install" };
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
using var sw = new StringWriter();
|
|
controller.ResponseWriter = sw;
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"init\",\"title\":\"My Title\",\"app_name\":\"TestApp\",\"install_dir\":\"C:\\\\Test\",\"branding_image\":\"img.png\"}");
|
|
|
|
Assert.True(ok);
|
|
Assert.Equal(2, view.Calls.Count);
|
|
Assert.Equal("ShowWelcomeAsync: appName=TestApp, installDir=C:\\Test, brandingImage=img.png", view.Calls[0]);
|
|
Assert.Equal("ShowInit: title=My Title, message=My Title", view.Calls[1]);
|
|
|
|
var written = sw.ToString().Trim();
|
|
using var responseDoc = JsonDocument.Parse(written);
|
|
var root = responseDoc.RootElement;
|
|
Assert.Equal("welcome_response", root.GetProperty("type").GetString());
|
|
Assert.Equal("install", root.GetProperty("result").GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public void HandleMessage_init_with_appName_and_installDir_shows_welcome_and_cancels()
|
|
{
|
|
var view = new FakeInstallerView { WelcomeResponder = (app, dir, img) => "cancel" };
|
|
var controller = new InstallerSessionController("dummy", view);
|
|
using var sw = new StringWriter();
|
|
controller.ResponseWriter = sw;
|
|
|
|
var ok = controller.HandleMessage("{\"type\":\"init\",\"title\":\"My Title\",\"app_name\":\"TestApp\",\"install_dir\":\"C:\\\\Test\",\"branding_image\":\"img.png\"}");
|
|
|
|
Assert.False(ok);
|
|
Assert.Equal(2, view.Calls.Count);
|
|
Assert.Equal("ShowWelcomeAsync: appName=TestApp, installDir=C:\\Test, brandingImage=img.png", view.Calls[0]);
|
|
Assert.Equal("CloseView", view.Calls[1]);
|
|
|
|
var written = sw.ToString().Trim();
|
|
using var responseDoc = JsonDocument.Parse(written);
|
|
var root = responseDoc.RootElement;
|
|
Assert.Equal("welcome_response", root.GetProperty("type").GetString());
|
|
Assert.Equal("cancel", root.GetProperty("result").GetString());
|
|
}
|
|
}
|