Files
hanzoai_agent/control-plane/internal/services/vc_service_test.go
zeekay 63bde13037 fix: replace invalid hyphenated Go identifiers with camelCase
The bulk rename from agentfield to hanzo-agents introduced hyphens into
Go variable names (e.g. hanzo-agentsHome), which are not valid Go
identifiers. Fixed 361 occurrences across 36 files to use camelCase
(e.g. hanzoAgentsHome). String literals like ".hanzo-agents" are
preserved as-is.
2026-02-13 12:43:38 -08:00

1148 lines
34 KiB
Go

package services
import (
"context"
"encoding/json"
"path/filepath"
"testing"
"time"
"github.com/hanzoai/agents/control-plane/internal/config"
"github.com/hanzoai/agents/control-plane/internal/storage"
"github.com/hanzoai/agents/control-plane/pkg/types"
"github.com/stretchr/testify/require"
)
func setupVCTestEnvironment(t *testing.T) (*VCService, *DIDService, storage.StorageProvider, context.Context) {
t.Helper()
provider, ctx := setupTestStorage(t)
registry := NewDIDRegistryWithStorage(provider)
require.NoError(t, registry.Initialize())
keystoreDir := filepath.Join(t.TempDir(), "keys")
ks, err := NewKeystoreService(&config.KeystoreConfig{Path: keystoreDir, Type: "local"})
require.NoError(t, err)
didCfg := &config.DIDConfig{
Enabled: true,
Keystore: config.KeystoreConfig{Path: keystoreDir, Type: "local"},
VCRequirements: config.VCRequirements{
RequireVCForExecution: true,
PersistExecutionVC: true,
HashSensitiveData: true,
},
}
didService := NewDIDService(didCfg, ks, registry)
hanzoAgentsID := "hanzo-agents-vc-test"
require.NoError(t, didService.Initialize(hanzoAgentsID))
vcService := NewVCService(didCfg, didService, provider)
require.NoError(t, vcService.Initialize())
return vcService, didService, provider, ctx
}
func TestVCService_IsExecutionVCEnabled(t *testing.T) {
tests := []struct {
name string
config *config.DIDConfig
expected bool
}{
{
name: "nil service",
config: nil,
expected: false,
},
{
name: "disabled DID system",
config: &config.DIDConfig{
Enabled: false,
VCRequirements: config.VCRequirements{
RequireVCForExecution: true,
},
},
expected: false,
},
{
name: "enabled but VC not required",
config: &config.DIDConfig{
Enabled: true,
VCRequirements: config.VCRequirements{
RequireVCForExecution: false,
},
},
expected: false,
},
{
name: "enabled and VC required",
config: &config.DIDConfig{
Enabled: true,
VCRequirements: config.VCRequirements{
RequireVCForExecution: true,
},
},
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var svc *VCService
if tt.config != nil {
svc = &VCService{config: tt.config}
}
result := svc.IsExecutionVCEnabled()
require.Equal(t, tt.expected, result)
})
}
}
func TestVCService_ShouldPersistExecutionVC(t *testing.T) {
tests := []struct {
name string
config *config.DIDConfig
expected bool
}{
{
name: "nil service",
config: nil,
expected: false,
},
{
name: "disabled DID system",
config: &config.DIDConfig{
Enabled: false,
VCRequirements: config.VCRequirements{
PersistExecutionVC: true,
},
},
expected: false,
},
{
name: "enabled but persistence disabled",
config: &config.DIDConfig{
Enabled: true,
VCRequirements: config.VCRequirements{
PersistExecutionVC: false,
},
},
expected: false,
},
{
name: "enabled and persistence enabled",
config: &config.DIDConfig{
Enabled: true,
VCRequirements: config.VCRequirements{
PersistExecutionVC: true,
},
},
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var svc *VCService
if tt.config != nil {
svc = &VCService{config: tt.config}
}
result := svc.ShouldPersistExecutionVC()
require.Equal(t, tt.expected, result)
})
}
}
func TestVCService_GenerateExecutionVC_Success(t *testing.T) {
vcService, didService, provider, ctx := setupVCTestEnvironment(t)
// Register an agent first
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-vc-test",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
Skills: []types.SkillDefinition{{ID: "skill1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
targetDID := regResp.IdentityPackage.SkillDIDs["skill1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-1",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: targetDID,
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
inputData := []byte(`{"input": "test"}`)
outputData := []byte(`{"output": "result"}`)
status := "succeeded"
vc, err := vcService.GenerateExecutionVC(execCtx, inputData, outputData, status, nil, 100)
require.NoError(t, err)
require.NotNil(t, vc)
require.NotEmpty(t, vc.VCID)
require.Equal(t, execCtx.ExecutionID, vc.ExecutionID)
require.Equal(t, execCtx.WorkflowID, vc.WorkflowID)
require.Equal(t, execCtx.SessionID, vc.SessionID)
require.Equal(t, callerDID, vc.CallerDID)
require.Equal(t, targetDID, vc.TargetDID)
require.NotEmpty(t, vc.Signature)
require.NotEmpty(t, vc.VCDocument)
require.NotEmpty(t, vc.InputHash)
require.NotEmpty(t, vc.OutputHash)
require.Equal(t, "succeeded", vc.Status)
require.Greater(t, vc.DocumentSize, int64(0))
// Verify VC document structure
var vcDoc types.VCDocument
err = json.Unmarshal(vc.VCDocument, &vcDoc)
require.NoError(t, err)
require.NotEmpty(t, vcDoc.Context)
require.NotEmpty(t, vcDoc.Type)
require.NotEmpty(t, vcDoc.ID)
require.Equal(t, callerDID, vcDoc.Issuer)
require.NotEmpty(t, vcDoc.IssuanceDate)
require.NotEmpty(t, vcDoc.Proof.ProofValue)
require.Equal(t, "Ed25519Signature2020", vcDoc.Proof.Type)
// Verify VC was stored
storedVC, err := provider.GetExecutionVC(ctx, vc.VCID)
require.NoError(t, err)
require.NotNil(t, storedVC)
require.Equal(t, vc.VCID, storedVC.VCID)
}
func TestVCService_GenerateExecutionVC_WithError(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-vc-error",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-error",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
errorMsg := "test error message"
vc, err := vcService.GenerateExecutionVC(execCtx, []byte(`{}`), nil, "failed", &errorMsg, 50)
require.NoError(t, err)
require.NotNil(t, vc)
require.Equal(t, "failed", vc.Status)
// Verify error message is in VC document
var vcDoc types.VCDocument
err = json.Unmarshal(vc.VCDocument, &vcDoc)
require.NoError(t, err)
require.Equal(t, errorMsg, vcDoc.CredentialSubject.Execution.ErrorMessage)
}
func TestVCService_GenerateExecutionVC_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
VCRequirements: config.VCRequirements{
RequireVCForExecution: true,
},
}
vcService := NewVCService(cfg, nil, provider)
execCtx := &types.ExecutionContext{
ExecutionID: "exec-1",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: "did:key:test",
Timestamp: time.Now(),
}
_, err := vcService.GenerateExecutionVC(execCtx, []byte(`{}`), []byte(`{}`), "succeeded", nil, 100)
require.Error(t, err)
require.Contains(t, err.Error(), "DID system is disabled")
_ = ctx
}
func TestVCService_GenerateExecutionVC_VCNotRequired(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: true,
VCRequirements: config.VCRequirements{
RequireVCForExecution: false,
},
}
vcService := NewVCService(cfg, nil, provider)
execCtx := &types.ExecutionContext{
ExecutionID: "exec-1",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: "did:key:test",
Timestamp: time.Now(),
}
vc, err := vcService.GenerateExecutionVC(execCtx, []byte(`{}`), []byte(`{}`), "succeeded", nil, 100)
require.NoError(t, err)
require.Nil(t, vc) // Should return nil when VC generation is disabled by config
_ = ctx
}
func TestVCService_GenerateExecutionVC_EmptyData(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-empty",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-empty",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
// Test with nil input/output
vc, err := vcService.GenerateExecutionVC(execCtx, nil, nil, "succeeded", nil, 0)
require.NoError(t, err)
require.NotNil(t, vc)
// Test with empty slices
vc2, err := vcService.GenerateExecutionVC(execCtx, []byte{}, []byte{}, "succeeded", nil, 0)
require.NoError(t, err)
require.NotNil(t, vc2)
}
func TestVCService_GenerateExecutionVC_LongErrorMessage(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-long-error",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-long-error",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
// Create error message longer than 500 characters
longError := make([]byte, 600)
for i := range longError {
longError[i] = 'a'
}
errorMsg := string(longError)
vc, err := vcService.GenerateExecutionVC(execCtx, []byte(`{}`), nil, "failed", &errorMsg, 50)
require.NoError(t, err)
require.NotNil(t, vc)
// Verify error message was truncated
var vcDoc types.VCDocument
err = json.Unmarshal(vc.VCDocument, &vcDoc)
require.NoError(t, err)
require.LessOrEqual(t, len(vcDoc.CredentialSubject.Execution.ErrorMessage), 500+len("...[truncated]"))
require.Contains(t, vcDoc.CredentialSubject.Execution.ErrorMessage, "...[truncated]")
}
func TestVCService_VerifyVC_Success(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent and generate a VC
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-verify",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-verify",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc, err := vcService.GenerateExecutionVC(execCtx, []byte(`{"input": "test"}`), []byte(`{"output": "result"}`), "succeeded", nil, 100)
require.NoError(t, err)
// Verify the VC
verifyResp, err := vcService.VerifyVC(vc.VCDocument)
require.NoError(t, err)
require.NotNil(t, verifyResp)
require.True(t, verifyResp.Valid)
require.Equal(t, callerDID, verifyResp.IssuerDID)
require.NotEmpty(t, verifyResp.IssuedAt)
require.Contains(t, verifyResp.Message, "verified successfully")
}
func TestVCService_VerifyVC_InvalidDocument(t *testing.T) {
vcService, _, _, _ := setupVCTestEnvironment(t)
invalidDoc := json.RawMessage(`{"invalid": "json"}`)
verifyResp, err := vcService.VerifyVC(invalidDoc)
require.NoError(t, err)
require.NotNil(t, verifyResp)
require.False(t, verifyResp.Valid)
require.Contains(t, verifyResp.Error, "failed to parse VC document")
}
func TestVCService_VerifyVC_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
}
vcService := NewVCService(cfg, nil, provider)
doc := json.RawMessage(`{"@context": ["https://www.w3.org/2018/credentials/v1"]}`)
verifyResp, err := vcService.VerifyVC(doc)
require.NoError(t, err)
require.NotNil(t, verifyResp)
require.False(t, verifyResp.Valid)
require.Contains(t, verifyResp.Error, "DID system is disabled")
_ = ctx
}
func TestVCService_VerifyVC_TamperedSignature(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent and generate a VC
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-tamper",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-tamper",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc, err := vcService.GenerateExecutionVC(execCtx, []byte(`{"input": "test"}`), []byte(`{"output": "result"}`), "succeeded", nil, 100)
require.NoError(t, err)
// Tamper with the signature
var vcDoc types.VCDocument
err = json.Unmarshal(vc.VCDocument, &vcDoc)
require.NoError(t, err)
vcDoc.Proof.ProofValue = "tampered_signature"
tamperedDoc, err := json.Marshal(vcDoc)
require.NoError(t, err)
verifyResp, err := vcService.VerifyVC(tamperedDoc)
require.NoError(t, err)
require.NotNil(t, verifyResp)
require.False(t, verifyResp.Valid)
require.Contains(t, verifyResp.Message, "Invalid signature")
}
func TestVCService_VerifyVC_InvalidIssuerDID(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent and generate a VC
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-invalid-issuer",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-invalid-issuer",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc, err := vcService.GenerateExecutionVC(execCtx, []byte(`{"input": "test"}`), []byte(`{"output": "result"}`), "succeeded", nil, 100)
require.NoError(t, err)
// Change the issuer DID to an invalid one
var vcDoc types.VCDocument
err = json.Unmarshal(vc.VCDocument, &vcDoc)
require.NoError(t, err)
vcDoc.Issuer = "did:key:invalid"
invalidDoc, err := json.Marshal(vcDoc)
require.NoError(t, err)
verifyResp, err := vcService.VerifyVC(invalidDoc)
require.NoError(t, err)
require.NotNil(t, verifyResp)
require.False(t, verifyResp.Valid)
require.Contains(t, verifyResp.Error, "failed to resolve issuer DID")
}
func TestVCService_GetWorkflowVCStatusSummaries(t *testing.T) {
vcService, _, _, _ := setupVCTestEnvironment(t)
// Test with empty workflow IDs
summaries, err := vcService.GetWorkflowVCStatusSummaries([]string{})
require.NoError(t, err)
require.Empty(t, summaries)
// Test with nil workflow IDs
summaries, err = vcService.GetWorkflowVCStatusSummaries(nil)
require.NoError(t, err)
require.Empty(t, summaries)
// Test with workflow IDs (no VCs stored yet)
summaries, err = vcService.GetWorkflowVCStatusSummaries([]string{"workflow-1", "workflow-2"})
require.NoError(t, err)
require.Len(t, summaries, 2)
require.Equal(t, "none", summaries["workflow-1"].VerificationStatus)
require.Equal(t, "none", summaries["workflow-2"].VerificationStatus)
require.False(t, summaries["workflow-1"].HasVCs)
require.False(t, summaries["workflow-2"].HasVCs)
}
func TestVCService_GetWorkflowVCStatusSummaries_WithEmptyIDs(t *testing.T) {
vcService, _, _, _ := setupVCTestEnvironment(t)
// Test with empty string IDs (should be skipped)
summaries, err := vcService.GetWorkflowVCStatusSummaries([]string{"workflow-1", "", "workflow-2", ""})
require.NoError(t, err)
require.Len(t, summaries, 2)
require.Contains(t, summaries, "workflow-1")
require.Contains(t, summaries, "workflow-2")
}
func TestVCService_GetWorkflowVCStatusSummaries_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
}
vcService := NewVCService(cfg, nil, provider)
summaries, err := vcService.GetWorkflowVCStatusSummaries([]string{"workflow-1"})
require.NoError(t, err)
require.Len(t, summaries, 1)
require.Equal(t, "none", summaries["workflow-1"].VerificationStatus)
_ = ctx
}
func TestVCService_GetWorkflowVCChain_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
}
vcService := NewVCService(cfg, nil, provider)
chain, err := vcService.GetWorkflowVCChain("workflow-1")
require.Error(t, err)
require.Nil(t, chain)
require.Contains(t, err.Error(), "DID system is disabled")
_ = ctx
}
func TestVCService_GetWorkflowVCChain_NoVCs(t *testing.T) {
vcService, _, _, _ := setupVCTestEnvironment(t)
chain, err := vcService.GetWorkflowVCChain("workflow-nonexistent")
require.NoError(t, err)
require.NotNil(t, chain)
require.Equal(t, "workflow-nonexistent", chain.WorkflowID)
require.Empty(t, chain.ComponentVCs)
require.Equal(t, 0, chain.TotalSteps)
require.Equal(t, "pending", chain.Status)
}
func TestVCService_CreateWorkflowVC_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
}
vcService := NewVCService(cfg, nil, provider)
workflowVC, err := vcService.CreateWorkflowVC("workflow-1", "session-1", []string{"vc-1", "vc-2"})
require.Error(t, err)
require.Nil(t, workflowVC)
require.Contains(t, err.Error(), "DID system is disabled")
_ = ctx
}
func TestVCService_CreateWorkflowVC_Success(t *testing.T) {
vcService, didService, provider, ctx := setupVCTestEnvironment(t)
// Register an agent first
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-workflow-vc",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
// Generate some execution VCs
execCtx1 := &types.ExecutionContext{
ExecutionID: "exec-1",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc1, err := vcService.GenerateExecutionVC(execCtx1, []byte(`{"input": "1"}`), []byte(`{"output": "1"}`), "succeeded", nil, 100)
require.NoError(t, err)
execCtx2 := &types.ExecutionContext{
ExecutionID: "exec-2",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc2, err := vcService.GenerateExecutionVC(execCtx2, []byte(`{"input": "2"}`), []byte(`{"output": "2"}`), "succeeded", nil, 200)
require.NoError(t, err)
// Create workflow VC
workflowVC, err := vcService.CreateWorkflowVC("workflow-1", "session-1", []string{vc1.VCID, vc2.VCID})
require.NoError(t, err)
require.NotNil(t, workflowVC)
require.Equal(t, "workflow-1", workflowVC.WorkflowID)
require.Equal(t, "session-1", workflowVC.SessionID)
require.Len(t, workflowVC.ComponentVCs, 2)
require.Equal(t, 2, workflowVC.TotalSteps)
require.Equal(t, 2, workflowVC.CompletedSteps)
require.NotEmpty(t, workflowVC.WorkflowVCID)
// Verify workflow VC was stored
storedVC, err := provider.GetWorkflowVC(ctx, "workflow-1")
require.NoError(t, err)
require.NotNil(t, storedVC)
require.Equal(t, workflowVC.WorkflowVCID, storedVC.WorkflowVCID)
}
func TestVCService_QueryExecutionVCs_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
}
vcService := NewVCService(cfg, nil, provider)
filters := &types.VCFilters{
WorkflowID: stringPtr("workflow-1"),
}
vcs, err := vcService.QueryExecutionVCs(filters)
require.Error(t, err)
require.Nil(t, vcs)
require.Contains(t, err.Error(), "DID system is disabled")
_ = ctx
}
func TestVCService_QueryExecutionVCs_Success(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-query",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
// Generate execution VCs
execCtx1 := &types.ExecutionContext{
ExecutionID: "exec-query-1",
WorkflowID: "workflow-query",
SessionID: "session-query",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc1, err := vcService.GenerateExecutionVC(execCtx1, []byte(`{"input": "1"}`), []byte(`{"output": "1"}`), "succeeded", nil, 100)
require.NoError(t, err)
execCtx2 := &types.ExecutionContext{
ExecutionID: "exec-query-2",
WorkflowID: "workflow-query",
SessionID: "session-query",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc2, err := vcService.GenerateExecutionVC(execCtx2, []byte(`{"input": "2"}`), []byte(`{"output": "2"}`), "succeeded", nil, 200)
require.NoError(t, err)
// Query by workflow ID
filters := &types.VCFilters{
WorkflowID: stringPtr("workflow-query"),
}
vcs, err := vcService.QueryExecutionVCs(filters)
require.NoError(t, err)
require.Len(t, vcs, 2)
vcIDs := make(map[string]bool)
for _, vc := range vcs {
vcIDs[vc.VCID] = true
}
require.True(t, vcIDs[vc1.VCID])
require.True(t, vcIDs[vc2.VCID])
}
func TestVCService_GetExecutionVCByExecutionID(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-get-exec",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-get-by-id",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc, err := vcService.GenerateExecutionVC(execCtx, []byte(`{"input": "test"}`), []byte(`{"output": "result"}`), "succeeded", nil, 100)
require.NoError(t, err)
// Get VC by execution ID
retrievedVC, err := vcService.GetExecutionVCByExecutionID("exec-get-by-id")
require.NoError(t, err)
require.NotNil(t, retrievedVC)
require.Equal(t, vc.VCID, retrievedVC.VCID)
require.Equal(t, "exec-get-by-id", retrievedVC.ExecutionID)
}
func TestVCService_GetExecutionVCByExecutionID_NotFound(t *testing.T) {
vcService, _, _, _ := setupVCTestEnvironment(t)
vc, err := vcService.GetExecutionVCByExecutionID("nonexistent-exec-id")
require.Error(t, err)
require.Nil(t, vc)
require.Contains(t, err.Error(), "execution VC not found")
}
func TestVCService_ListWorkflowVCs_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
}
vcService := NewVCService(cfg, nil, provider)
workflowVCs, err := vcService.ListWorkflowVCs()
require.Error(t, err)
require.Nil(t, workflowVCs)
require.Contains(t, err.Error(), "DID system is disabled")
_ = ctx
}
func TestVCService_ListWorkflowVCs_Success(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-list-workflow",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
// Generate execution VC
execCtx := &types.ExecutionContext{
ExecutionID: "exec-list-1",
WorkflowID: "workflow-list",
SessionID: "session-list",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc, err := vcService.GenerateExecutionVC(execCtx, []byte(`{"input": "test"}`), []byte(`{"output": "result"}`), "succeeded", nil, 100)
require.NoError(t, err)
// Create workflow VC
workflowVC, err := vcService.CreateWorkflowVC("workflow-list", "session-list", []string{vc.VCID})
require.NoError(t, err)
// List workflow VCs
workflowVCs, err := vcService.ListWorkflowVCs()
require.NoError(t, err)
require.NotNil(t, workflowVCs)
require.GreaterOrEqual(t, len(workflowVCs), 1)
found := false
for _, wvc := range workflowVCs {
if wvc.WorkflowVCID == workflowVC.WorkflowVCID {
found = true
break
}
}
require.True(t, found)
}
func TestVCService_VerifyExecutionVCComprehensive_Success(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-comprehensive",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
Skills: []types.SkillDefinition{{ID: "skill1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
targetDID := regResp.IdentityPackage.SkillDIDs["skill1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-comprehensive",
WorkflowID: "workflow-comprehensive",
SessionID: "session-comprehensive",
CallerDID: callerDID,
TargetDID: targetDID,
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
_, err = vcService.GenerateExecutionVC(execCtx, []byte(`{"input": "test"}`), []byte(`{"output": "result"}`), "succeeded", nil, 100)
require.NoError(t, err)
// Perform comprehensive verification
result, err := vcService.VerifyExecutionVCComprehensive("exec-comprehensive")
require.NoError(t, err)
require.NotNil(t, result)
require.True(t, result.Valid)
require.Greater(t, result.OverallScore, 0.0)
require.Empty(t, result.CriticalIssues)
require.True(t, result.IntegrityChecks.MetadataConsistency)
require.True(t, result.IntegrityChecks.FieldConsistency)
require.True(t, result.IntegrityChecks.HashValidation)
require.True(t, result.IntegrityChecks.StructuralIntegrity)
require.True(t, result.SecurityAnalysis.KeyValidation)
require.True(t, result.SecurityAnalysis.DIDAuthenticity)
require.True(t, result.ComplianceChecks.W3CCompliance)
require.True(t, result.ComplianceChecks.HanzoAgentsStandardCompliance)
}
func TestVCService_VerifyExecutionVCComprehensive_NotFound(t *testing.T) {
vcService, _, _, _ := setupVCTestEnvironment(t)
result, err := vcService.VerifyExecutionVCComprehensive("nonexistent-exec-id")
require.NoError(t, err)
require.NotNil(t, result)
require.False(t, result.Valid)
require.Equal(t, 0.0, result.OverallScore)
require.Len(t, result.CriticalIssues, 1)
require.Equal(t, "vc_not_found", result.CriticalIssues[0].Type)
}
func TestVCService_VerifyExecutionVCComprehensive_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
}
vcService := NewVCService(cfg, nil, provider)
result, err := vcService.VerifyExecutionVCComprehensive("exec-1")
require.NoError(t, err)
require.NotNil(t, result)
require.False(t, result.Valid)
require.Equal(t, 0.0, result.OverallScore)
require.Len(t, result.CriticalIssues, 1)
require.Equal(t, "system_disabled", result.CriticalIssues[0].Type)
_ = ctx
}
func TestVCService_VerifyExecutionVCComprehensive_IssuerMismatch(t *testing.T) {
vcService, didService, _, ctx := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-mismatch",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
execCtx := &types.ExecutionContext{
ExecutionID: "exec-mismatch",
WorkflowID: "workflow-1",
SessionID: "session-1",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
_, err = vcService.GenerateExecutionVC(execCtx, []byte(`{"input": "test"}`), []byte(`{"output": "result"}`), "succeeded", nil, 100)
require.NoError(t, err)
// Get the VC from storage to tamper with it
storedVC, err := vcService.vcStorage.GetExecutionVCByExecutionID("exec-mismatch")
require.NoError(t, err)
require.NotNil(t, storedVC)
// Tamper with issuer DID - create a new VC with tampered issuer
tamperedVC := *storedVC
tamperedVC.IssuerDID = "did:key:tampered"
// Store tampered VC using vcStorage
err = vcService.vcStorage.StoreExecutionVC(ctx, &tamperedVC)
require.NoError(t, err)
// Verify - should detect issuer mismatch
result, err := vcService.VerifyExecutionVCComprehensive("exec-mismatch")
require.NoError(t, err)
require.NotNil(t, result)
require.False(t, result.Valid)
require.Greater(t, len(result.CriticalIssues), 0)
// Find issuer mismatch issue
found := false
for _, issue := range result.CriticalIssues {
if issue.Type == "issuer_mismatch" {
found = true
break
}
}
require.True(t, found)
}
func TestVCService_VerifyWorkflowVCComprehensive_DisabledSystem(t *testing.T) {
provider, ctx := setupTestStorage(t)
cfg := &config.DIDConfig{
Enabled: false,
}
vcService := NewVCService(cfg, nil, provider)
result, err := vcService.VerifyWorkflowVCComprehensive("workflow-1")
require.NoError(t, err)
require.NotNil(t, result)
require.False(t, result.Valid)
require.Equal(t, 0.0, result.OverallScore)
require.Len(t, result.CriticalIssues, 1)
require.Equal(t, "system_disabled", result.CriticalIssues[0].Type)
_ = ctx
}
func TestVCService_VerifyWorkflowVCComprehensive_NoVCs(t *testing.T) {
vcService, _, _, _ := setupVCTestEnvironment(t)
result, err := vcService.VerifyWorkflowVCComprehensive("workflow-nonexistent")
require.NoError(t, err)
require.NotNil(t, result)
// Should handle gracefully when workflow has no VCs
}
func TestVCService_DetermineWorkflowStatus(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-status",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
// Create VCs with different statuses
execCtx1 := &types.ExecutionContext{
ExecutionID: "exec-status-1",
WorkflowID: "workflow-status",
SessionID: "session-status",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc1, err := vcService.GenerateExecutionVC(execCtx1, []byte(`{"input": "1"}`), []byte(`{"output": "1"}`), "succeeded", nil, 100)
require.NoError(t, err)
execCtx2 := &types.ExecutionContext{
ExecutionID: "exec-status-2",
WorkflowID: "workflow-status",
SessionID: "session-status",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
vc2, err := vcService.GenerateExecutionVC(execCtx2, []byte(`{"input": "2"}`), nil, "failed", stringPtr("error"), 50)
require.NoError(t, err)
// Get workflow VC chain to test status determination
chain, err := vcService.GetWorkflowVCChain("workflow-status")
require.NoError(t, err)
require.NotNil(t, chain)
require.Equal(t, "failed", chain.Status) // Should be failed because one VC failed
require.Len(t, chain.ComponentVCs, 2)
// Verify both VCs are in the chain
vcIDs := make(map[string]bool)
for _, vc := range chain.ComponentVCs {
vcIDs[vc.VCID] = true
}
require.True(t, vcIDs[vc1.VCID])
require.True(t, vcIDs[vc2.VCID])
}
func TestVCService_DetermineWorkflowStatus_AllSucceeded(t *testing.T) {
vcService, didService, _, _ := setupVCTestEnvironment(t)
// Register an agent
req := &types.DIDRegistrationRequest{
AgentNodeID: "agent-status-all-success",
Reasoners: []types.ReasonerDefinition{{ID: "reasoner1"}},
}
regResp, err := didService.RegisterAgent(req)
require.NoError(t, err)
require.True(t, regResp.Success)
callerDID := regResp.IdentityPackage.ReasonerDIDs["reasoner1"].DID
// Create multiple succeeded VCs
for i := 1; i <= 3; i++ {
execCtx := &types.ExecutionContext{
ExecutionID: "exec-success-" + string(rune('0'+i)),
WorkflowID: "workflow-all-success",
SessionID: "session-success",
CallerDID: callerDID,
TargetDID: "",
AgentNodeDID: regResp.IdentityPackage.AgentDID.DID,
Timestamp: time.Now(),
}
_, err := vcService.GenerateExecutionVC(execCtx, []byte(`{"input": "test"}`), []byte(`{"output": "result"}`), "succeeded", nil, 100)
require.NoError(t, err)
}
// Get workflow VC chain
chain, err := vcService.GetWorkflowVCChain("workflow-all-success")
require.NoError(t, err)
require.NotNil(t, chain)
require.Equal(t, "succeeded", chain.Status)
require.Len(t, chain.ComponentVCs, 3)
}
// Helper function
func stringPtr(s string) *string {
return &s
}