Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -349,25 +349,49 @@ public async Task SaveStateAsync(
// Serialize to JSON
var json = JsonSerializer.Serialize(dynamicData, DefaultJsonOptions);

// Only update in current directory if it already exists
var currentDirPath = Path.Combine(Environment.CurrentDirectory, statePath);
if (File.Exists(currentDirPath))
// If an absolute path is provided, use it directly (for testing and explicit control)
if (Path.IsPathRooted(statePath))
{
try
{
// Save the state to the local current directory
await File.WriteAllTextAsync(statePath, json);
_logger?.LogDebug("Saved dynamic state to absolute path: {StatePath}", statePath);
return;
}
catch (Exception ex)
{
_logger?.LogError(ex, "Failed to save dynamic state to: {StatePath}", statePath);
throw;
}
}

// For relative paths, check if we're in a project directory (has local static config)
var staticConfigPath = Path.Combine(Environment.CurrentDirectory, ConfigConstants.DefaultConfigFileName);
bool hasLocalStaticConfig = File.Exists(staticConfigPath);

if (hasLocalStaticConfig)
{
// We're in a project directory - save state locally only
// This ensures each project maintains its own independent configuration
var currentDirPath = Path.Combine(Environment.CurrentDirectory, statePath);
try
{
await File.WriteAllTextAsync(currentDirPath, json);
_logger?.LogDebug("Saved dynamic state to: {StatePath}", currentDirPath);
_logger?.LogDebug("Saved dynamic state to local project directory: {StatePath}", currentDirPath);
}
catch (Exception ex)
{
_logger?.LogError(ex, "Failed to save dynamic state to: {StatePath}", currentDirPath);
throw;
}
}

// Always sync to global directory for portability
await SyncConfigToGlobalDirectoryAsync(statePath, json, throwOnError: true);
else
{
// Not in a project directory - save to global directory for portability
// This allows CLI commands to work when run from any directory
await SyncConfigToGlobalDirectoryAsync(statePath, json, throwOnError: true);
_logger?.LogDebug("Saved dynamic state to global directory (no local static config found)");
}
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,130 @@ public async Task SaveStateAsync_OverwritesExistingFile()
Assert.DoesNotContain("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", secondContent);
}

[Fact]
public async Task SaveStateAsync_SavesLocallyWhenStaticConfigExists()
{
// Arrange - Create a project directory with a static config
var projectDir = Path.Combine(Path.GetTempPath(), $"agent365-project-{Guid.NewGuid()}");
Directory.CreateDirectory(projectDir);

try
{
var originalDir = Environment.CurrentDirectory;
Environment.CurrentDirectory = projectDir;

try
{
// Create a static config file in the project directory
var staticConfigPath = Path.Combine(projectDir, ConfigConstants.DefaultConfigFileName);
var staticConfig = new
{
tenantId = "12345678-1234-1234-1234-123456789012",
subscriptionId = "87654321-4321-4321-4321-210987654321",
resourceGroup = "rg-test",
location = "eastus",
appServicePlanName = "asp-test",
webAppName = "webapp-test",
agentIdentityDisplayName = "Test Agent"
};
await File.WriteAllTextAsync(staticConfigPath, JsonSerializer.Serialize(staticConfig, new JsonSerializerOptions { WriteIndented = true }));

// Create a config to save
var config = new Agent365Config { TenantId = "12345678-1234-1234-1234-123456789012" };
config.AgentBlueprintId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";

// Get global config path to verify it's NOT written there
var globalDir = ConfigService.GetGlobalConfigDirectory();
var globalStatePath = Path.Combine(globalDir, ConfigConstants.DefaultStateFileName);

// Delete global state if it exists to ensure clean test
if (File.Exists(globalStatePath))
{
File.Delete(globalStatePath);
}

// Act - Save state (should go to local directory, NOT global)
await _service.SaveStateAsync(config, ConfigConstants.DefaultStateFileName);

// Assert - State should be saved locally
var localStatePath = Path.Combine(projectDir, ConfigConstants.DefaultStateFileName);
Assert.True(File.Exists(localStatePath), "Local state file should exist in project directory");

var localContent = await File.ReadAllTextAsync(localStatePath);
Assert.Contains("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", localContent);

// Assert - State should NOT be saved to global directory
Assert.False(File.Exists(globalStatePath), "Global state file should NOT exist when saving in a project directory");
}
finally
{
Environment.CurrentDirectory = originalDir;
}
}
finally
{
if (Directory.Exists(projectDir))
{
Directory.Delete(projectDir, recursive: true);
}
}
}

[Fact]
public async Task SaveStateAsync_SavesGloballyWhenNoStaticConfigExists()
{
// Arrange - Use a directory without a static config
var tempDir = Path.Combine(Path.GetTempPath(), $"agent365-noproj-{Guid.NewGuid()}");
Directory.CreateDirectory(tempDir);

try
{
var originalDir = Environment.CurrentDirectory;
Environment.CurrentDirectory = tempDir;

try
{
// Create a config to save
var config = new Agent365Config { TenantId = "12345678-1234-1234-1234-123456789012" };
config.AgentBlueprintId = "bbbbbbbb-cccc-dddd-eeee-ffffffffffff";

// Get global config path
var globalDir = ConfigService.GetGlobalConfigDirectory();
var globalStatePath = Path.Combine(globalDir, ConfigConstants.DefaultStateFileName);

// Delete global state if it exists to ensure clean test
if (File.Exists(globalStatePath))
{
File.Delete(globalStatePath);
}

// Act - Save state (should go to global directory, NOT local)
await _service.SaveStateAsync(config, ConfigConstants.DefaultStateFileName);

// Assert - State should be saved globally
Assert.True(File.Exists(globalStatePath), "Global state file should exist when no local config present");

var globalContent = await File.ReadAllTextAsync(globalStatePath);
Assert.Contains("bbbbbbbb-cccc-dddd-eeee-ffffffffffff", globalContent);

// Assert - State should NOT be saved to current directory
var localStatePath = Path.Combine(tempDir, ConfigConstants.DefaultStateFileName);
Assert.False(File.Exists(localStatePath), "Local state file should NOT exist when no static config present");
}
finally
{
Environment.CurrentDirectory = originalDir;
}
}
finally
{
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, recursive: true);
}
}
}

#endregion

#region ValidateAsync Tests
Expand Down
Loading