Fix weather forecast parsing and NewsAPI fallback

This commit is contained in:
Jacob Dubin
2026-05-10 23:08:06 -05:00
parent 4bc87f927b
commit 0c597ebbf8
11 changed files with 55098 additions and 15 deletions

View File

@@ -77,6 +77,44 @@ public sealed class ProviderCachingTests
Assert.Equal(1, handler.GetCallCount("/v2/top-headlines"));
}
[Fact]
public async Task NewsApiBriefingProvider_FallsBackToUncategorizedHeadlines_WhenCategoryReturnsEmpty()
{
var handler = new CountingHttpMessageHandler(message =>
{
var path = message.RequestUri?.AbsolutePath ?? string.Empty;
if (!string.Equals(path, "/v2/top-headlines", StringComparison.OrdinalIgnoreCase))
{
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
var query = message.RequestUri?.Query ?? string.Empty;
if (query.Contains("category=sports", StringComparison.OrdinalIgnoreCase))
{
return JsonResponse("""{"status":"ok","articles":[]}""");
}
return JsonResponse(
"""{"status":"ok","articles":[{"title":"General robotics update","description":"Top story","source":{"name":"AP News"},"url":"https://example.com/general"}]}""");
});
var provider = new NewsApiBriefingProvider(
new HttpClient(handler),
new NewsApiOptions
{
ApiKey = "test-key",
CacheTtlSeconds = 300,
FailureCacheTtlSeconds = 30
},
NullLogger<NewsApiBriefingProvider>.Instance);
var result = await provider.GetBriefingAsync(new NewsBriefingRequest(["sports"], 3));
Assert.NotNull(result);
Assert.Single(result!.Headlines);
Assert.Equal("General robotics update", result.Headlines[0].Title);
Assert.Equal(2, handler.GetCallCount("/v2/top-headlines"));
}
private static HttpResponseMessage JsonResponse(string body)
{
return new HttpResponseMessage(HttpStatusCode.OK)

View File

@@ -1462,6 +1462,7 @@ public sealed class JiboInteractionServiceTests
Assert.NotNull(decision.SkillPayload);
Assert.Contains("cat='weather'", decision.SkillPayload!["esml"]?.ToString(), StringComparison.OrdinalIgnoreCase);
Assert.Contains("meta='rain'", decision.SkillPayload["esml"]?.ToString(), StringComparison.OrdinalIgnoreCase);
Assert.Equal("report-skill", decision.SkillPayload["skillId"]);
Assert.Equal("WeatherCommentRain", decision.SkillPayload["mim_id"]);
Assert.Equal(true, decision.SkillPayload["weather_view_enabled"]);
Assert.Equal("weatherHiLo", decision.SkillPayload["weather_view_kind"]);
@@ -1826,6 +1827,34 @@ public sealed class JiboInteractionServiceTests
Assert.Equal(5, provider.LastRequest.ForecastDayOffset);
}
[Fact]
public async Task BuildDecisionAsync_WeatherForecastNextPhrase_WithContext_ReturnsFiveDaySummary()
{
var provider = new CapturingWeatherReportProvider
{
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
};
var service = CreateService(weatherReportProvider: provider);
var decision = await service.BuildDecisionAsync(new TurnContext
{
RawTranscript = "what's the forecast next",
NormalizedTranscript = "what's the forecast next",
Attributes = new Dictionary<string, object?>
{
["context"] = """{"runtime":{"location":{"iso":"2026-04-20T08:00:00-05:00"}}}"""
}
});
Assert.Equal("weather", decision.IntentName);
Assert.Contains("next five-day forecast", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Seattle, US", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Temperatures are in Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
Assert.NotNull(provider.LastRequest);
Assert.Null(provider.LastRequest!.LocationQuery);
Assert.Equal(5, provider.LastRequest.ForecastDayOffset);
}
[Fact]
public async Task BuildDecisionAsync_WeatherDayAfterTomorrow_WithContext_PassesDayOffsetAndLocation()
{

View File

@@ -2202,6 +2202,9 @@ public sealed class JiboWebSocketServiceTests
var skillReply = replies.Last(static reply => string.Equals(ReadReplyType(reply), "SKILL_ACTION", StringComparison.Ordinal));
using var skillPayload = JsonDocument.Parse(skillReply.Text!);
Assert.Equal(
"report-skill",
skillPayload.RootElement.GetProperty("data").GetProperty("skill").GetProperty("id").GetString());
var jcpConfig = skillPayload.RootElement
.GetProperty("data")
.GetProperty("action")