Document commute provider seam for personal report

This commit is contained in:
Jacob Dubin
2026-05-20 23:25:41 -05:00
parent c76af83d7e
commit 884b2215c7
21 changed files with 1046 additions and 55 deletions

View File

@@ -1,4 +1,5 @@
using Jibo.Cloud.Application.Abstractions;
using Jibo.Cloud.Domain.Models;
using Jibo.Cloud.Infrastructure.Persistence;
namespace Jibo.Cloud.Tests.Infrastructure;
@@ -103,6 +104,21 @@ public sealed class PersistenceStoreTests
var update = firstStore.CreateUpdate("1.0.0", "1.0.1", "Bug fix", null, 42, "robot", null, null);
var media = firstStore.CreateMedia("openjibo-default-loop", "persisted-photo", "image", "photo-ref", false,
new Dictionary<string, object?> { ["note"] = "roundtrip" });
var commute = firstStore.UpsertCommuteProfile(new CommuteProfileRecord
{
LoopId = "openjibo-default-loop",
Mode = "driving",
WorkHour = 8,
WorkMinute = 30,
TypicalDurationMinutes = 25
});
var calendarEvent = firstStore.UpsertCalendarEvent(new CalendarEventRecord
{
LoopId = "openjibo-default-loop",
Summary = "Report review",
TimeLabel = "at 6:00 p.m.",
Date = DateOnly.FromDateTime(DateTime.UtcNow)
});
var sessionToken = firstStore.IssueRobotToken("robot-123");
var device = firstStore.GetOrCreateDevice("robot-123", "3.2.1", "4.5.6");
firstStore.SavePersistedState();
@@ -117,6 +133,10 @@ public sealed class PersistenceStoreTests
Assert.Equal(firstInfo.Revision, secondInfo.Revision);
Assert.Contains(secondStore.ListUpdates("robot"), item => item.UpdateId == update.UpdateId);
Assert.Contains(secondStore.ListMedia(), item => item.Path == media.Path);
Assert.Contains(secondStore.GetCommuteProfiles("openjibo-default-loop"),
item => item.Id == commute.Id && item.Mode == commute.Mode);
Assert.Contains(secondStore.GetCalendarEvents("openjibo-default-loop"),
item => item.Id == calendarEvent.Id && item.Summary == calendarEvent.Summary);
Assert.NotNull(secondStore.FindSessionByToken(sessionToken));
Assert.Equal("3.2.1", secondStore.GetOrCreateDevice(device.DeviceId, null, null).FirmwareVersion);
Assert.NotEmpty(secondStore.GetPeople());

View File

@@ -167,6 +167,43 @@ public sealed class JiboCloudProtocolServiceTests
}
}
[Fact]
public async Task PersonUpsertCommute_ThenListCommute_ReturnsPersistedLoopProfile()
{
var service = new JiboCloudProtocolService(new InMemoryCloudStateStore());
var upsert = await service.DispatchAsync(new ProtocolEnvelope
{
HostName = "api.jibo.com",
Method = "POST",
ServicePrefix = "Person_20160715",
Operation = "UpsertCommute",
BodyText =
"""{"loopId":"loop-123","mode":"walking","workHour":8,"workMinute":15,"typicalDurationMinutes":22}"""
});
using var upsertPayload = JsonDocument.Parse(upsert.BodyText);
Assert.Equal(200, upsert.StatusCode);
Assert.Equal("loop-123", upsertPayload.RootElement.GetProperty("loopId").GetString());
Assert.Equal("walking", upsertPayload.RootElement.GetProperty("mode").GetString());
var listed = await service.DispatchAsync(new ProtocolEnvelope
{
HostName = "api.jibo.com",
Method = "POST",
ServicePrefix = "Person_20160715",
Operation = "ListCommute",
BodyText = """{"loopId":"loop-123"}"""
});
using var listedPayload = JsonDocument.Parse(listed.BodyText);
Assert.Equal(200, listed.StatusCode);
Assert.Contains(listedPayload.RootElement.EnumerateArray(),
item => item.GetProperty("loopId").GetString() == "loop-123" &&
item.GetProperty("mode").GetString() == "walking" &&
item.GetProperty("workHour").GetInt32() == 8);
}
[Fact]
public async Task MediaCreateAndGet_ReturnsCreatedItem()
{

View File

@@ -2,6 +2,9 @@ using System.Text;
using System.Text.Json;
using Jibo.Cloud.Application.Abstractions;
using Jibo.Cloud.Application.Services;
using Jibo.Cloud.Domain.Models;
using Jibo.Cloud.Infrastructure.Calendar;
using Jibo.Cloud.Infrastructure.Commute;
using Jibo.Cloud.Infrastructure.Content;
using Jibo.Cloud.Infrastructure.Persistence;
using Jibo.Runtime.Abstractions;
@@ -1891,10 +1894,15 @@ public sealed class JiboInteractionServiceTests
{
Snapshot = new WeatherReportSnapshot("Boston, U.S.", "light rain", 61, 65, 54, "rain", false)
};
var calendarProvider = new CapturingCalendarReportProvider
var cloudStateStore = new InMemoryCloudStateStore();
cloudStateStore.UpsertCalendarEvent(new CalendarEventRecord
{
Snapshot = new CalendarReportSnapshot(["get personal report from jibo"], ["at 6:00 p.m."], [])
};
LoopId = "openjibo-default-loop",
Summary = "get personal report from jibo",
TimeLabel = "at 6:00 p.m.",
Date = DateOnly.FromDateTime(DateTime.UtcNow)
});
var calendarProvider = new CloudStateCalendarReportProvider(cloudStateStore);
var service = CreateService(weatherReportProvider: weatherProvider, calendarReportProvider: calendarProvider);
var decision = await service.BuildDecisionAsync(new TurnContext
@@ -1913,6 +1921,54 @@ public sealed class JiboInteractionServiceTests
StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task BuildDecisionAsync_PersonalReport_UsesCommuteProviderAndNormalTraffic()
{
var weatherProvider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Boston, U.S.", "light rain", 61, 65, 54, "rain", false)
};
var calendarStore = new InMemoryCloudStateStore();
calendarStore.UpsertCalendarEvent(new CalendarEventRecord
{
LoopId = "openjibo-default-loop",
Summary = "get personal report from jibo",
TimeLabel = "at 6:00 p.m.",
Date = DateOnly.FromDateTime(DateTime.UtcNow)
});
var calendarProvider = new CloudStateCalendarReportProvider(calendarStore);
var cloudStateStore = new InMemoryCloudStateStore();
var commuteProvider = new CloudStateCommuteReportProvider(cloudStateStore);
var commuteTime = DateTimeOffset.Now.AddMinutes(45);
cloudStateStore.UpsertCommuteProfile(new CommuteProfileRecord
{
LoopId = "openjibo-default-loop",
Mode = "driving",
WorkHour = commuteTime.Hour,
WorkMinute = commuteTime.Minute,
TypicalDurationMinutes = 25
});
var service = CreateService(
weatherReportProvider: weatherProvider,
calendarReportProvider: calendarProvider,
commuteReportProvider: commuteProvider);
var decision = await service.BuildDecisionAsync(new TurnContext
{
RawTranscript = "yes",
NormalizedTranscript = "yes",
Attributes = new Dictionary<string, object?>
{
[PersonalReportStateKey] = "awaiting_identity_confirmation",
[PersonalReportUserNameKey] = "alex"
}
});
Assert.Equal("personal_report_delivered", decision.IntentName);
Assert.Contains("commute", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task BuildDecisionAsync_PersonalReport_NoMatchRetriesThenDeclines()
{
@@ -4192,15 +4248,4 @@ public sealed class JiboInteractionServiceTests
}
}
private sealed class CapturingCalendarReportProvider : ICalendarReportProvider
{
public CalendarReportSnapshot? Snapshot { get; init; }
public Task<CalendarReportSnapshot?> GetReportAsync(
TurnContext turn,
CancellationToken cancellationToken = default)
{
return Task.FromResult(Snapshot);
}
}
}