Skip to content

Commit 39266bd

Browse files
committed
test: restore PIT 100% (ChatTranscript.removePendingUserTurn) + jsonSchemaToGrammar guard
Fixes the mutation-coverage gate that PR #258 (audit correctness fixes) dropped to 97%. - ChatTranscript.removePendingUserTurn() was added in #258 (for Session.cancelStream's stream rollback) with zero test coverage. ChatTranscript is a PIT target class (value.*), so all 6 of its mutants survived (NO_COVERAGE) → score 97% < 100% threshold, failing the Ubuntu job. Add three ChatTranscriptTest cases — trailing user turn removed (true + rolled back), assistant-ending round (false + intact), empty transcript (false + no throw) — which kill all 6 mutants (2 NegateConditionals, 2 MathMutator, the true/false return-value mutators). - Add testJsonSchemaToGrammarRejectsMalformedSchema: a regression for the Tier 1 JNI exception-boundary fix. A malformed schema passed to the public static LlamaModel.jsonSchemaToGrammar(String) used to let a C++ json::parse exception cross the JNI boundary and abort the JVM; it must now surface as a catchable LlamaException. (Model-gated by the class @BeforeAll, like the existing happy-path test, so it is exercised in the CI Java Tests jobs.) Verified locally: full configured PIT (mvn test-compile org.pitest:pitest-maven:mutationCoverage) 234/234 mutations killed (100%), BUILD SUCCESS; ChatTranscriptTest 15/15 green; Spotless clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qg1mYW7hHjtVvAfMeMEmPq
1 parent 90f95d0 commit 39266bd

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

src/test/java/net/ladenthin/llama/LlamaModelTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,14 @@ public void testJsonSchemaToGrammar() {
691691
assertEquals(expectedGrammar, actualGrammar);
692692
}
693693

694+
@Test
695+
public void testJsonSchemaToGrammarRejectsMalformedSchema() {
696+
// Regression for the JNI exception-boundary fix (audit Tier 1, PR #258): a malformed schema
697+
// string used to let a C++ json::parse exception escape across the JNI boundary and abort the
698+
// JVM. It must now surface as a catchable LlamaException instead of crashing the process.
699+
assertThrows(LlamaException.class, () -> LlamaModel.jsonSchemaToGrammar("{ this is not valid json"));
700+
}
701+
694702
@Test
695703
public void testTemplate() {
696704

src/test/java/net/ladenthin/llama/value/ChatTranscriptTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,44 @@ void appendUserAndAssistantSeparatelyMatchAppendRound() {
109109
assertThat("atomic-round and split-commit must converge", b.snapshot(), is(a.snapshot()));
110110
}
111111

112+
@Test
113+
@DisplayName("removePendingUserTurn drops a trailing user turn and reports true")
114+
void removePendingUserTurnDropsTrailingUserTurn() {
115+
ChatTranscript t = new ChatTranscript(null);
116+
t.appendUserTurn("pending question");
117+
assertThat("precondition: one dangling user turn", t.size(), is(1));
118+
119+
boolean removed = t.removePendingUserTurn();
120+
121+
assertThat("a dangling user turn must be reported as removed", removed, is(true));
122+
assertThat("transcript is rolled back to its pre-stream (empty) shape", t.size(), is(0));
123+
}
124+
125+
@Test
126+
@DisplayName("removePendingUserTurn is a no-op when the last turn is an assistant turn")
127+
void removePendingUserTurnKeepsCommittedRound() {
128+
ChatTranscript t = new ChatTranscript(null);
129+
t.appendRound("q", "a"); // last turn is "assistant", not a dangling user turn
130+
131+
boolean removed = t.removePendingUserTurn();
132+
133+
assertThat("a committed round has no dangling user turn to remove", removed, is(false));
134+
assertThat("the committed round must be left intact", t.size(), is(2));
135+
assertThat(t.snapshot().get(0).getRole(), is("user"));
136+
assertThat(t.snapshot().get(1).getRole(), is("assistant"));
137+
}
138+
139+
@Test
140+
@DisplayName("removePendingUserTurn is a safe no-op on an empty transcript")
141+
void removePendingUserTurnNoOpOnEmptyTranscript() {
142+
ChatTranscript t = new ChatTranscript("system");
143+
144+
boolean removed = t.removePendingUserTurn();
145+
146+
assertThat("an empty transcript has nothing to remove", removed, is(false));
147+
assertThat(t.size(), is(0));
148+
}
149+
112150
@Test
113151
@DisplayName("messagesWithPendingUserTurn does NOT mutate the transcript")
114152
void messagesWithPendingUserTurnDoesNotMutate() {

0 commit comments

Comments
 (0)