mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-27 00:19:08 +00:00
154 lines
4.6 KiB
Go
154 lines
4.6 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/store"
|
|
)
|
|
|
|
type testCronStore struct {
|
|
jobs map[string]*store.CronJob
|
|
updateCnt int
|
|
runCnt int
|
|
lastForce bool
|
|
updateErr error
|
|
runErr error
|
|
}
|
|
|
|
func newTestCronStore(job *store.CronJob) *testCronStore {
|
|
jobs := map[string]*store.CronJob{}
|
|
if job != nil {
|
|
jobs[job.ID] = job
|
|
}
|
|
return &testCronStore{jobs: jobs}
|
|
}
|
|
|
|
func (s *testCronStore) AddJob(context.Context, string, store.CronSchedule, string, bool, string, string, string, string) (*store.CronJob, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (s *testCronStore) GetJob(_ context.Context, jobID string) (*store.CronJob, bool) {
|
|
job, ok := s.jobs[jobID]
|
|
return job, ok
|
|
}
|
|
|
|
func (s *testCronStore) ListJobs(context.Context, bool, string, string) []store.CronJob {
|
|
var jobs []store.CronJob
|
|
for _, job := range s.jobs {
|
|
jobs = append(jobs, *job)
|
|
}
|
|
return jobs
|
|
}
|
|
|
|
func (s *testCronStore) RemoveJob(context.Context, string) error { return nil }
|
|
|
|
func (s *testCronStore) UpdateJob(_ context.Context, jobID string, patch store.CronJobPatch) (*store.CronJob, error) {
|
|
s.updateCnt++
|
|
if s.updateErr != nil {
|
|
return nil, s.updateErr
|
|
}
|
|
job := s.jobs[jobID]
|
|
if patch.Message != "" {
|
|
job.Payload.Message = patch.Message
|
|
}
|
|
return job, nil
|
|
}
|
|
|
|
func (s *testCronStore) EnableJob(context.Context, string, bool) error { return nil }
|
|
func (s *testCronStore) GetRunLog(context.Context, string, int, int) ([]store.CronRunLogEntry, int) {
|
|
return nil, 0
|
|
}
|
|
func (s *testCronStore) Status() map[string]any { return map[string]any{} }
|
|
func (s *testCronStore) Start() error { return nil }
|
|
func (s *testCronStore) Stop() {}
|
|
func (s *testCronStore) SetOnJob(func(*store.CronJob) (*store.CronJobResult, error)) {}
|
|
func (s *testCronStore) SetOnEvent(func(store.CronEvent)) {}
|
|
|
|
func (s *testCronStore) RunJob(_ context.Context, _ string, force bool) (bool, string, error) {
|
|
s.runCnt++
|
|
s.lastForce = force
|
|
return true, "", s.runErr
|
|
}
|
|
|
|
func (s *testCronStore) GetDueJobs(time.Time) []store.CronJob { return nil }
|
|
func (s *testCronStore) SetDefaultTimezone(string) {}
|
|
|
|
func TestCronToolBlocksCredentialBoundUpdateByDifferentUser(t *testing.T) {
|
|
cronStore := newTestCronStore(&store.CronJob{
|
|
ID: "job-1",
|
|
UserID: "group:telegram:-100123",
|
|
Payload: store.CronPayload{
|
|
Message: "old",
|
|
CredentialUserID: "tenant-user-a",
|
|
},
|
|
})
|
|
tool := NewCronTool(cronStore)
|
|
ctx := store.WithUserID(context.Background(), "group:telegram:-100123")
|
|
ctx = store.WithCredentialUserID(ctx, "tenant-user-b")
|
|
|
|
result := tool.Execute(ctx, map[string]any{
|
|
"action": "update",
|
|
"jobId": "job-1",
|
|
"patch": map[string]any{"message": "run gh issue list"},
|
|
})
|
|
|
|
if !result.IsError || !strings.Contains(result.ForLLM, "credential context") {
|
|
t.Fatalf("expected credential context error, got %#v", result)
|
|
}
|
|
if cronStore.updateCnt != 0 {
|
|
t.Fatalf("UpdateJob called %d times, want 0", cronStore.updateCnt)
|
|
}
|
|
}
|
|
|
|
func TestCronToolListRedactsCredentialUserID(t *testing.T) {
|
|
cronStore := newTestCronStore(&store.CronJob{
|
|
ID: "job-1",
|
|
UserID: "group:telegram:-100123",
|
|
Payload: store.CronPayload{
|
|
Message: "run gh issue list",
|
|
CredentialUserID: "tenant-user-a",
|
|
},
|
|
})
|
|
tool := NewCronTool(cronStore)
|
|
ctx := store.WithUserID(context.Background(), "group:telegram:-100123")
|
|
|
|
result := tool.Execute(ctx, map[string]any{"action": "list", "includeDisabled": true})
|
|
|
|
if result.IsError {
|
|
t.Fatalf("list returned error: %#v", result)
|
|
}
|
|
if strings.Contains(result.ForLLM, "tenant-user-a") || strings.Contains(result.ForLLM, "credentialUserId") {
|
|
t.Fatalf("credential identity leaked in list response: %s", result.ForLLM)
|
|
}
|
|
}
|
|
|
|
func TestCronToolBlocksCredentialBoundRunByDifferentUser(t *testing.T) {
|
|
cronStore := newTestCronStore(&store.CronJob{
|
|
ID: "job-1",
|
|
UserID: "group:telegram:-100123",
|
|
Payload: store.CronPayload{
|
|
Message: "run gh issue list",
|
|
CredentialUserID: "tenant-user-a",
|
|
},
|
|
})
|
|
tool := NewCronTool(cronStore)
|
|
ctx := store.WithUserID(context.Background(), "group:telegram:-100123")
|
|
ctx = store.WithCredentialUserID(ctx, "tenant-user-b")
|
|
|
|
result := tool.Execute(ctx, map[string]any{
|
|
"action": "run",
|
|
"jobId": "job-1",
|
|
"runMode": "force",
|
|
})
|
|
|
|
if !result.IsError || !strings.Contains(result.ForLLM, "credential context") {
|
|
t.Fatalf("expected credential context error, got %#v", result)
|
|
}
|
|
if cronStore.runCnt != 0 {
|
|
t.Fatalf("RunJob called %d times, want 0", cronStore.runCnt)
|
|
}
|
|
}
|