Ctrl K
ComunidadGuías…

internal/approvalgate/redeemed_call_test.go

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

Archivo original ↗Preguntar a KiBot
package approvalgate

import (
	"encoding/json"
	"errors"
	"sync"
	"testing"

	"github.com/yeomyeonggeori/blueclaw/internal/task"
	"github.com/yeomyeonggeori/blueprotocol/agentcontract"
	"github.com/yeomyeonggeori/blueprotocol/holdrecord"
)

type heldCallFixture struct {
	taskRunService *task.TaskRunService
	taskRunID      string
	holdID         string
}

func aHeldMessage(t *testing.T, decision string) heldCallFixture {
	t.Helper()
	taskRunService := task.NewTaskRunService(task.NewTaskEventService())
	taskRun := taskRunService.CreateTaskRun("person-1", "conversation-1", "박예시에게 보내줘")
	holdID := holdrecord.Open(taskRunService, taskRun.TaskRunID, agentcontract.HeldCall{
		ToolName:          "message_send",
		ToolInput:         json.RawMessage(`{"recipientHint":"박예시","message":"내일 10시 어때요?"}`),
		ApprovedToolInput: json.RawMessage(`{"message":"내일 10시 어때요?","recipientHint":"person-2"}`),
	}, nil).ID
	if decision != "" {
		holdrecord.Decide(taskRunService, taskRun.TaskRunID, holdID, decision, "chat_reply")
	}
	return heldCallFixture{taskRunService: taskRunService, taskRunID: taskRun.TaskRunID, holdID: holdID}
}

func (fixture heldCallFixture) approvedCall() HeldCallRedemption {
	return HeldCallRedemption{
		TaskRunID:         fixture.taskRunID,
		HoldID:            fixture.holdID,
		RequesterPersonID: "person-1",
		ToolName:          "message_send",
		ToolInput:         json.RawMessage(`{ "recipientHint": "person-2", "message": "내일 10시 어때요?" }`),
	}
}

func TestAnApprovedHoldIsRedeemedByExactlyTheCallItApproved(t *testing.T) {
	fixture := aHeldMessage(t, holdrecord.DecisionApprove)

	if errorValue := RedeemHeldCall(fixture.taskRunService, fixture.approvedCall()); errorValue != nil {
		t.Fatalf("the approved call was refused: %v", errorValue)
	}
	if errorValue := RedeemHeldCall(fixture.taskRunService, fixture.approvedCall()); !errors.Is(errorValue, ErrHoldWasRedeemedAlready) {
		t.Fatalf("a second redemption of one approval answered %v", errorValue)
	}
}

func TestAHoldIsNotRedeemedForAnythingButTheCallItsRequesterApproved(t *testing.T) {
	for _, testCase := range []struct {
		name     string
		decision string
		change   func(*HeldCallRedemption)
		expected error
	}{
		{"an invented hold id", holdrecord.DecisionApprove, func(call *HeldCallRedemption) { call.HoldID = "held-test" }, ErrHoldIsNotInThisRun},
		{"no hold id", holdrecord.DecisionApprove, func(call *HeldCallRedemption) { call.HoldID = "" }, ErrHoldIsNotInThisRun},
		{"a run that does not exist", holdrecord.DecisionApprove, func(call *HeldCallRedemption) { call.TaskRunID = "run-missing" }, ErrHoldBelongsToNoRun},
		{"another person's run", holdrecord.DecisionApprove, func(call *HeldCallRedemption) { call.RequesterPersonID = "person-2" }, ErrHoldIsAnotherPersons},
		{"a hold still waiting", "", func(*HeldCallRedemption) {}, ErrHoldWasNotApproved},
		{"a rejected hold", holdrecord.DecisionReject, func(*HeldCallRedemption) {}, ErrHoldWasNotApproved},
		{"a different message", holdrecord.DecisionApprove, func(call *HeldCallRedemption) {
			call.ToolInput = json.RawMessage(`{"recipientHint":"person-2","message":"회의 취소합니다"}`)
		}, ErrHoldApprovedAnotherCall},
		{"a different recipient", holdrecord.DecisionApprove, func(call *HeldCallRedemption) {
			call.ToolInput = json.RawMessage(`{"recipientHint":"person-3","message":"내일 10시 어때요?"}`)
		}, ErrHoldApprovedAnotherCall},
		{"a different tool", holdrecord.DecisionApprove, func(call *HeldCallRedemption) { call.ToolName = "mail_message_send" }, ErrHoldApprovedAnotherCall},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			fixture := aHeldMessage(t, testCase.decision)
			call := fixture.approvedCall()
			testCase.change(&call)

			if errorValue := RedeemHeldCall(fixture.taskRunService, call); !errors.Is(errorValue, testCase.expected) {
				t.Fatalf("expected %v, got %v", testCase.expected, errorValue)
			}
			if errorValue := RedeemHeldCall(fixture.taskRunService, fixture.approvedCall()); testCase.decision == holdrecord.DecisionApprove && errorValue != nil {
				t.Fatalf("a refused redemption spent the approval anyway: %v", errorValue)
			}
		})
	}
}

func TestOneApprovalCarriedOutByTwoCallsAtOnceRedeemsOnce(t *testing.T) {
	fixture := aHeldMessage(t, holdrecord.DecisionApprove)
	results := make(chan error, 8)
	var started sync.WaitGroup
	for range 8 {
		started.Add(1)
		go func() {
			defer started.Done()
			results <- RedeemHeldCall(fixture.taskRunService, fixture.approvedCall())
		}()
	}
	started.Wait()
	close(results)

	redeemed := 0
	for errorValue := range results {
		if errorValue == nil {
			redeemed++
		}
	}
	if redeemed != 1 {
		t.Fatalf("one approval was carried out %d times", redeemed)
	}
}