- Fill TimeZoneId only for a configured session/host zone with a resolvable IANA id (IExtensionHost.TimeZoneId); otherwise children inherit the host's TZ instead of TZ=Local/UTC. - Host hardening logs a warning instead of throwing when libc/prctl can't be resolved. - A hook or process_spawn handler returning a null or incomplete context fails the spawn with InvalidOperationException, so bash reports "process environment hook failed". - Move tool-result redaction into the Agent slice with a behavioural no-change check. - ProcessSpawnContext.ApplyTo(psi) sets cwd and replaces the environment; used by bash and ExecAsync. - Split the Telegram process environment types into their own files; move the Extensions.Tests NoHostHardening under Support/; check the Claude Code assembly never references the spawn policy. - Spec and docs: TZ inheritance, incomplete contexts, ApplyTo, async spawn handlers, root skip.
80 lines
3.2 KiB
C#
80 lines
3.2 KiB
C#
using Volcanic.AgenticCore.Abstractions.Extensions;
|
|
using Volcanic.AgenticCore.Abstractions.Tools;
|
|
using Volcanic.AgenticCore.Features.Extensions;
|
|
using Volcanic.AgenticCore.Hosting;
|
|
using Volcanic.AgenticCore.Testing;
|
|
using Volcanic.AgenticCore.Tests.Support;
|
|
|
|
namespace Volcanic.AgenticCore.Tests.Features;
|
|
|
|
public class SessionTimeZoneTests
|
|
{
|
|
private static readonly TimeZoneInfo Tokyo = TimeZoneInfo.FindSystemTimeZoneById("Asia/Tokyo");
|
|
private static readonly TimeZoneInfo Berlin = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
|
|
|
[Fact]
|
|
public async Task Session_option_wins_over_the_host_default()
|
|
{
|
|
await using var host = new TestHost(o => o.DefaultTimeZone = Tokyo);
|
|
await using var session = await host.CreateSessionAsync(b => b.TimeZone = Berlin);
|
|
Assert.Equal("Europe/Berlin", TimeZoneIds.ToIana(session.TimeZone));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Host_default_applies_when_the_session_sets_none()
|
|
{
|
|
await using var host = new TestHost(o => o.DefaultTimeZone = Tokyo);
|
|
await using var session = await host.CreateSessionAsync();
|
|
Assert.Equal("Asia/Tokyo", TimeZoneIds.ToIana(session.TimeZone));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Local_zone_is_the_fallback()
|
|
{
|
|
await using var host = new TestHost();
|
|
await using var session = await host.CreateSessionAsync();
|
|
Assert.Equal(TimeZoneInfo.Local.Id, session.TimeZone.Id);
|
|
}
|
|
|
|
public sealed record ProbeArgs(string Value);
|
|
|
|
[Fact]
|
|
public async Task Extension_context_reports_the_session_zone()
|
|
{
|
|
TimeZoneInfo? seen = null;
|
|
var probe = new DelegateTool<ProbeArgs>("probe", "p", (_, inv, _) => { seen = inv.Context.TimeZone; return Task.FromResult(ToolResult.Text("ok")); });
|
|
await using var host = new TestHost(null, new LambdaExtension("tools", api => { api.RegisterTool(probe); return ValueTask.CompletedTask; }));
|
|
host.Faux.Enqueue(FauxTurn.Tool("probe", new { value = "x" }), FauxTurn.Say("done"));
|
|
await using var session = await host.CreateSessionAsync(b => b.TimeZone = Tokyo);
|
|
|
|
await session.PromptAsync("go");
|
|
|
|
Assert.NotNull(seen);
|
|
Assert.Equal("Asia/Tokyo", TimeZoneIds.ToIana(seen!));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Session_reports_a_TZ_id_only_for_a_configured_zone()
|
|
{
|
|
await using var host = new TestHost();
|
|
await using (var plain = await host.CreateSessionAsync())
|
|
Assert.Null(((IExtensionHost)plain).TimeZoneId);
|
|
await using var configured = await host.CreateSessionAsync(b => b.TimeZone = Tokyo);
|
|
Assert.Equal("Asia/Tokyo", ((IExtensionHost)configured).TimeZoneId);
|
|
}
|
|
|
|
[Fact]
|
|
public void ToIana_returns_null_for_an_unnamed_or_unknown_zone()
|
|
{
|
|
Assert.Null(TimeZoneIds.ToIana(TimeZoneInfo.CreateCustomTimeZone("Local", TimeSpan.FromHours(1), "Local", "Local")));
|
|
Assert.Null(TimeZoneIds.ToIana(TimeZoneInfo.CreateCustomTimeZone("Not/AZone", TimeSpan.FromHours(1), "x", "x")));
|
|
}
|
|
|
|
[Fact]
|
|
public void ToIana_keeps_iana_ids_and_converts_windows_ids()
|
|
{
|
|
Assert.Equal("Europe/Berlin", TimeZoneIds.ToIana(Berlin));
|
|
Assert.Equal("Europe/Berlin", TimeZoneIds.ToIana(TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time")));
|
|
}
|
|
}
|