Ctrl K
ComunidadGuías…

internal/adminapi/tool_inventory_handler_test.go

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

Archivo original ↗Preguntar a KiBot
package adminapi

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"slices"
	"testing"

	"github.com/yeomyeonggeori/blueclaw/internal/agentruntime"
	"github.com/yeomyeonggeori/blueclaw/internal/capability"
	"github.com/yeomyeonggeori/blueprotocol/toolcontract"
)

func validTaskAddDescriptor() agentruntime.CapabilityToolDescriptor {
	return agentruntime.CapabilityToolDescriptor{
		Name:              "task_add",
		CanonicalName:     "task_add",
		Namespace:         "task",
		ModelName:         "task_add",
		ModelVisibility:   toolcontract.ToolVisibilityModel,
		Description:       "Create a task.",
		PrivacyClass:      "workspace_task",
		InputSchema:       json.RawMessage(`{"type":"object","properties":{"title":{"type":"string"}},"required":["title"],"additionalProperties":false}`),
		InputIntentSchema: json.RawMessage(`{"type":"object","properties":{"title":{"type":"string"}},"additionalProperties":false}`),
		OutputSchema:      json.RawMessage(`{"type":"object","properties":{"result":{}},"additionalProperties":false}`),
		ResultContract: &agentruntime.CapabilityToolResultContract{
			Schema: json.RawMessage(`{"type":"object","properties":{"taskID":{"type":"string"}},"required":["taskID"],"additionalProperties":false}`),
		},
		PolicyResource:  "tool:task_add",
		SideEffectClass: toolcontract.ToolSideEffectWorkspaceWrite,
		Availability:    agentruntime.CapabilityAvailability{State: "ok"},
	}
}

func companyInfoGetDescriptorWithOutputSchema(outputSchema string) agentruntime.CapabilityToolDescriptor {
	return agentruntime.CapabilityToolDescriptor{
		Name:              "company_info_get",
		CanonicalName:     "company_info_get",
		Namespace:         "company",
		ModelName:         "company_info_get",
		ModelVisibility:   toolcontract.ToolVisibilityModel,
		Description:       "Read the company's legal attributes.",
		PrivacyClass:      "company_profile",
		InputSchema:       json.RawMessage(`{"type":"object","properties":{},"additionalProperties":false}`),
		InputIntentSchema: json.RawMessage(`{"type":"object","properties":{},"additionalProperties":false}`),
		OutputSchema:      json.RawMessage(outputSchema),
		ResultContract: &agentruntime.CapabilityToolResultContract{
			Schema: json.RawMessage(outputSchema),
		},
		PolicyResource:  "tool:company_info_get",
		SideEffectClass: toolcontract.ToolSideEffectRead,
		Availability:    agentruntime.CapabilityAvailability{State: "ok"},
	}
}

func TestHandleListToolsNamesAQuarantinedProviderAndDropsItsTools(t *testing.T) {
	openOutputSchema := `{"type":"object","properties":{"legalAttributes":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":false}`
	toolCatalogBuilder := agentruntime.NewToolCatalogBuilder()
	toolCatalogBuilder.UseCapabilityToolDescriptors(capability.Client{}, []agentruntime.CapabilityToolDescriptor{
		validTaskAddDescriptor(),
		companyInfoGetDescriptorWithOutputSchema(openOutputSchema),
	})
	handler := ToolInventoryHandler{ToolCatalogBuilder: toolCatalogBuilder}

	responseRecorder := httptest.NewRecorder()
	handler.HandleListTools(responseRecorder, httptest.NewRequest(http.MethodGet, "/admin/api/tools", nil))

	if responseRecorder.Code != http.StatusOK {
		t.Fatalf("expected 200, got %d: %s", responseRecorder.Code, responseRecorder.Body.String())
	}
	var document ToolInventoryDocument
	if errorValue := json.Unmarshal(responseRecorder.Body.Bytes(), &document); errorValue != nil {
		t.Fatal(errorValue)
	}

	if slices.Contains(document.Tools, "task_add") {
		t.Fatalf("expected task_add absent, the open company_info_get schema quarantines the whole capabilityd provider, got %+v", document.Tools)
	}
	if slices.Contains(document.Tools, "company_info_get") {
		t.Fatalf("expected company_info_get absent for its own open schema, got %+v", document.Tools)
	}

	if len(document.QuarantinedProviders) != 1 {
		t.Fatalf("expected exactly one quarantined provider, got %+v", document.QuarantinedProviders)
	}
	quarantinedProvider := document.QuarantinedProviders[0]
	if quarantinedProvider.ProviderID != "capabilityd" {
		t.Fatalf("expected the capabilityd provider named, got %q", quarantinedProvider.ProviderID)
	}
	if quarantinedProvider.Reason == "" {
		t.Fatal("expected a reason naming what bluecollar refused")
	}
}

func TestHandleListToolsRegistersAClosedCatalog(t *testing.T) {
	closedOutputSchema := `{"type":"object","properties":{"legalAttributes":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"],"additionalProperties":false}}},"additionalProperties":false}`
	toolCatalogBuilder := agentruntime.NewToolCatalogBuilder()
	toolCatalogBuilder.UseCapabilityToolDescriptors(capability.Client{}, []agentruntime.CapabilityToolDescriptor{
		validTaskAddDescriptor(),
		companyInfoGetDescriptorWithOutputSchema(closedOutputSchema),
	})
	handler := ToolInventoryHandler{ToolCatalogBuilder: toolCatalogBuilder}

	responseRecorder := httptest.NewRecorder()
	handler.HandleListTools(responseRecorder, httptest.NewRequest(http.MethodGet, "/admin/api/tools", nil))

	var document ToolInventoryDocument
	if errorValue := json.Unmarshal(responseRecorder.Body.Bytes(), &document); errorValue != nil {
		t.Fatal(errorValue)
	}
	if !slices.Contains(document.Tools, "task_add") {
		t.Fatalf("expected task_add registered, got %+v", document.Tools)
	}
	if !slices.Contains(document.Tools, "company_info_get") {
		t.Fatalf("expected company_info_get registered, got %+v", document.Tools)
	}
	if len(document.QuarantinedProviders) != 0 {
		t.Fatalf("expected no quarantined provider, got %+v", document.QuarantinedProviders)
	}
}