test: isolate tray onboarding settings
Prevent tray onboarding tests from reading real user settings by allowing SettingsManager to use an explicit settings directory and using temp settings in onboarding tests. Document the isolation rule for future agents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
3b8793db37
commit
70b8ee6fb8
@ -22,4 +22,5 @@ If a command fails:
|
||||
Notes:
|
||||
|
||||
- If a build/test is blocked by an environmental lock (for example running executable locking output assemblies), stop/close the locking process and rerun.
|
||||
- Tray tests must isolate `SettingsManager` from real user settings. Do not use `new SettingsManager()` in tests unless the test intentionally reads `%APPDATA%\OpenClawTray\settings.json`; pass a temp settings directory or set `OPENCLAW_TRAY_DATA_DIR` before the test process starts.
|
||||
- Do not claim completion without reporting validation results.
|
||||
|
||||
@ -83,6 +83,12 @@ Translations are AI-generated following the repo convention. Technical terms (Ga
|
||||
|
||||
See [DEVELOPMENT.md](../DEVELOPMENT.md#developing--testing-the-onboarding-wizard) for build instructions, environment variables, and testing workflow.
|
||||
|
||||
### Test Isolation
|
||||
|
||||
`SettingsManager` loads `%APPDATA%\OpenClawTray\settings.json` by default. Onboarding tests must not use `new SettingsManager()` without an isolated settings directory, because local user settings such as `EnableNodeMode=true` change page ordering by intentionally skipping operator-only Wizard and Chat pages.
|
||||
|
||||
Use a temp settings directory for tests that construct `SettingsManager`, or set `OPENCLAW_TRAY_DATA_DIR` before the test process starts.
|
||||
|
||||
### Key Files
|
||||
|
||||
| Path | Purpose |
|
||||
|
||||
@ -12,17 +12,14 @@ public class SettingsManager
|
||||
{
|
||||
// OPENCLAW_TRAY_DATA_DIR overrides both this and App.DataPath so an isolated test
|
||||
// instance can run alongside the user's real tray without clobbering settings.
|
||||
private static readonly string SettingsDirectory =
|
||||
Environment.GetEnvironmentVariable("OPENCLAW_TRAY_DATA_DIR") is { Length: > 0 } overrideDir
|
||||
? overrideDir
|
||||
: Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"OpenClawTray");
|
||||
private static readonly string DefaultSettingsDirectory = GetDefaultSettingsDirectory();
|
||||
private static readonly string DefaultSettingsFilePath = Path.Combine(DefaultSettingsDirectory, "settings.json");
|
||||
|
||||
private static readonly string SettingsFilePath = Path.Combine(SettingsDirectory, "settings.json");
|
||||
private readonly string _settingsDirectory;
|
||||
private readonly string _settingsFilePath;
|
||||
|
||||
public static string SettingsDirectoryPath => SettingsDirectory;
|
||||
public static string SettingsPath => SettingsFilePath;
|
||||
public static string SettingsDirectoryPath => DefaultSettingsDirectory;
|
||||
public static string SettingsPath => DefaultSettingsFilePath;
|
||||
|
||||
// Connection
|
||||
public string GatewayUrl { get; set; } = "ws://localhost:18789";
|
||||
@ -76,18 +73,36 @@ public class SettingsManager
|
||||
public bool HasSeenActivityStreamTip { get; set; } = false;
|
||||
public string SkippedUpdateTag { get; set; } = "";
|
||||
|
||||
public SettingsManager()
|
||||
public SettingsManager() : this(DefaultSettingsDirectory)
|
||||
{
|
||||
}
|
||||
|
||||
public SettingsManager(string settingsDirectory)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(settingsDirectory))
|
||||
throw new ArgumentException("Settings directory cannot be empty.", nameof(settingsDirectory));
|
||||
|
||||
_settingsDirectory = settingsDirectory;
|
||||
_settingsFilePath = Path.Combine(settingsDirectory, "settings.json");
|
||||
Load();
|
||||
}
|
||||
|
||||
private static string GetDefaultSettingsDirectory()
|
||||
{
|
||||
return Environment.GetEnvironmentVariable("OPENCLAW_TRAY_DATA_DIR") is { Length: > 0 } overrideDir
|
||||
? overrideDir
|
||||
: Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"OpenClawTray");
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(SettingsFilePath))
|
||||
if (File.Exists(_settingsFilePath))
|
||||
{
|
||||
var json = File.ReadAllText(SettingsFilePath);
|
||||
var json = File.ReadAllText(_settingsFilePath);
|
||||
var loaded = SettingsData.FromJson(json);
|
||||
if (loaded != null)
|
||||
{
|
||||
@ -150,12 +165,12 @@ public class SettingsManager
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(SettingsDirectory);
|
||||
Directory.CreateDirectory(_settingsDirectory);
|
||||
// Lock the tray data dir to current user + SYSTEM + Administrators —
|
||||
// it co-locates the MCP bearer token, settings.json (which embeds
|
||||
// gateway/bootstrap credentials), and diagnostics jsonl. Other apps
|
||||
// running as the same user could otherwise read these freely.
|
||||
OpenClaw.Shared.Mcp.McpAuthToken.TryRestrictDataDirectoryAcl(SettingsDirectory);
|
||||
OpenClaw.Shared.Mcp.McpAuthToken.TryRestrictDataDirectoryAcl(_settingsDirectory);
|
||||
|
||||
var data = new SettingsData
|
||||
{
|
||||
@ -196,7 +211,7 @@ public class SettingsManager
|
||||
};
|
||||
|
||||
var json = data.ToJson();
|
||||
File.WriteAllText(SettingsFilePath, json);
|
||||
File.WriteAllText(_settingsFilePath, json);
|
||||
|
||||
Logger.Info("Settings saved");
|
||||
}
|
||||
|
||||
@ -16,11 +16,16 @@ public class ConnectionPageTopologyTests
|
||||
{
|
||||
private static OnboardingState CreateState(ConnectionMode m)
|
||||
{
|
||||
var s = new OnboardingState(new SettingsManager());
|
||||
var s = new OnboardingState(new SettingsManager(CreateTempSettingsDirectory()));
|
||||
s.Mode = m;
|
||||
return s;
|
||||
}
|
||||
|
||||
private static string CreateTempSettingsDirectory()
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), "OpenClaw.Tray.Tests", Guid.NewGuid().ToString("N"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPageOrder_WslMode_BehavesLikeLocal()
|
||||
{
|
||||
|
||||
@ -5,7 +5,22 @@ namespace OpenClaw.Tray.Tests;
|
||||
|
||||
public class OnboardingStateTests
|
||||
{
|
||||
private static OnboardingState CreateState() => new(new SettingsManager());
|
||||
private static OnboardingState CreateState() => new(CreateSettings());
|
||||
|
||||
private static SettingsManager CreateSettings(bool enableNodeMode = false)
|
||||
{
|
||||
var settings = new SettingsManager(CreateTempSettingsDirectory())
|
||||
{
|
||||
EnableNodeMode = enableNodeMode
|
||||
};
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
private static string CreateTempSettingsDirectory()
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), "OpenClaw.Tray.Tests", Guid.NewGuid().ToString("N"));
|
||||
}
|
||||
|
||||
#region GetPageOrder
|
||||
|
||||
@ -55,6 +70,20 @@ public class OnboardingStateTests
|
||||
pages);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPageOrder_NodeMode_SkipsOperatorPages()
|
||||
{
|
||||
var state = new OnboardingState(CreateSettings(enableNodeMode: true));
|
||||
state.Mode = ConnectionMode.Local;
|
||||
state.ShowChat = true;
|
||||
|
||||
var pages = state.GetPageOrder();
|
||||
|
||||
Assert.Equal(
|
||||
[OnboardingRoute.Welcome, OnboardingRoute.Connection, OnboardingRoute.Permissions, OnboardingRoute.Ready],
|
||||
pages);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ConnectionMode.Local)]
|
||||
[InlineData(ConnectionMode.Remote)]
|
||||
@ -122,7 +151,7 @@ public class OnboardingStateTests
|
||||
[Fact]
|
||||
public void Complete_SavesSettings()
|
||||
{
|
||||
var settings = new SettingsManager();
|
||||
var settings = CreateSettings();
|
||||
settings.GatewayUrl = "ws://test:9999";
|
||||
var state = new OnboardingState(settings);
|
||||
|
||||
@ -215,7 +244,7 @@ public class OnboardingStateTests
|
||||
[Fact]
|
||||
public void Settings_ReturnsInjectedManager()
|
||||
{
|
||||
var settings = new SettingsManager();
|
||||
var settings = CreateSettings();
|
||||
var state = new OnboardingState(settings);
|
||||
|
||||
Assert.Same(settings, state.Settings);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user