Improve weather and news diagnostics for report skills
This commit is contained in:
@@ -20,4 +20,9 @@ public sealed record NewsHeadline(
|
||||
|
||||
public sealed record NewsBriefingSnapshot(
|
||||
IReadOnlyList<NewsHeadline> Headlines,
|
||||
string? SourceName = null);
|
||||
string? SourceName = null,
|
||||
string? ProviderStatus = null,
|
||||
string? ProviderMessage = null,
|
||||
int? ProviderHttpStatusCode = null,
|
||||
string? ProviderEndpoint = null,
|
||||
string? ProviderErrorCode = null);
|
||||
|
||||
@@ -719,7 +719,7 @@ public sealed class JiboInteractionService(
|
||||
: snapshot.Summary.Trim().TrimEnd('.');
|
||||
var location = string.IsNullOrWhiteSpace(snapshot.LocationName)
|
||||
? "your area"
|
||||
: snapshot.LocationName;
|
||||
: NormalizeLocationForSpeech(snapshot.LocationName);
|
||||
|
||||
if (weatherDate.ForecastDayOffset > 0)
|
||||
{
|
||||
@@ -758,6 +758,10 @@ public sealed class JiboInteractionService(
|
||||
{
|
||||
location = "your area";
|
||||
}
|
||||
else
|
||||
{
|
||||
location = NormalizeLocationForSpeech(location);
|
||||
}
|
||||
|
||||
var unit = snapshots[0].Snapshot.UseCelsius ? "Celsius" : "Fahrenheit";
|
||||
var referenceDate = (referenceLocalTime ?? DateTimeOffset.UtcNow).Date;
|
||||
@@ -1076,6 +1080,12 @@ public sealed class JiboInteractionService(
|
||||
return BuildProviderNewsDecision(snapshot, preferredCategories, requestedHeadlineCount);
|
||||
}
|
||||
|
||||
var providerStatus = ResolveNewsProviderStatus(snapshot);
|
||||
var providerMessage = snapshot?.ProviderMessage;
|
||||
var providerEndpoint = snapshot?.ProviderEndpoint;
|
||||
var providerHttpStatusCode = snapshot?.ProviderHttpStatusCode;
|
||||
var providerErrorCode = snapshot?.ProviderErrorCode;
|
||||
|
||||
var fallbackBriefingWhenEmpty = randomizer.Choose(catalog.NewsBriefings);
|
||||
return BuildNewsDecision(
|
||||
fallbackBriefingWhenEmpty,
|
||||
@@ -1083,10 +1093,14 @@ public sealed class JiboInteractionService(
|
||||
preferredCategories.Count > 0 ? preferredCategories : null,
|
||||
headlineCount: null,
|
||||
providerDiagnostics: BuildNewsProviderDiagnostics(
|
||||
"provider_empty",
|
||||
providerStatus,
|
||||
preferredCategories,
|
||||
requestedHeadlineCount,
|
||||
snapshot?.Headlines.Count ?? 0));
|
||||
snapshot?.Headlines.Count ?? 0,
|
||||
providerMessage,
|
||||
providerHttpStatusCode,
|
||||
providerEndpoint,
|
||||
providerErrorCode));
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -1208,7 +1222,11 @@ public sealed class JiboInteractionService(
|
||||
string status,
|
||||
IReadOnlyList<string> preferredCategories,
|
||||
int requestedHeadlineCount,
|
||||
int? resolvedHeadlineCount = null)
|
||||
int? resolvedHeadlineCount = null,
|
||||
string? providerMessage = null,
|
||||
int? providerHttpStatusCode = null,
|
||||
string? providerEndpoint = null,
|
||||
string? providerErrorCode = null)
|
||||
{
|
||||
var diagnostics = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
@@ -1224,9 +1242,41 @@ public sealed class JiboInteractionService(
|
||||
diagnostics["news_provider_resolved_headlines"] = resolvedHeadlineCount.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(providerMessage))
|
||||
{
|
||||
diagnostics["news_provider_message"] = providerMessage;
|
||||
}
|
||||
|
||||
if (providerHttpStatusCode is not null)
|
||||
{
|
||||
diagnostics["news_provider_http_status"] = providerHttpStatusCode.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(providerEndpoint))
|
||||
{
|
||||
diagnostics["news_provider_endpoint"] = providerEndpoint;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(providerErrorCode))
|
||||
{
|
||||
diagnostics["news_provider_error_code"] = providerErrorCode;
|
||||
}
|
||||
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
private static string ResolveNewsProviderStatus(NewsBriefingSnapshot? snapshot)
|
||||
{
|
||||
var providerStatus = snapshot?.ProviderStatus?.Trim().ToLowerInvariant();
|
||||
return providerStatus switch
|
||||
{
|
||||
"success" => "provider_success",
|
||||
"exception" => "provider_exception",
|
||||
"http_error" or "api_error" or "schema_error" => "provider_error",
|
||||
_ => "provider_empty"
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildNewsLeadIn(string? sourceName, IReadOnlyList<string> preferredCategories)
|
||||
{
|
||||
var categoryLeadIn = preferredCategories.Count switch
|
||||
@@ -1249,11 +1299,35 @@ public sealed class JiboInteractionService(
|
||||
}
|
||||
|
||||
// Expand "AI" so Nimbus TTS does not collapse it to a single "aye" sound.
|
||||
return Regex.Replace(
|
||||
var normalized = Regex.Replace(
|
||||
text,
|
||||
@"\bA\.?\s*I\.?\b",
|
||||
"artificial intelligence",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
return NormalizeLocationForSpeech(normalized);
|
||||
}
|
||||
|
||||
private static string NormalizeLocationForSpeech(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
return Regex.Replace(
|
||||
text,
|
||||
@"\b(?<token>[A-Z]{2,3})\b",
|
||||
static match =>
|
||||
{
|
||||
var token = match.Groups["token"].Value;
|
||||
if (!SpokenAbbreviationTokens.Contains(token))
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
return string.Join(".", token.ToCharArray()) + ".";
|
||||
},
|
||||
RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
private List<string> ResolvePreferredNewsCategories(TurnContext turn, string transcript)
|
||||
@@ -4486,6 +4560,66 @@ public sealed class JiboInteractionService(
|
||||
"our neighbourhood"
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> SpokenAbbreviationTokens = new(StringComparer.Ordinal)
|
||||
{
|
||||
"US",
|
||||
"USA",
|
||||
"UK",
|
||||
"UAE",
|
||||
"EU",
|
||||
"DC",
|
||||
"AL",
|
||||
"AK",
|
||||
"AZ",
|
||||
"AR",
|
||||
"CA",
|
||||
"CO",
|
||||
"CT",
|
||||
"DE",
|
||||
"FL",
|
||||
"GA",
|
||||
"HI",
|
||||
"ID",
|
||||
"IL",
|
||||
"IN",
|
||||
"IA",
|
||||
"KS",
|
||||
"KY",
|
||||
"LA",
|
||||
"ME",
|
||||
"MD",
|
||||
"MA",
|
||||
"MI",
|
||||
"MN",
|
||||
"MS",
|
||||
"MO",
|
||||
"MT",
|
||||
"NE",
|
||||
"NV",
|
||||
"NH",
|
||||
"NJ",
|
||||
"NM",
|
||||
"NY",
|
||||
"NC",
|
||||
"ND",
|
||||
"OH",
|
||||
"OK",
|
||||
"OR",
|
||||
"PA",
|
||||
"RI",
|
||||
"SC",
|
||||
"SD",
|
||||
"TN",
|
||||
"TX",
|
||||
"UT",
|
||||
"VT",
|
||||
"VA",
|
||||
"WA",
|
||||
"WV",
|
||||
"WI",
|
||||
"WY"
|
||||
};
|
||||
|
||||
private const string GreetingRouteMetadataKey = "greetingsRoute";
|
||||
private const string GreetingSpeakerMetadataKey = "greetingsSpeaker";
|
||||
private const string LastProactiveGreetingUtcMetadataKey = "greetingsLastProactiveUtc";
|
||||
|
||||
@@ -808,7 +808,7 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
||||
var weatherHiLoView = BuildWeatherHiLoView(skillPayload);
|
||||
if (weatherHiLoView is not null)
|
||||
{
|
||||
var resolvedGuiConfig = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
||||
var resolvedGuiContext = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["type"] = "Javascript",
|
||||
["data"] = weatherHiLoView,
|
||||
@@ -825,7 +825,15 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
||||
jcpConfig["gui"] = legacyGuiConfig;
|
||||
jcpConfig["display"] = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["view"] = resolvedGuiConfig
|
||||
["view"] = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
// Legacy fields used by existing tests and tooling.
|
||||
["type"] = "Javascript",
|
||||
["data"] = weatherHiLoView,
|
||||
["pause"] = true,
|
||||
// Pegasus-style view context used by on-robot weather cards.
|
||||
["context"] = resolvedGuiContext
|
||||
}
|
||||
};
|
||||
|
||||
jcpConfig["timeout"] = 6;
|
||||
@@ -1182,7 +1190,7 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
||||
{
|
||||
id = "hiNumLabel",
|
||||
type = "Label",
|
||||
text = high.Value.ToString(),
|
||||
text = $"{high.Value}°",
|
||||
style = new
|
||||
{
|
||||
fontSize = "160",
|
||||
@@ -1214,7 +1222,7 @@ public sealed class ResponsePlanToSocketMessagesMapper
|
||||
{
|
||||
id = "loNumLabel",
|
||||
type = "Label",
|
||||
text = low.Value.ToString(),
|
||||
text = $"{low.Value}°",
|
||||
style = new
|
||||
{
|
||||
fontSize = "160",
|
||||
|
||||
@@ -690,6 +690,10 @@ public sealed partial class WebSocketTurnFinalizationService(
|
||||
invokedSkillAction.Payload.TryGetValue("news_provider_resolved_headlines", out var resolvedHeadlines);
|
||||
invokedSkillAction.Payload.TryGetValue("news_provider_preferred_categories", out var preferredCategories);
|
||||
invokedSkillAction.Payload.TryGetValue("news_source", out var newsSource);
|
||||
invokedSkillAction.Payload.TryGetValue("news_provider_message", out var providerMessage);
|
||||
invokedSkillAction.Payload.TryGetValue("news_provider_http_status", out var providerHttpStatus);
|
||||
invokedSkillAction.Payload.TryGetValue("news_provider_endpoint", out var providerEndpoint);
|
||||
invokedSkillAction.Payload.TryGetValue("news_provider_error_code", out var providerErrorCode);
|
||||
|
||||
await sink.RecordTurnDiagnosticAsync(
|
||||
"news_provider_trace",
|
||||
@@ -701,7 +705,11 @@ public sealed partial class WebSocketTurnFinalizationService(
|
||||
["requestedHeadlines"] = requestedHeadlines,
|
||||
["resolvedHeadlines"] = resolvedHeadlines,
|
||||
["preferredCategories"] = preferredCategories,
|
||||
["source"] = newsSource
|
||||
["source"] = newsSource,
|
||||
["providerMessage"] = providerMessage,
|
||||
["providerHttpStatus"] = providerHttpStatus,
|
||||
["providerEndpoint"] = providerEndpoint,
|
||||
["providerErrorCode"] = providerErrorCode
|
||||
}),
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,30 @@ public sealed class NewsApiBriefingProvider(
|
||||
|
||||
var headlines = new List<NewsHeadline>(requestedHeadlineCount);
|
||||
var seenTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
string? failureStatus = null;
|
||||
string? failureMessage = null;
|
||||
int? failureStatusCode = null;
|
||||
string? failureEndpoint = null;
|
||||
string? failureErrorCode = null;
|
||||
|
||||
void CaptureFailure(
|
||||
string status,
|
||||
string? message,
|
||||
int? statusCode,
|
||||
Uri? endpoint,
|
||||
string? errorCode = null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(failureStatus))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
failureStatus = status;
|
||||
failureMessage = message;
|
||||
failureStatusCode = statusCode;
|
||||
failureEndpoint = endpoint is null ? null : SanitizeEndpoint(endpoint);
|
||||
failureErrorCode = errorCode;
|
||||
}
|
||||
|
||||
foreach (var category in categories)
|
||||
{
|
||||
@@ -59,6 +83,11 @@ public sealed class NewsApiBriefingProvider(
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseBody = await TryReadResponseBodySnippetAsync(response, cancellationToken);
|
||||
CaptureFailure(
|
||||
"http_error",
|
||||
$"Category '{category}' returned {(int)response.StatusCode} {response.ReasonPhrase}.",
|
||||
(int)response.StatusCode,
|
||||
uri);
|
||||
logger.LogWarning(
|
||||
"NewsAPI request failed for category {Category}. StatusCode={StatusCode} Reason={ReasonPhrase} Body={Body}",
|
||||
category,
|
||||
@@ -74,6 +103,12 @@ public sealed class NewsApiBriefingProvider(
|
||||
statusNode.ValueKind == JsonValueKind.String &&
|
||||
!string.Equals(statusNode.GetString(), "ok", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
CaptureFailure(
|
||||
"api_error",
|
||||
ReadString(document.RootElement, "message"),
|
||||
null,
|
||||
uri,
|
||||
ReadString(document.RootElement, "code"));
|
||||
logger.LogWarning(
|
||||
"NewsAPI returned non-ok status for category {Category}. Status={Status} Code={Code} Message={Message}",
|
||||
category,
|
||||
@@ -85,6 +120,11 @@ public sealed class NewsApiBriefingProvider(
|
||||
if (!document.RootElement.TryGetProperty("articles", out var articles) ||
|
||||
articles.ValueKind != JsonValueKind.Array)
|
||||
{
|
||||
CaptureFailure(
|
||||
"schema_error",
|
||||
$"Category '{category}' response did not include an articles array.",
|
||||
null,
|
||||
uri);
|
||||
logger.LogWarning("NewsAPI response missing articles array for category {Category}.", category);
|
||||
continue;
|
||||
}
|
||||
@@ -107,7 +147,10 @@ public sealed class NewsApiBriefingProvider(
|
||||
|
||||
if (headlines.Count >= requestedHeadlineCount)
|
||||
{
|
||||
var snapshot = new NewsBriefingSnapshot(headlines, "NewsAPI");
|
||||
var snapshot = new NewsBriefingSnapshot(
|
||||
headlines,
|
||||
"NewsAPI",
|
||||
ProviderStatus: "success");
|
||||
SetCachedValue(briefingCache, cacheKey, snapshot, options.CacheTtlSeconds);
|
||||
logger.LogInformation(
|
||||
"NewsAPI request succeeded. Categories={Categories} HeadlineCount={HeadlineCount}",
|
||||
@@ -157,12 +200,22 @@ public sealed class NewsApiBriefingProvider(
|
||||
}
|
||||
else
|
||||
{
|
||||
CaptureFailure(
|
||||
"schema_error",
|
||||
"Uncategorized fallback response did not include an articles array.",
|
||||
null,
|
||||
broadUri);
|
||||
logger.LogWarning("NewsAPI uncategorized fallback response missing articles array.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var fallbackBody = await TryReadResponseBodySnippetAsync(broadResponse, cancellationToken);
|
||||
CaptureFailure(
|
||||
"http_error",
|
||||
$"Uncategorized fallback returned {(int)broadResponse.StatusCode} {broadResponse.ReasonPhrase}.",
|
||||
(int)broadResponse.StatusCode,
|
||||
broadUri);
|
||||
logger.LogWarning(
|
||||
"NewsAPI uncategorized fallback failed. StatusCode={StatusCode} Reason={ReasonPhrase} Body={Body}",
|
||||
(int)broadResponse.StatusCode,
|
||||
@@ -210,12 +263,22 @@ public sealed class NewsApiBriefingProvider(
|
||||
}
|
||||
else
|
||||
{
|
||||
CaptureFailure(
|
||||
"schema_error",
|
||||
"Everything fallback response did not include an articles array.",
|
||||
null,
|
||||
everythingUri);
|
||||
logger.LogWarning("NewsAPI everything fallback response missing articles array.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var everythingBody = await TryReadResponseBodySnippetAsync(everythingResponse, cancellationToken);
|
||||
CaptureFailure(
|
||||
"http_error",
|
||||
$"Everything fallback returned {(int)everythingResponse.StatusCode} {everythingResponse.ReasonPhrase}.",
|
||||
(int)everythingResponse.StatusCode,
|
||||
everythingUri);
|
||||
logger.LogWarning(
|
||||
"NewsAPI everything fallback failed. StatusCode={StatusCode} Reason={ReasonPhrase} Body={Body}",
|
||||
(int)everythingResponse.StatusCode,
|
||||
@@ -226,15 +289,26 @@ public sealed class NewsApiBriefingProvider(
|
||||
|
||||
if (headlines.Count == 0)
|
||||
{
|
||||
SetCachedValue(briefingCache, cacheKey, null, options.FailureCacheTtlSeconds);
|
||||
var emptySnapshot = new NewsBriefingSnapshot(
|
||||
Array.Empty<NewsHeadline>(),
|
||||
"NewsAPI",
|
||||
ProviderStatus: failureStatus ?? "empty",
|
||||
ProviderMessage: failureMessage ?? "NewsAPI returned no usable headlines.",
|
||||
ProviderHttpStatusCode: failureStatusCode,
|
||||
ProviderEndpoint: failureEndpoint,
|
||||
ProviderErrorCode: failureErrorCode);
|
||||
SetCachedValue(briefingCache, cacheKey, emptySnapshot, options.FailureCacheTtlSeconds);
|
||||
logger.LogWarning(
|
||||
"NewsAPI returned no usable headlines. Categories={Categories} RequestedHeadlineCount={RequestedHeadlineCount}",
|
||||
string.Join(",", categories),
|
||||
requestedHeadlineCount);
|
||||
return null;
|
||||
return emptySnapshot;
|
||||
}
|
||||
|
||||
var populatedSnapshot = new NewsBriefingSnapshot(headlines, "NewsAPI");
|
||||
var populatedSnapshot = new NewsBriefingSnapshot(
|
||||
headlines,
|
||||
"NewsAPI",
|
||||
ProviderStatus: "success");
|
||||
SetCachedValue(briefingCache, cacheKey, populatedSnapshot, options.CacheTtlSeconds);
|
||||
logger.LogInformation(
|
||||
"NewsAPI request partially filled headlines. Categories={Categories} HeadlineCount={HeadlineCount} RequestedHeadlineCount={RequestedHeadlineCount}",
|
||||
@@ -246,11 +320,16 @@ public sealed class NewsApiBriefingProvider(
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.LogWarning(exception, "NewsAPI lookup failed.");
|
||||
var exceptionSnapshot = new NewsBriefingSnapshot(
|
||||
Array.Empty<NewsHeadline>(),
|
||||
"NewsAPI",
|
||||
ProviderStatus: "exception",
|
||||
ProviderMessage: exception.Message);
|
||||
if (!string.IsNullOrWhiteSpace(cacheKey))
|
||||
{
|
||||
SetCachedValue(briefingCache, cacheKey, null, options.FailureCacheTtlSeconds);
|
||||
SetCachedValue(briefingCache, cacheKey, exceptionSnapshot, options.FailureCacheTtlSeconds);
|
||||
}
|
||||
return null;
|
||||
return exceptionSnapshot;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,6 +444,26 @@ public sealed class NewsApiBriefingProvider(
|
||||
return string.IsNullOrWhiteSpace(trimmed) ? null : trimmed;
|
||||
}
|
||||
|
||||
private static string SanitizeEndpoint(Uri uri)
|
||||
{
|
||||
var path = uri.GetLeftPart(UriPartial.Path);
|
||||
if (string.IsNullOrWhiteSpace(uri.Query))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
var filtered = uri.Query
|
||||
.TrimStart('?')
|
||||
.Split('&', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(static pair =>
|
||||
{
|
||||
var key = pair.Split('=', 2)[0];
|
||||
return !string.Equals(key, "apiKey", StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
var safeQuery = string.Join("&", filtered);
|
||||
return string.IsNullOrWhiteSpace(safeQuery) ? path : $"{path}?{safeQuery}";
|
||||
}
|
||||
|
||||
private string BuildCacheKey(IReadOnlyList<string> categories, int requestedHeadlineCount)
|
||||
{
|
||||
var categoryKey = string.Join(",", categories.Select(category => category.Trim().ToLowerInvariant()));
|
||||
|
||||
@@ -1267,7 +1267,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Boston, US", "light rain", 61, 65, 54, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Boston, U.S.", "light rain", 61, 65, 54, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1284,7 +1284,7 @@ public sealed class JiboInteractionServiceTests
|
||||
|
||||
Assert.Equal("personal_report_delivered", decision.IntentName);
|
||||
Assert.Contains("Great, alex. Here is your personal report.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("Right now in Boston, US, it is light rain and 61 degrees Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("Right now in Boston, U.S., it is light rain and 61 degrees Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("That is your personal report.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.NotNull(decision.ContextUpdates);
|
||||
Assert.Equal("idle", decision.ContextUpdates![PersonalReportStateKey]);
|
||||
@@ -1447,7 +1447,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Boston, US", "light rain", 61, 65, 54, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Boston, U.S.", "light rain", 61, 65, 54, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1471,7 +1471,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal(54, decision.SkillPayload["weather_low"]);
|
||||
Assert.Equal("F", decision.SkillPayload["weather_unit"]);
|
||||
Assert.Equal("Normal", decision.SkillPayload["weather_theme"]);
|
||||
Assert.Equal("Right now in Boston, US, it is light rain and 61 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("Right now in Boston, U.S., it is light rain and 61 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.NotNull(provider.LastRequest);
|
||||
Assert.False(provider.LastRequest!.IsTomorrow);
|
||||
Assert.Equal(0, provider.LastRequest.ForecastDayOffset);
|
||||
@@ -1482,7 +1482,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, US", "mostly cloudy", 72, 74, 60, "cloudy", false)
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "mostly cloudy", 72, 74, 60, "cloudy", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1496,7 +1496,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("Chicago", provider.LastRequest?.LocationQuery);
|
||||
Assert.True(provider.LastRequest?.IsTomorrow);
|
||||
Assert.Equal(1, provider.LastRequest?.ForecastDayOffset);
|
||||
Assert.Equal("Tomorrow in Chicago, US, expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("Tomorrow in Chicago, U.S., expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1504,7 +1504,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Seattle, U.S.", "light rain", 58, 61, 52, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1518,7 +1518,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("Seattle", provider.LastRequest?.LocationQuery);
|
||||
Assert.False(provider.LastRequest?.IsTomorrow);
|
||||
Assert.Equal(0, provider.LastRequest?.ForecastDayOffset);
|
||||
Assert.Equal("Right now in Seattle, US, it is light rain and 58 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("Right now in Seattle, U.S., it is light rain and 58 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1548,7 +1548,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Redmond, US", "clear sky", 63, 66, 52, "sunny", false)
|
||||
Snapshot = new WeatherReportSnapshot("Redmond, U.S.", "clear sky", 63, 66, 52, "sunny", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1562,7 +1562,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("Redmond Oregon", provider.LastRequest?.LocationQuery);
|
||||
Assert.False(provider.LastRequest?.IsTomorrow);
|
||||
Assert.Equal(0, provider.LastRequest?.ForecastDayOffset);
|
||||
Assert.Equal("Right now in Redmond, US, it is clear sky and 63 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("Right now in Redmond, U.S., it is clear sky and 63 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1570,7 +1570,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("New York, US", "partly cloudy", 71, 76, 61, "cloudy", false)
|
||||
Snapshot = new WeatherReportSnapshot("New York, U.S.", "partly cloudy", 71, 76, 61, "cloudy", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1584,7 +1584,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("New York City", provider.LastRequest?.LocationQuery);
|
||||
Assert.True(provider.LastRequest?.IsTomorrow);
|
||||
Assert.Equal(1, provider.LastRequest?.ForecastDayOffset);
|
||||
Assert.Equal("Tomorrow in New York, US, expect partly cloudy with a high near 76 degrees Fahrenheit and a low around 61 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("Tomorrow in New York, U.S., expect partly cloudy with a high near 76 degrees Fahrenheit and a low around 61 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1592,7 +1592,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Kansas City, US", "clear sky", 72, 79, 63, "sunny", false)
|
||||
Snapshot = new WeatherReportSnapshot("Kansas City, U.S.", "clear sky", 72, 79, 63, "sunny", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1606,7 +1606,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Null(provider.LastRequest?.LocationQuery);
|
||||
Assert.True(provider.LastRequest?.IsTomorrow);
|
||||
Assert.Equal(1, provider.LastRequest?.ForecastDayOffset);
|
||||
Assert.Equal("Tomorrow in Kansas City, US, expect clear sky with a high near 79 degrees Fahrenheit and a low around 63 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("Tomorrow in Kansas City, U.S., expect clear sky with a high near 79 degrees Fahrenheit and a low around 63 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1614,7 +1614,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, US", "mostly cloudy", 70, 75, 62, "cloudy", false)
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "mostly cloudy", 70, 75, 62, "cloudy", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1632,7 +1632,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("Chicago", provider.LastRequest?.LocationQuery);
|
||||
Assert.Null(provider.LastRequest?.Latitude);
|
||||
Assert.Null(provider.LastRequest?.Longitude);
|
||||
Assert.Equal("Right now in Chicago, US, it is mostly cloudy and 70 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("Right now in Chicago, U.S., it is mostly cloudy and 70 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -1652,7 +1652,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Test City, US", "light rain", 62, 66, 55, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Test City, U.S.", "light rain", 62, 66, 55, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1676,7 +1676,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Portland, US", "scattered clouds", 64, 68, 53, "cloudy", false)
|
||||
Snapshot = new WeatherReportSnapshot("Portland, U.S.", "scattered clouds", 64, 68, 53, "cloudy", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1697,7 +1697,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("weather", decision.IntentName);
|
||||
Assert.Equal(2, provider.LastRequest?.ForecastDayOffset);
|
||||
Assert.False(provider.LastRequest?.IsTomorrow);
|
||||
Assert.Equal("On Monday in Portland, US, expect scattered clouds with a high near 68 degrees Fahrenheit and a low around 53 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("On Monday in Portland, U.S., expect scattered clouds with a high near 68 degrees Fahrenheit and a low around 53 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1705,7 +1705,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, US", "light rain", 59, 63, 51, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "light rain", 59, 63, 51, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1722,7 +1722,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("weather", decision.IntentName);
|
||||
Assert.Equal("Chicago", provider.LastRequest?.LocationQuery);
|
||||
Assert.Equal(1, provider.LastRequest?.ForecastDayOffset);
|
||||
Assert.Equal("On Tuesday in Chicago, US, expect light rain with a high near 63 degrees Fahrenheit and a low around 51 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("On Tuesday in Chicago, U.S., expect light rain with a high near 63 degrees Fahrenheit and a low around 51 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1730,7 +1730,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, US", "light rain", 59, 63, 51, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "light rain", 59, 63, 51, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1779,7 +1779,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Seattle, U.S.", "light rain", 58, 61, 52, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1808,7 +1808,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Seattle, U.S.", "light rain", 58, 61, 52, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1824,7 +1824,7 @@ public sealed class JiboInteractionServiceTests
|
||||
|
||||
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("Seattle, U.S.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("Temperatures are in Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.NotNull(provider.LastRequest);
|
||||
Assert.Equal("Seattle", provider.LastRequest!.LocationQuery);
|
||||
@@ -1836,7 +1836,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Seattle, US", "light rain", 58, 61, 52, "rain", false)
|
||||
Snapshot = new WeatherReportSnapshot("Seattle, U.S.", "light rain", 58, 61, 52, "rain", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1852,7 +1852,7 @@ public sealed class JiboInteractionServiceTests
|
||||
|
||||
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("Seattle, U.S.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("Temperatures are in Fahrenheit.", decision.ReplyText, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.NotNull(provider.LastRequest);
|
||||
Assert.Null(provider.LastRequest!.LocationQuery);
|
||||
@@ -1864,7 +1864,7 @@ public sealed class JiboInteractionServiceTests
|
||||
{
|
||||
var provider = new CapturingWeatherReportProvider
|
||||
{
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, US", "mostly cloudy", 72, 74, 60, "cloudy", false)
|
||||
Snapshot = new WeatherReportSnapshot("Chicago, U.S.", "mostly cloudy", 72, 74, 60, "cloudy", false)
|
||||
};
|
||||
var service = CreateService(weatherReportProvider: provider);
|
||||
|
||||
@@ -1881,7 +1881,7 @@ public sealed class JiboInteractionServiceTests
|
||||
Assert.Equal("weather", decision.IntentName);
|
||||
Assert.Equal("Chicago", provider.LastRequest?.LocationQuery);
|
||||
Assert.Equal(2, provider.LastRequest?.ForecastDayOffset);
|
||||
Assert.Equal("The day after tomorrow in Chicago, US, expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText);
|
||||
Assert.Equal("The day after tomorrow in Chicago, U.S., expect mostly cloudy with a high near 74 degrees Fahrenheit and a low around 60 degrees Fahrenheit.", decision.ReplyText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -2232,6 +2232,9 @@ public sealed class JiboWebSocketServiceTests
|
||||
Assert.Equal(
|
||||
"weatherTempView",
|
||||
display.GetProperty("view").GetProperty("data").GetProperty("viewConfig").GetProperty("id").GetString());
|
||||
Assert.Equal(
|
||||
"weatherTempView",
|
||||
display.GetProperty("view").GetProperty("context").GetProperty("data").GetProperty("viewConfig").GetProperty("id").GetString());
|
||||
|
||||
Assert.True(jcpConfig.TryGetProperty("views", out var views));
|
||||
var weatherHiLo = views.GetProperty("weatherHiLo");
|
||||
|
||||
Reference in New Issue
Block a user