fix(app): scope the UI test fixture to its token, never in an installed build #146
No files matched your search
@@ -65,7 +65,7 @@ flowchart LR
|
||||
| Activity logs | out | Actions and errors; token *lengths* only | File ACL | `IActivityLog` callers never pass tokens |
|
||||
| `.git/gclo-recovery.json`, `gclo.checkoutpending` config key | out/in | Path-recovery plan; checkout-pending marker | File ACL | Written atomically; read only for the repository it belongs to |
|
||||
| Command line (CLI) | in | Options, org, target, filters | n/a | `OptionReader` / `RepoFilterSpec`; parallelism bounded 1..64; token options refused on argv |
|
||||
| `GCLO_DATA_DIR`, `GCLO_UITEST_FIXTURE` env | in | Data root override; offline test seam | n/a | Fixture only answers for the exact fixture token; never set in production |
|
||||
| `GCLO_DATA_DIR`, `GCLO_UITEST_FIXTURE` env | in | Data root override; offline test seam | n/a | Fixture only answers for the exact fixture token (every other token, and the question whether a folder is a repository, go to the real services); ignored by an installed build; logged when enabled; never set in production (#145) |
|
||||
| `wsl.exe -e sh -s` | out/in | POSIX script on stdin, org/repo/url as positional args, `GCLO_GIT_TOKEN` via `WSLENV` | PAT via env | Nothing is re-parsed by wsl.exe's command line; token never on argv or disk; UTF-16 wsl.exe messages decoded |
|
||||
| Update feed (Velopack) | in | `releases.<channel>.json`, packages | HTTPS; package hashes | Velopack verifies package hashes against the feed; channel never crosses |
|
||||
| Release assets (users) | in | Setup, portable, CLI zip, nupkg, SBOMs | HTTPS | `SHA256SUMS`, build-provenance and SBOM attestations (`gh attestation verify`) |
|
||||
|
||||
@@ -94,7 +94,8 @@ public sealed class AppSession : IDisposable
|
||||
|
||||
|
||||
|
||||
private bool LogContains(string fragment)
|
||||
/// <summary>True when any of the app's activity-log files holds <paramref name="fragment"/>.</summary>
|
||||
public bool LogContains(string fragment)
|
||||
{
|
||||
string logs = Path.Combine(DataDirectory, "logs");
|
||||
if(!Directory.Exists(logs))
|
||||
|
||||
@@ -37,6 +37,14 @@ public sealed partial class SmokeTests : UiTestBase
|
||||
|
||||
|
||||
|
||||
// (1b) The offline fixture this suite runs against announces itself in the app's
|
||||
// log: a session with it enabled is never mistaken for an ordinary one (#145).
|
||||
[Fact]
|
||||
public void ActivityLog_AfterLaunch_SaysTheUiTestFixtureIsEnabled()
|
||||
=> Assert.True(Session.LogContains("UI test fixture enabled"), "the app logs one line when the fixture is honoured");
|
||||
|
||||
|
||||
|
||||
// (2) The navigation pane carries the pinned Quick Sync plus both footer commands.
|
||||
[Fact]
|
||||
public void NavigationPane_AfterLaunch_ShowsQuickSyncAddAccountAndSyncAll()
|
||||
|
||||
@@ -114,6 +114,12 @@ public sealed partial class MainWindow : Window
|
||||
_log = new FileActivityLog(System.IO.Path.Combine(GcloPaths.DataRoot, "logs"));
|
||||
App.CrashLog = _log; // the unhandled-exception net now reaches the activity log
|
||||
_log.Info($"gclo {BuildVersion.Describe(typeof(MainWindow).Assembly)} starting.");
|
||||
if(UiTestFixture.IsEnabled)
|
||||
{
|
||||
// Worth a line in every log it applies to: with it on, one fixed token is
|
||||
// answered from a built-in organization instead of GitHub (#145).
|
||||
_log.Info("UI test fixture enabled (GCLO_UITEST_FIXTURE=1): the fixture token is answered offline.");
|
||||
}
|
||||
_updateService = new UpdateService(_log);
|
||||
_tokenVault = new CredentialManagerVault();
|
||||
_accountsStore = new AccountsStore(_tokenVault, log: _log);
|
||||
|
||||
+33
-4
@@ -20,7 +20,9 @@ namespace gclo;
|
||||
/// network - a fixed organization with three repositories, and a git client that
|
||||
/// succeeds without touching disk. Any other token (even with the variable set) goes
|
||||
/// to the real services, so the existing offline-input tests keep their meaning
|
||||
/// (a dummy token still gets GitHub's 401). Production never sets the variable.
|
||||
/// (a dummy token still gets GitHub's 401). Production never sets the variable, and an
|
||||
/// installed build ignores it: the seam exists for a build run from its output folder
|
||||
/// by the test suite, not for the app people install (#145).
|
||||
/// </summary>
|
||||
internal static class UiTestFixture
|
||||
{
|
||||
@@ -30,8 +32,13 @@ internal static class UiTestFixture
|
||||
/// <summary>The organization the fixture lists.</summary>
|
||||
public const string Organization = "fixture-org";
|
||||
|
||||
/// <summary>True when the process was started for the UI suite with the fixture enabled.</summary>
|
||||
public static bool IsEnabled { get; } = string.Equals(Environment.GetEnvironmentVariable("GCLO_UITEST_FIXTURE"), "1", StringComparison.Ordinal);
|
||||
/// <summary>
|
||||
/// True when the process was started for the UI suite with the fixture enabled, and
|
||||
/// is not an installed build.
|
||||
/// </summary>
|
||||
public static bool IsEnabled { get; } =
|
||||
string.Equals(Environment.GetEnvironmentVariable("GCLO_UITEST_FIXTURE"), "1", StringComparison.Ordinal)
|
||||
&& !IsInstalledBuild();
|
||||
|
||||
private static readonly IReadOnlyList<RepoDescriptor> s_repositories =
|
||||
[
|
||||
@@ -42,6 +49,25 @@ internal static class UiTestFixture
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// True when the app was installed by Velopack (it then has an app id). A build run
|
||||
/// from its output folder, which is what the UI suite launches, has none.
|
||||
/// </summary>
|
||||
private static bool IsInstalledBuild()
|
||||
{
|
||||
try
|
||||
{
|
||||
return !string.IsNullOrEmpty(Velopack.Locators.VelopackLocator.Current?.AppId);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
// No locator for this platform or layout: not an installed build.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static IRepositoryLister WrapRepositoryLister(IRepositoryLister real)
|
||||
=> IsEnabled ? new RepositoryLister(real) : real;
|
||||
|
||||
@@ -81,7 +107,10 @@ internal static class UiTestFixture
|
||||
/// <summary>Succeeds without writing anything; the real client handles every other token.</summary>
|
||||
private sealed class GitClient(IGitClient real) : IGitClient
|
||||
{
|
||||
public bool IsValidRepository(string path) => false;
|
||||
// No token is passed here, so the real client answers for everyone. The
|
||||
// fixture's own clones write nothing, so its folders are never repositories
|
||||
// and the fixture token always takes the clone path (#145).
|
||||
public bool IsValidRepository(string path) => real.IsValidRepository(path);
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user