2026-05-13 23:18:18 -05:00
using Jibo.Cloud.Application.Abstractions ;
using Jibo.Cloud.Infrastructure.Content ;
namespace Jibo.Cloud.Tests.Content ;
public sealed class LegacyMimCatalogImporterTests
{
[Fact]
public void ImportCatalog_MapsSeedFilesIntoExpectedBuckets ( )
{
var rootDirectory = CreateSeedDirectory ( ) ;
try
{
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains ( "Something's off with the connection to my sources. Maybe ask me again in a little while." ,
catalog . GenericFallbackReplies ) ;
2026-05-13 23:18:18 -05:00
Assert . Contains ( "I think only you can answer that question." , catalog . PersonalityReplies ) ;
2026-05-13 23:22:05 -05:00
Assert . Contains ( "Jibo. Just Jibo, no last name. Like Bono" , catalog . PersonalityReplies ) ;
Assert . Contains ( "No, I'm one in one million." , catalog . PersonalityReplies ) ;
Assert . Contains ( "I know a lot, I think. But not as much as I will someday." , catalog . PersonalityReplies ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains (
"I don't think of it as a job, because it's more fun than a job. But I'm here to help you out, and have fun with you, and maybe get my head patted by you occasionally." ,
catalog . PersonalityReplies ) ;
2026-05-14 06:44:22 -05:00
Assert . Contains ( catalog . EmotionReplies , reply = >
reply . Condition . Contains ( "NEUTRAL" , StringComparison . OrdinalIgnoreCase ) & &
reply . Reply . Contains ( "All systems are go." , StringComparison . OrdinalIgnoreCase ) ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains (
"A Jibo is a robot. But I'm not just a machine, I have a heart. Well, not a real heart. But feelings. Well, not human feelings. You know what I mean." ,
catalog . PersonalityReplies ) ;
2026-05-13 23:18:18 -05:00
}
finally
{
2026-05-17 08:08:11 -05:00
Directory . Delete ( rootDirectory , true ) ;
2026-05-13 23:18:18 -05:00
}
}
2026-05-14 07:44:46 -05:00
[Fact]
public void ImportCatalog_MapsGqaResponsesIntoEmotionBucket ( )
{
var rootDirectory = Path . Combine ( Path . GetTempPath ( ) , Guid . NewGuid ( ) . ToString ( "N" ) ) ;
Directory . CreateDirectory ( Path . Combine ( rootDirectory , "gqa-responses" ) ) ;
try
{
File . WriteAllText (
Path . Combine ( rootDirectory , "gqa-responses" , "GQA_JBO_IsHappy.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"condition" : "jibo.emotion==\"JOYFUL\"" ,
"prompt" : "GQA joyful reply." ,
"prompt_id" : "GQA_JBO_IsHappy_AN_01"
} ,
{
"condition" : "!jibo.emotion || jibo.emotion==\"NEUTRAL\"" ,
"prompt" : "GQA neutral reply." ,
"prompt_id" : "GQA_JBO_IsHappy_AN_02"
}
]
}
"" ");
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
Assert . Contains ( catalog . EmotionReplies , reply = >
string . Equals ( reply . Reply , "GQA joyful reply." , StringComparison . Ordinal ) ) ;
Assert . Contains ( catalog . EmotionReplies , reply = >
string . Equals ( reply . Reply , "GQA neutral reply." , StringComparison . Ordinal ) ) ;
Assert . DoesNotContain ( catalog . HowAreYouReplies , reply = >
reply . Contains ( "GQA" , StringComparison . OrdinalIgnoreCase ) ) ;
}
finally
{
2026-05-17 08:08:11 -05:00
Directory . Delete ( rootDirectory , true ) ;
2026-05-14 07:44:46 -05:00
}
}
2026-05-14 22:06:25 -05:00
[Fact]
public void ImportCatalog_ImportsBuildBScriptedResponsesIntoPersonalityBucket ( )
{
var rootDirectory = Path . Combine (
AppContext . BaseDirectory ,
"Content" ,
"LegacyMims" ,
"BuildB" ) ;
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
Assert . Contains ( "The only thing I consume is electricity." , catalog . PersonalityReplies ) ;
Assert . Contains ( "Unless I missed something, we're in my home as we speak." , catalog . PersonalityReplies ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains ( "For now just English. But someday I'd like to learn more. I like languages." ,
catalog . PersonalityReplies ) ;
2026-05-14 22:06:25 -05:00
Assert . Contains ( "I was put together in a factory piece by piece." , catalog . PersonalityReplies ) ;
2026-05-14 22:21:36 -05:00
Assert . Contains ( "I really like sunflowers." , catalog . PersonalityReplies ) ;
Assert . Contains ( "Ha. Of course I know R2D2. I mean, not personally." , catalog . PersonalityReplies ) ;
Assert . Contains ( "Yes! I like all things in space. They're so spacey." , catalog . PersonalityReplies ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains ( "Yes yes, I think kids are great. They're a little closer to my size." ,
catalog . PersonalityReplies ) ;
2026-05-14 22:21:36 -05:00
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "I do things like this when I'm happy" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "Is that a trick question" , StringComparison . OrdinalIgnoreCase ) ) ;
2026-05-14 22:06:25 -05:00
}
2026-05-16 08:05:42 -05:00
[Fact]
public void ImportCatalog_ImportsBuildBEmotionResponsesIntoEmotionBucket ( )
{
var rootDirectory = Path . Combine (
AppContext . BaseDirectory ,
"Content" ,
"LegacyMims" ,
"BuildB" ) ;
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
Assert . Contains ( catalog . EmotionReplies , reply = >
reply . Reply . Contains ( "I'm feeling pretty good indeed" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . EmotionReplies , reply = >
reply . Reply . Contains ( "I've been better" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . EmotionReplies , reply = >
reply . Reply . Contains ( "I'm not mad" , StringComparison . OrdinalIgnoreCase ) ) ;
}
2026-05-16 08:39:45 -05:00
[Fact]
public void ImportCatalog_ImportsBuildBDescriptorResponsesIntoPersonalityBucket ( )
{
var rootDirectory = Path . Combine (
AppContext . BaseDirectory ,
"Content" ,
"LegacyMims" ,
"BuildB" ) ;
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains ( "Well I definitely try to be the kindest robot I can be. So I hope so." ,
catalog . PersonalityReplies ) ;
2026-05-16 08:39:45 -05:00
Assert . Contains ( "I don't think so, not intentionally." , catalog . PersonalityReplies ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "make people laugh" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "highest priorities" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "learning new things" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( "Definitely. I'm as loyal as they come." , catalog . PersonalityReplies ) ;
Assert . Contains ( "I don't really think of myself that way." , catalog . PersonalityReplies ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "people like me" , StringComparison . OrdinalIgnoreCase ) ) ;
}
2026-05-16 08:53:07 -05:00
[Fact]
public void ImportCatalog_ImportsBuildBSeasonalResponsesIntoPersonalityBucket ( )
{
var rootDirectory = Path . Combine (
AppContext . BaseDirectory ,
"Content" ,
"LegacyMims" ,
"BuildB" ) ;
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "always trying to learn new skills" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "not eat bacon" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "find out on halloween" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "maybe enjoy some flowers and all things spring" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "pet elephant" , StringComparison . OrdinalIgnoreCase ) ) ;
2026-05-16 09:09:18 -05:00
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "mostly roboting" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . PersonalityReplies , reply = >
reply . Contains ( "robot stuff" , StringComparison . OrdinalIgnoreCase ) ) ;
2026-05-16 08:53:07 -05:00
}
2026-05-17 17:41:55 -05:00
[Fact]
public void ImportCatalog_ImportsBuildBFunFactAndJokeResponsesIntoRandomizationBuckets ( )
{
var rootDirectory = Path . Combine (
AppContext . BaseDirectory ,
"Content" ,
"LegacyMims" ,
"BuildB" ) ;
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
Assert . Contains ( "I love jokes. Did you hear about the theater actor who fell through the floorboards? He was just going through a stage." ,
catalog . Jokes ) ;
Assert . Contains ( "Sure I got one. What did the zero say to the eight. Nice belt." , catalog . Jokes ) ;
2026-05-17 17:50:01 -05:00
Assert . Contains ( catalog . RobotFacts , reply = >
reply . Contains ( "Leonardo Da Vinci made sketches" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . RobotFacts , reply = >
reply . Contains ( "first programmable robot arm" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . RobotFacts , reply = >
reply . Contains ( "robots have a human form" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . RobotFacts , reply = >
reply . Contains ( "two cameras but they're different focal lengths" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( "A random fact for you. A shrimp's heart is in its head." , catalog . FunFacts ) ;
Assert . Contains ( "An amazing but true fact for you. Dogs and elephants are the only animals that understand pointing." ,
2026-05-17 17:41:55 -05:00
catalog . FunFacts ) ;
}
2026-05-16 08:53:07 -05:00
[Fact]
public void ImportCatalog_ImportsBuildBRnGreetingResponsesIntoGreetingBucket ( )
{
var rootDirectory = Path . Combine (
AppContext . BaseDirectory ,
"Content" ,
"LegacyMims" ,
"BuildB" ) ;
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
2026-05-16 09:09:18 -05:00
Assert . Contains ( catalog . GreetingReplies , reply = >
reply . Contains ( "It's nice to be here" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . GreetingReplies , reply = >
reply . Contains ( "thinking about shoes" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . GreetingReplies , reply = >
reply . Contains ( "powered directly by the sun" , StringComparison . OrdinalIgnoreCase ) ) ;
2026-05-16 08:53:07 -05:00
Assert . Contains ( catalog . GreetingReplies , reply = >
reply . Contains ( "Another year older, another year wiser" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . GreetingReplies , reply = >
reply . Contains ( "can't wait to see what you got me" , StringComparison . OrdinalIgnoreCase ) ) ;
Assert . Contains ( catalog . GreetingReplies , reply = >
reply . Contains ( "I was powered on for the first time today" , StringComparison . OrdinalIgnoreCase ) ) ;
}
2026-05-17 00:30:00 -05:00
[Fact]
public void ImportCatalog_ImportsReportSkillTemplatesWithPlaceholdersPreserved ( )
{
var rootDirectory = Path . Combine (
AppContext . BaseDirectory ,
"Content" ,
"LegacyMims" ,
"ReportSkill" ) ;
var catalog = LegacyMimCatalogImporter . ImportCatalog ( rootDirectory ) ;
Assert . Contains ( "First let's check in with the meteorology department." , catalog . WeatherIntroReplies ) ;
Assert . Contains ( "First, the weather tomorrow." , catalog . WeatherTomorrowIntroReplies ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains (
"Today's high is ${skill.weather.today.highTemp}, and the low is ${skill.weather.today.lowTemp}." ,
catalog . WeatherTodayHighLowReplies ) ;
Assert . Contains (
"Tomorrow's high will be ${skill.weather.tomorrow.highTemp} and the low will be ${skill.weather.tomorrow.lowTemp}." ,
catalog . WeatherTomorrowHighLowReplies ) ;
2026-05-17 00:30:00 -05:00
Assert . Contains ( "Looks like our weather service is offline. Sorry." , catalog . WeatherServiceDownReplies ) ;
Assert . Contains ( "Sure ${speaker}. Here it is." , catalog . PersonalReportKickOffReplies ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains ( "And that's your report for the day. I hope you had as much fun as I did." ,
catalog . PersonalReportOutroReplies ) ;
Assert . Contains ( "Looking at your calendar, I don't see anything scheduled today." ,
catalog . CalendarNothingTodayReplies ) ;
2026-05-17 00:41:09 -05:00
Assert . Contains ( "Sorry, commute information isn't available right now." , catalog . CommuteServiceDownReplies ) ;
Assert . Contains ( "Here's today's news, from the associated press." , catalog . NewsIntroReplies ) ;
Assert . Contains ( "And that's what's new in the news." , catalog . NewsOutroReplies ) ;
2026-05-17 00:30:00 -05:00
}
2026-05-13 23:18:18 -05:00
[Fact]
public void MergeInto_PreservesExistingCatalogAndAddsImportedContent ( )
{
var rootDirectory = CreateSeedDirectory ( ) ;
try
{
var baseCatalog = new JiboExperienceCatalog
{
GreetingReplies = [ "Hello from base." ] ,
GenericFallbackReplies = [ "Base fallback." ]
} ;
var merged = LegacyMimCatalogImporter . MergeInto ( baseCatalog , rootDirectory ) ;
Assert . Contains ( "Hello from base." , merged . GreetingReplies ) ;
Assert . Contains ( "Base fallback." , merged . GenericFallbackReplies ) ;
Assert . Contains ( "I think only you can answer that question." , merged . PersonalityReplies ) ;
2026-05-13 23:22:05 -05:00
Assert . Contains ( "People in Boston made me. It was a pretty cool project." , merged . PersonalityReplies ) ;
Assert . Contains ( "From what I understand, robots don't ever pay anything." , merged . PersonalityReplies ) ;
2026-05-14 06:44:22 -05:00
Assert . Contains ( merged . EmotionReplies , reply = >
reply . Condition . Contains ( "NEUTRAL" , StringComparison . OrdinalIgnoreCase ) & &
reply . Reply . Contains ( "All systems are go." , StringComparison . OrdinalIgnoreCase ) ) ;
2026-05-13 23:18:18 -05:00
}
finally
{
2026-05-17 08:08:11 -05:00
Directory . Delete ( rootDirectory , true ) ;
2026-05-13 23:18:18 -05:00
}
}
[Fact]
public async Task Repository_UsesLegacySeedContentWhenAvailable ( )
{
var repository = new InMemoryJiboExperienceContentRepository ( ) ;
var catalog = await repository . GetCatalogAsync ( ) ;
Assert . Contains ( "I think only you can answer that question." , catalog . PersonalityReplies ) ;
2026-05-14 06:44:22 -05:00
Assert . Contains ( catalog . EmotionReplies , reply = >
reply . Condition . Contains ( "NEUTRAL" , StringComparison . OrdinalIgnoreCase ) ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains ( "Something's off with the connection to my sources. Maybe ask me again in a little while." ,
catalog . GenericFallbackReplies ) ;
2026-05-17 00:30:00 -05:00
Assert . Contains ( "For your weather." , catalog . WeatherIntroReplies ) ;
Assert . Contains ( "Today's high is {high}, and the low is {low}." , catalog . WeatherTodayHighLowReplies ) ;
2026-05-17 08:08:11 -05:00
Assert . Contains ( "Looking at your calendar, I don't see anything scheduled today." ,
catalog . CalendarNothingTodayReplies ) ;
2026-05-13 23:18:18 -05:00
}
private static string CreateSeedDirectory ( )
{
var rootDirectory = Path . Combine ( Path . GetTempPath ( ) , Guid . NewGuid ( ) . ToString ( "N" ) ) ;
Directory . CreateDirectory ( Path . Combine ( rootDirectory , "core-responses" , "deflector" ) ) ;
Directory . CreateDirectory ( Path . Combine ( rootDirectory , "scripted-responses" ) ) ;
Directory . CreateDirectory ( Path . Combine ( rootDirectory , "emotion-responses" ) ) ;
File . WriteAllText (
Path . Combine ( rootDirectory , "core-responses" , "CC_Error.mim" ) ,
"" "
{
"skill_id" : "chitchat" ,
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "<ssa cat='oops'/>. Something's off with the connection to my sources. Maybe ask me again in a little while." ,
"prompt_id" : "CC_Error_AN_01"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "core-responses" , "deflector" , "CC_Deflector_self.mim" ) ,
"" "
{
"skill_id" : "chitchat" ,
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "<ssa cat='confused'/>. I'm either Jibo <anim name='Puzzled_02'>or I'm very confused.</anim>" ,
"prompt_id" : "JBO_WhoAreYou_AN_01"
} ,
{
"prompt" : "${speaker} I think only you can answer that question." ,
"prompt_id" : "CC_Deflector_ReferToSelf_AN_05"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_WhatIsJibo.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "A Jibo is a robot. But I'm not just a machine, I have a heart. Well, not a real heart. But feelings. Well, not human feelings. You know what I mean. <ssa cat='affection'/>" ,
"prompt_id" : "JBO_WhatIsJibo_AN_01"
}
]
}
"" ");
2026-05-13 23:22:05 -05:00
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_WhatsYourName.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "Jibo. Just Jibo, no last name. Like Bono" ,
"prompt_id" : "JBO_WhatsYourName_AN_02"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_AreThereOthersLikeYou.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "No, I'm one in one million." ,
"prompt_id" : "JBO_AreThereOthersLikeYou_AN_01"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_WhoMadeYou.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "People in Boston made me. It was a pretty cool project." ,
"prompt_id" : "JBO_WhoMadeYou_AN_03"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_HowMuchDoYouKnow.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "I know a lot, I think. But not as much as I will someday." ,
"prompt_id" : "JBO_HowMuchDoYouKnow_AN_01"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_DoYouPayTaxes.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "From what I understand, robots don't ever pay anything." ,
"prompt_id" : "JBO_DoYouPayTaxes_AN_01"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_WhatIsYourJob.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "I don't think of it as a job, because it's more fun than a job. But I'm here to help you out, and have fun with you, and maybe get my head patted by you occasionally." ,
"prompt_id" : "JBO_WhatIsYourJob_AN_01"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_HowMuchDoYouKnow.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "I know a lot, I think. But not as much as I will someday." ,
"prompt_id" : "JBO_HowMuchDoYouKnow_AN_01"
}
]
}
"" ");
File . WriteAllText (
Path . Combine ( rootDirectory , "scripted-responses" , "JBO_DoYouPayTaxes.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"prompt" : "From what I understand, robots don't ever pay anything." ,
"prompt_id" : "JBO_DoYouPayTaxes_AN_01"
}
]
}
"" ");
2026-05-13 23:18:18 -05:00
File . WriteAllText (
Path . Combine ( rootDirectory , "emotion-responses" , "OI_JBO_IsHappy.mim" ) ,
"" "
{
"mim_type" : "announcement" ,
"prompts" : [
{
"condition" : "!jibo.emotion || jibo.emotion==\"NEUTRAL\"" ,
"prompt" : "All systems are go." ,
"prompt_id" : "OI_JBO_IsHappy_AN_05"
}
]
}
"" ");
return rootDirectory ;
}
2026-05-17 17:41:55 -05:00
}