Ctrl K
ComunidadGuías…

internal/approvalgate/question_facts_test.go

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

Archivo original ↗Preguntar a KiBot
//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)
	}
}