Ctrl K
ComunidadGuías…

internal/adminapi/attention_handler_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package adminapi

import (
	"context"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/yeomyeonggeori/blueclaw/internal/llm"
)

func TestAttentionHandlerReturnsSilentDecision(t *testing.T) {
	languageModel := &recordingAttentionLanguageModel{content: `{"status":"ATTENTION_SILENT","message":"","reason":"not_useful"}`}
	handler := AttentionHandler{LanguageModel: languageModel}
	request := httptest.NewRequest(http.MethodPost, "/admin/api/attention/run", strings.NewReader(`{
		"jobID":"job-1",
		"toolName":"user_confirm",
		"localDecision":{
			"shouldEscalate":true,
			"importance":"medium",
			"confidence":0.9,
			"reasonCodes":["blocked"],
			"summaryForRemote":"Waiting for confirmation.",
			"privacyClass":"user_input"
		}
	}`))
	responseRecorder := httptest.NewRecorder()

	handler.HandleRunAttention(responseRecorder, request)

	if responseRecorder.Code != http.StatusOK {
		t.Fatalf("expected ok response, got %d: %s", responseRecorder.Code, responseRecorder.Body.String())
	}
	if !strings.Contains(responseRecorder.Body.String(), "ATTENTION_SILENT") {
		t.Fatalf("expected silent response, got %s", responseRecorder.Body.String())
	}
	if languageModel.request.StructuredOutputSchema.Name != "blueclaw_attention_decision" {
		t.Fatalf("expected attention schema, got %+v", languageModel.request.StructuredOutputSchema)
	}
}

func TestAttentionHandlerReturnsMessageDecision(t *testing.T) {
	handler := AttentionHandler{LanguageModel: &recordingAttentionLanguageModel{content: `{"status":"ATTENTION_MESSAGE","message":"This looks like it needs a check.","reason":"blocked"}`}}
	request := httptest.NewRequest(http.MethodPost, "/admin/api/attention/run", strings.NewReader(`{
		"jobID":"job-1",
		"toolName":"browser_wait",
		"localDecision":{
			"shouldEscalate":true,
			"importance":"high",
			"confidence":0.95,
			"reasonCodes":["blocked"],
			"summaryForRemote":"The page is still loading.",
			"privacyClass":"device_browser"
		}
	}`))
	responseRecorder := httptest.NewRecorder()

	handler.HandleRunAttention(responseRecorder, request)

	if responseRecorder.Code != http.StatusOK {
		t.Fatalf("expected ok response, got %d: %s", responseRecorder.Code, responseRecorder.Body.String())
	}
	if !strings.Contains(responseRecorder.Body.String(), "ATTENTION_MESSAGE") || !strings.Contains(responseRecorder.Body.String(), "This looks like it needs a check.") {
		t.Fatalf("expected message response, got %s", responseRecorder.Body.String())
	}
}

func TestAttentionHandlerRejectsSilentLocalDecision(t *testing.T) {
	handler := AttentionHandler{LanguageModel: &recordingAttentionLanguageModel{content: `{"status":"ATTENTION_SILENT","message":"","reason":"not_useful"}`}}
	request := httptest.NewRequest(http.MethodPost, "/admin/api/attention/run", strings.NewReader(`{
		"jobID":"job-1",
		"toolName":"user_confirm",
		"localDecision":{
			"shouldEscalate":false,
			"summaryForRemote":"Waiting for confirmation."
		}
	}`))
	responseRecorder := httptest.NewRecorder()

	handler.HandleRunAttention(responseRecorder, request)

	if responseRecorder.Code != http.StatusBadRequest {
		t.Fatalf("expected bad request, got %d: %s", responseRecorder.Code, responseRecorder.Body.String())
	}
}

type recordingAttentionLanguageModel struct {
	content string
	request llm.StructuredResponseRequest
}

func (languageModel *recordingAttentionLanguageModel) GenerateResponse(context.Context, string) (string, error) {
	return "", nil
}

func (languageModel *recordingAttentionLanguageModel) GenerateStructuredResponse(_ context.Context, request llm.StructuredResponseRequest) (llm.StructuredResponse, error) {
	languageModel.request = request
	return llm.StructuredResponse{Content: languageModel.content}, nil
}