internal/approvalgate/question_facts_test.go
Rama main · réplica de lectura del repositorio original.
//go:build !nobundledharness
package approvalgate
import (
"context"
"encoding/json"
"strings"
"testing"
"github.com/yeomyeonggeori/bluecollar/approval"
"github.com/yeomyeonggeori/blueprotocol/toolcontract"
)
func TestTheWordingIsHandedTheInputsTheToolDeclaresAndTheModelsDraft(t *testing.T) {
gate, _, taskRun := gateFixture(t)
languageModel := &wordingLanguageModel{question: "명령을 실행할까요?"}
gate.UseQuestionWorder(approval.NewWorder(languageModel))
request := approvalRequestFixture(taskRun.TaskRunID)
request.ToolName = "bash"
request.ToolInput = json.RawMessage(`{"command":"rm -r build","approvalReason":"빌드를 다시 하기 위해"}`)
request.ApprovalInputFields = []string{"command"}
request.ModelDraft = "build 폴더를 지울까요?"
gate.AwaitApproval(context.Background(), request)
wordingContext := marshalRequestMessages(languageModel.lastRequest)
if strings.Contains(wordingContext, "approvalReason") || !strings.Contains(wordingContext, "rm -r build") || !strings.Contains(wordingContext, "build 폴더를 지울까요?") {
t.Fatalf("expected the declared input and the draft to reach the wording and the reason to stay out, got %s", wordingContext)
}
}
func TestTheTurnGateHandsTheToolsApprovalFactsToTheWording(t *testing.T) {
gate, _, taskRun := gateFixture(t)
languageModel := &wordingLanguageModel{question: "삭제할까요?"}
gate.UseQuestionWorder(approval.NewWorder(languageModel))
toolSet := toolcontract.NewToolSet([]string{"file_delete"})
toolSet.AllowTestReplacement()
registerDeletingTool(t, toolSet)
toolSet.UseToolCallGate(gate.TurnGate(TurnContext{RequesterPersonID: "person-1"}))
toolSet.Invoke(toolcontract.WithTaskRunID(context.Background(), taskRun.TaskRunID), toolcontract.ToolInvocation{ToolName: "file_delete", Input: json.RawMessage(`{"path":"~/a.md","note":"x"}`)})
wordingContext := marshalRequestMessages(languageModel.lastRequest)
for _, expectedFragment := range []string{`"operation":"file_delete"`, `"path":"~/a.md"`} {
if !strings.Contains(wordingContext, expectedFragment) {
t.Fatalf("expected %q in %s", expectedFragment, wordingContext)
}
}
if strings.Contains(wordingContext, `"note"`) {
t.Fatalf("an input the tool did not declare is not what the action does, got %s", wordingContext)
}
}
func registerDeletingTool(t *testing.T, toolSet *toolcontract.ToolSet) {
t.Helper()
errorValue := toolSet.RegisterTool(toolcontract.ToolDefinition{
ID: "test:file_delete",
Name: "file_delete",
Description: "file_delete",
Visibility: toolcontract.ToolVisibilityModel,
InputSchema: json.RawMessage(`{"type":"object"}`),
RequiresApproval: true,
ApprovalInputFields: []string{"path"},
SideEffectClass: toolcontract.ToolSideEffectStateChange,
ResultContract: &toolcontract.ToolResultContract{Schema: json.RawMessage(`{"type":"object"}`)},
}, func(context.Context, toolcontract.ToolInvocation) (toolcontract.ToolResult, error) {
return toolcontract.ToolSuccessData("done", json.RawMessage(`{}`)), nil
})
if errorValue != nil {
t.Fatalf("expected the tool to register: %v", errorValue)
}
}
func TestTheChoicesATargetCarriesAreHandedToTheWording(t *testing.T) {
gate, _, taskRun, _, _ := choiceGate(t, ApprovalAnswer{})
languageModel := &wordingLanguageModel{question: "지금 업데이트할까요?"}
gate.UseQuestionWorder(approval.NewWorder(languageModel))
gate.AwaitApproval(context.Background(), hostUpdateRequest(taskRun.TaskRunID))
wordingContext := marshalRequestMessages(languageModel.lastRequest)
if !strings.Contains(wordingContext, `"key":"offHours"`) || !strings.Contains(wordingContext, laterStartsAt) {
t.Fatalf("the question offers exactly the choices the host computed, got %s", wordingContext)
}
}