@@ -27,97 +27,110 @@ import org.junit.jupiter.api.Test
2727 * T-07-01 mitigation: HeroDemoTest uses identical syntax to the README example.
2828 */
2929class HeroDemoTest {
30-
3130 @Test
32- fun `hero demo - simple text response` () = runTest {
33- // This is the README hero demo pattern
34- val runner = agent(" demo-agent" ) {
35- model = MockLLMBackend (" mock" )
36- .whenCalled(
37- LLMChunk .Text (" Hello from kore!" ),
38- LLMChunk .Usage (inputTokens = 20 , outputTokens = 10 ),
39- LLMChunk .Done ,
40- )
41- budget(maxTokens = 1_000 )
42- }
31+ fun `hero demo - simple text response` () =
32+ runTest {
33+ // This is the README hero demo pattern
34+ val runner =
35+ agent(" demo-agent" ) {
36+ model =
37+ MockLLMBackend (" mock" )
38+ .whenCalled(
39+ LLMChunk .Text (" Hello from kore!" ),
40+ LLMChunk .Usage (inputTokens = 20 , outputTokens = 10 ),
41+ LLMChunk .Done ,
42+ )
43+ budget(maxTokens = 1_000 )
44+ }
4345
44- val result = runner.run (AgentTask (id = " demo-1" , input = " say hello" )).await()
46+ val result = runner.run (AgentTask (id = " demo-1" , input = " say hello" )).await()
4547
46- val success = result.shouldBeInstanceOf<AgentResult .Success >()
47- success.output shouldBe " Hello from kore!"
48- success.tokenUsage.inputTokens shouldBe 20
49- success.tokenUsage.outputTokens shouldBe 10
50- }
48+ val success = result.shouldBeInstanceOf<AgentResult .Success >()
49+ success.output shouldBe " Hello from kore!"
50+ success.tokenUsage.inputTokens shouldBe 20
51+ success.tokenUsage.outputTokens shouldBe 10
52+ }
5153
5254 @Test
53- fun `hero demo - tool calling loop` () = runTest {
54- val toolProvider = MockToolProvider ()
55- .withTool(" echo" , " Echoes the input" , " {}" )
56- .returnsFor(" echo" , " echoed!" )
57-
58- var llmCallNumber = 0
59- val mockBackend = object : LLMBackend {
60- override val name = " mock"
61- override fun call (
62- messages : List <ConversationMessage >,
63- tools : List <ToolDefinition >,
64- config : LLMConfig ,
65- ) = flow {
66- llmCallNumber++
67- if (llmCallNumber == 1 ) {
68- emit(LLMChunk .ToolCall (id = " c1" , name = " echo" , arguments = """ {"text":"hi"}""" ))
69- emit(LLMChunk .Done )
70- } else {
71- emit(LLMChunk .Text (" Tool returned: echoed!" ))
72- emit(LLMChunk .Done )
55+ fun `hero demo - tool calling loop` () =
56+ runTest {
57+ val toolProvider =
58+ MockToolProvider ()
59+ .withTool(" echo" , " Echoes the input" , " {}" )
60+ .returnsFor(" echo" , " echoed!" )
61+
62+ var llmCallNumber = 0
63+ val mockBackend =
64+ object : LLMBackend {
65+ override val name = " mock"
66+
67+ override fun call (
68+ messages : List <ConversationMessage >,
69+ tools : List <ToolDefinition >,
70+ config : LLMConfig ,
71+ ) = flow {
72+ llmCallNumber++
73+ if (llmCallNumber == 1 ) {
74+ emit(LLMChunk .ToolCall (id = " c1" , name = " echo" , arguments = """ {"text":"hi"}""" ))
75+ emit(LLMChunk .Done )
76+ } else {
77+ emit(LLMChunk .Text (" Tool returned: echoed!" ))
78+ emit(LLMChunk .Done )
79+ }
80+ }
7381 }
74- }
75- }
7682
77- val runner = agent(" tool-agent" ) {
78- model = mockBackend
79- tools(toolProvider)
80- }
83+ val runner =
84+ agent(" tool-agent" ) {
85+ model = mockBackend
86+ tools(toolProvider)
87+ }
8188
82- val result = runner.run (AgentTask (id = " tool-1" , input = " echo hi" )).await()
89+ val result = runner.run (AgentTask (id = " tool-1" , input = " echo hi" )).await()
8390
84- val success = result.shouldBeInstanceOf<AgentResult .Success >()
85- success.output shouldBe " Tool returned: echoed!"
86- llmCallNumber shouldBe 2
87- }
91+ val success = result.shouldBeInstanceOf<AgentResult .Success >()
92+ success.output shouldBe " Tool returned: echoed!"
93+ llmCallNumber shouldBe 2
94+ }
8895
8996 @Test
90- fun `fallback chain compiles and wraps backends` () = runTest {
91- val primary = MockLLMBackend (" primary" )
92- // No .whenCalled() on primary → throws on first call → fallback activates
93- val fallback = MockLLMBackend (" fallback" )
94- .whenCalled(LLMChunk .Text (" from fallback" ), LLMChunk .Done )
95-
96- val resilient = primary fallbackTo fallback
97-
98- resilient.name shouldBe " primary"
99-
100- // Primary has no responses — will fail — fallback should be used
101- val runner = agent(" fallback-agent" ) {
102- model = resilient
97+ fun `fallback chain compiles and wraps backends` () =
98+ runTest {
99+ val primary = MockLLMBackend (" primary" )
100+ // No .whenCalled() on primary → throws on first call → fallback activates
101+ val fallback =
102+ MockLLMBackend (" fallback" )
103+ .whenCalled(LLMChunk .Text (" from fallback" ), LLMChunk .Done )
104+
105+ val resilient = primary fallbackTo fallback
106+
107+ resilient.name shouldBe " primary"
108+
109+ // Primary has no responses — will fail — fallback should be used
110+ val runner =
111+ agent(" fallback-agent" ) {
112+ model = resilient
113+ }
114+ val result = runner.run (AgentTask (id = " fb-1" , input = " test" )).await()
115+ // primary fails (no scripted response) → fallback returns "from fallback"
116+ val success = result.shouldBeInstanceOf<AgentResult .Success >()
117+ success.output shouldBe " from fallback"
103118 }
104- val result = runner.run (AgentTask (id = " fb-1" , input = " test" )).await()
105- // primary fails (no scripted response) → fallback returns "from fallback"
106- val success = result.shouldBeInstanceOf<AgentResult .Success >()
107- success.output shouldBe " from fallback"
108- }
109119
110120 @Test
111- fun `budget exceeded stops the agent loop` () = runTest {
112- val backend = MockLLMBackend (" mock" )
113- .whenCalled(LLMChunk .Text (" response" ), LLMChunk .Done )
121+ fun `budget exceeded stops the agent loop` () =
122+ runTest {
123+ val backend =
124+ MockLLMBackend (" mock" )
125+ .whenCalled(LLMChunk .Text (" response" ), LLMChunk .Done )
126+
127+ val runner =
128+ agent(" budget-agent" ) {
129+ model = backend
130+ budget(maxTokens = 0L ) // zero budget → BudgetExceeded immediately
131+ }
114132
115- val runner = agent(" budget-agent" ) {
116- model = backend
117- budget(maxTokens = 0L ) // zero budget → BudgetExceeded immediately
133+ val result = runner.run (AgentTask (id = " budget-1" , input = " test" )).await()
134+ result.shouldBeInstanceOf<AgentResult .BudgetExceeded >()
118135 }
119-
120- val result = runner.run (AgentTask (id = " budget-1" , input = " test" )).await()
121- result.shouldBeInstanceOf<AgentResult .BudgetExceeded >()
122- }
123136}
0 commit comments