Files
claude-botandClaude Opus 5.5 7f29ebb878
review-bot reviewed: comment
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/review-bot-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
chore: follow homelab to konairius/homelab
Flux source URL, flux-git rotator verify repo, review-bot REPOS, pipeline API,
clone and push URLs, the heating-can source label and the review-bot and
kuma-sync Go module paths. Merge right after the transfer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:43:23 +02:00

69 lines
3.1 KiB
Go

package main
import (
"context"
"strings"
"testing"
)
// A stream that breaks off after the conversation started is answered from its stored history.
func TestBrokenStreamIsAnsweredFromTheHistory(t *testing.T) {
f := newFixture(t)
f.breakStreams = 1
f.history = `{"conversation_id":"conv_1","entries":[
{"type":"message.input","content":"Review pull request #7"},
{"type":"tool.execution","name":"conn_get_file_contents"},
{"type":"message.output","content":"## Summary\n\nfine\n\n## Recommendation\n\nLooks reasonable.\n\nVerdict: comment"}]}`
if err := f.rev.Review(context.Background(), "konairius/homelab", 7); err != nil {
t.Fatal(err)
}
if f.convCalls != 1 || f.historyCalls != 1 || len(f.postedReviews()) != 1 {
t.Errorf("conversations %d, history reads %d, reviews %d; want 1, 1 and 1", f.convCalls, f.historyCalls, len(f.postedReviews()))
}
}
// Without a finished answer in the history, a broken stream is asked again.
func TestBrokenStreamWithoutHistoryIsAskedAgain(t *testing.T) {
f := newFixture(t)
f.breakStreams = 1
if err := f.rev.Review(context.Background(), "konairius/homelab", 7); err != nil {
t.Fatal(err)
}
if f.convCalls != 2 || f.historyCalls != 2 || len(f.postedReviews()) != 1 {
t.Errorf("conversations %d, history reads %d, reviews %d; want 2, 2 and 1", f.convCalls, f.historyCalls, len(f.postedReviews()))
}
}
func TestStreamErrorEventIsNotRetried(t *testing.T) {
f := newFixture(t)
f.streamError = "Failed to generate response."
err := f.rev.Review(context.Background(), "konairius/homelab", 7)
if err == nil || !strings.Contains(err.Error(), "Failed to generate response.") {
t.Fatalf("want the stream error, got %v", err)
}
if f.convCalls != 1 || len(f.postedReviews()) != 0 {
t.Errorf("conversations %d, reviews %d; want 1 and 0", f.convCalls, len(f.postedReviews()))
}
}
func TestReadStreamJoinsDeltasOfOneMessage(t *testing.T) {
stream := "event: conversation.response.started\ndata: {\"type\":\"conversation.response.started\",\"conversation_id\":\"c9\"}\n\n" +
"data: {\"type\":\"message.output.delta\",\"id\":\"m1\",\"content\":\"## Sum\"}\n\n" +
"data: {\"type\":\"message.output.delta\",\"id\":\"m1\",\"content\":{\"type\":\"thinking\",\"thinking\":[]}}\n\n" +
"data: {\"type\":\"message.output.delta\",\"id\":\"m1\",\"content\":{\"type\":\"text\",\"text\":\"mary\"}}\n\n" +
"data: {\"type\":\"tool.execution.started\",\"id\":\"t1\",\"name\":\"conn_list_commits\",\"arguments\":\"{}\"}\n\n" +
"data: {\"type\":\"message.output.delta\",\"id\":\"m2\",\"content\":\"next\"}\r\n\r\n" +
"data: {\"type\":\"conversation.response.done\"}\n\n"
conv, a, err := readStream(strings.NewReader(stream))
if err != nil || conv != "c9" || a.Text != "## Summary\nnext" || a.ToolCalls != 1 {
t.Errorf("conv %q, answer %+v, err %v", conv, a, err)
}
}
func TestReadStreamWithoutDoneIsBroken(t *testing.T) {
_, _, err := readStream(strings.NewReader("data: {\"type\":\"conversation.response.started\",\"conversation_id\":\"c9\"}\n\n"))
if !retryable(err) {
t.Errorf("a stream that ends without done must be retryable, got %v", err)
}
}