初始化仓库

This commit is contained in:
2026-06-02 23:14:41 +08:00
commit 0bc3f02670
520 changed files with 191097 additions and 0 deletions
+228
View File
@@ -0,0 +1,228 @@
package fake
import (
"fmt"
"strings"
"time"
"github.com/chenhg5/cc-connect/core"
)
// TestMessage creates a basic test message with sensible defaults.
func TestMessage() *core.Message {
return &core.Message{
SessionKey: "test:channel:user",
Platform: "test",
MessageID: "msg-test-001",
UserID: "user-test",
UserName: "Test User",
ChatName: "Test Channel",
Content: "Hello, world",
ReplyCtx: nil,
}
}
// TestMessageWithContent creates a message with specific content.
func TestMessageWithContent(content string) *core.Message {
msg := TestMessage()
msg.Content = content
return msg
}
// TestMessageWithSession creates a message with a specific session key.
func TestMessageWithSession(sessionKey string) *core.Message {
msg := TestMessage()
msg.SessionKey = sessionKey
return msg
}
// TestMessageWithImages creates a message with images.
func TestMessageWithImages(images []core.ImageAttachment) *core.Message {
msg := TestMessage()
msg.Images = images
return msg
}
// TestMessageWithFiles creates a message with files.
func TestMessageWithFiles(files []core.FileAttachment) *core.Message {
msg := TestMessage()
msg.Files = files
return msg
}
// TestMessageWithAudio creates a message with audio.
func TestMessageWithAudio(audio *core.AudioAttachment) *core.Message {
msg := TestMessage()
msg.Audio = audio
return msg
}
// TestMessageFromVoice creates a message that originated from voice.
func TestMessageFromVoice(content string) *core.Message {
msg := TestMessageWithContent(content)
msg.FromVoice = true
return msg
}
// TestLongMessage creates a message with a very long content for truncation testing.
func TestLongMessage(length int) *core.Message {
return &core.Message{
SessionKey: "test:channel:user",
Platform: "test",
MessageID: "msg-test-long",
UserID: "user-test",
Content: strings.Repeat("x", length),
}
}
// TestSpecialCharsMessage creates a message with special characters for security testing.
func TestSpecialCharsMessage() *core.Message {
return &core.Message{
SessionKey: "test:channel:user",
Platform: "test",
MessageID: "msg-test-special",
UserID: "user-test",
Content: "Hello! 🎉 <script>alert('xss')</script> & \"quotes\"",
}
}
// TestImageAttachment creates a test image attachment.
func TestImageAttachment(mimeType, filename string, data []byte) core.ImageAttachment {
return core.ImageAttachment{
MimeType: mimeType,
Data: data,
FileName: filename,
}
}
// TestFileAttachment creates a test file attachment.
func TestFileAttachment(mimeType, filename string, data []byte) core.FileAttachment {
return core.FileAttachment{
MimeType: mimeType,
Data: data,
FileName: filename,
}
}
// TestAudioAttachment creates a test audio attachment.
func TestAudioAttachment(mimeType, format string, data []byte, duration int) *core.AudioAttachment {
return &core.AudioAttachment{
MimeType: mimeType,
Data: data,
Format: format,
Duration: duration,
}
}
// TestEvent creates a test event.
func TestEvent(eventType core.EventType, content string) core.Event {
return core.Event{
Type: eventType,
Content: content,
Done: eventType == core.EventResult || eventType == core.EventError,
}
}
// TestTextEvent creates a text event.
func TestTextEvent(content string) core.Event {
return TestEvent(core.EventText, content)
}
// TestResultEvent creates a result event.
func TestResultEvent(content string) core.Event {
return TestEvent(core.EventResult, content)
}
// TestErrorEvent creates an error event.
func TestErrorEvent(err error) core.Event {
return core.Event{
Type: core.EventError,
Done: true,
Error: err,
}
}
// TestPermissionRequestEvent creates a permission request event.
func TestPermissionRequestEvent(requestID, toolName, toolInput string) core.Event {
return core.Event{
Type: core.EventPermissionRequest,
ToolName: toolName,
ToolInput: toolInput,
RequestID: requestID,
}
}
// TestToolUseEvent creates a tool use event.
func TestToolUseEvent(toolName, toolInput string) core.Event {
return core.Event{
Type: core.EventToolUse,
ToolName: toolName,
ToolInput: toolInput,
}
}
// TestThinkingEvent creates a thinking event.
func TestThinkingEvent(content string) core.Event {
return TestEvent(core.EventThinking, content)
}
// TestHistoryEntry creates a test history entry.
func TestHistoryEntry(role, content string) core.HistoryEntry {
return core.HistoryEntry{
Role: role,
Content: content,
Timestamp: time.Now(),
}
}
// TestAgentSessionInfo creates a test agent session info.
func TestAgentSessionInfo(id, summary string, messageCount int) core.AgentSessionInfo {
return core.AgentSessionInfo{
ID: id,
Summary: summary,
MessageCount: messageCount,
ModifiedAt: time.Now(),
}
}
// TestPermissionResult creates a test permission result.
func TestPermissionResultAllow() core.PermissionResult {
return core.PermissionResult{
Behavior: "allow",
}
}
func TestPermissionResultDeny(message string) core.PermissionResult {
return core.PermissionResult{
Behavior: "deny",
Message: message,
}
}
// TestProviderConfig creates a test provider config.
func TestProviderConfig(name, apiKey, baseURL, model string) core.ProviderConfig {
return core.ProviderConfig{
Name: name,
APIKey: apiKey,
BaseURL: baseURL,
Model: model,
}
}
// TestModelOption creates a test model option.
func TestModelOption(name, desc, alias string) core.ModelOption {
return core.ModelOption{
Name: name,
Desc: desc,
Alias: alias,
}
}
// BatchTestMessages creates multiple test messages for batch testing.
func BatchTestMessages(count int) []*core.Message {
messages := make([]*core.Message, count)
for i := 0; i < count; i++ {
messages[i] = TestMessageWithContent(fmt.Sprintf("Test message %d", i))
}
return messages
}
+151
View File
@@ -0,0 +1,151 @@
package fake
import (
"context"
"fmt"
"sync"
"time"
"github.com/chenhg5/cc-connect/core"
)
// TestUsageReport creates a test usage report.
func TestUsageReport(provider, accountID, email string) *core.UsageReport {
return &core.UsageReport{
Provider: provider,
AccountID: accountID,
UserID: "test-user",
Email: email,
Plan: "pro",
Buckets: []core.UsageBucket{
{
Name: "Standard Requests",
Allowed: true,
LimitReached: false,
Windows: []core.UsageWindow{
{UsedPercent: 45, WindowSeconds: 3600},
},
},
},
Credits: &core.UsageCredits{
HasCredits: true,
Unlimited: false,
Balance: "$12.50",
},
}
}
// TestPermissionModeInfo creates a test permission mode info.
func TestPermissionModeInfo(key, name, nameZh, desc, descZh string) core.PermissionModeInfo {
return core.PermissionModeInfo{
Key: key,
Name: name,
NameZh: nameZh,
Desc: desc,
DescZh: descZh,
}
}
// TestCard creates a test card using the CardBuilder.
func TestCard() *core.Card {
return core.NewCard().
Title("Test Card", "blue").
Markdown("**Test content**").
Build()
}
// TestCardWithTitle creates a card with a specific title.
func TestCardWithTitle(title string) *core.Card {
return core.NewCard().
Title(title, "blue").
Markdown("**Content**").
Build()
}
// TestCardWithButtons creates a card with buttons.
func TestCardWithButtons(buttons ...core.CardButton) *core.Card {
return core.NewCard().
Title("Test Card", "blue").
Markdown("Select an option:").
Buttons(buttons...).
Build()
}
// TestMessageHandler is a simple message handler for testing.
type TestMessageHandler struct {
mu sync.Mutex
Messages []*core.Message
}
func NewTestMessageHandler() *TestMessageHandler {
return &TestMessageHandler{
Messages: make([]*core.Message, 0),
}
}
func (h *TestMessageHandler) Handle(p core.Platform, msg *core.Message) {
h.mu.Lock()
defer h.mu.Unlock()
h.Messages = append(h.Messages, msg)
}
func (h *TestMessageHandler) GetMessages() []*core.Message {
h.mu.Lock()
defer h.mu.Unlock()
cp := make([]*core.Message, len(h.Messages))
copy(cp, h.Messages)
return cp
}
func (h *TestMessageHandler) Clear() {
h.mu.Lock()
defer h.mu.Unlock()
h.Messages = h.Messages[:0]
}
// TestDedupeItem creates a test deduplication item.
type TestDedupeItem struct {
key string
expiration time.Time
}
func NewTestDedupeItem(key string, ttl time.Duration) *TestDedupeItem {
return &TestDedupeItem{
key: key,
expiration: time.Now().Add(ttl),
}
}
// TestRateLimiterToken creates a test rate limiter token bucket state.
type TestRateLimiterToken struct{}
// TestCronJob creates a test cron job.
func TestCronJob(id, desc, prompt string, cronExpr string) *core.CronJob {
enabled := true
return &core.CronJob{
ID: id,
Description: desc,
Prompt: prompt,
CronExpr: cronExpr,
Enabled: enabled,
}
}
// TestAgentSessionInfoList creates a list of test agent session info.
func TestAgentSessionInfoList(count int) []core.AgentSessionInfo {
sessions := make([]core.AgentSessionInfo, count)
for i := 0; i < count; i++ {
sessions[i] = core.AgentSessionInfo{
ID: fmt.Sprintf("session-%d", i),
Summary: fmt.Sprintf("Test session %d", i),
MessageCount: (i + 1) * 10,
ModifiedAt: time.Now().Add(-time.Duration(i) * time.Hour),
}
}
return sessions
}
// TestContext returns a context with timeout for testing.
func TestContext(timeout time.Duration) (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), timeout)
}
+226
View File
@@ -0,0 +1,226 @@
package fake
import (
"context"
"io"
"sync"
"time"
"github.com/chenhg5/cc-connect/core"
)
// FakeAgentSession is a fake implementation of AgentSession for testing.
// It simulates agent behavior without calling real CLI tools.
type FakeAgentSession struct {
mu sync.RWMutex
sessionID string
promptQueue []string
events []core.Event
closed bool
alive bool
responseDelay time.Duration
responses []string
responseIdx int
}
func NewFakeAgentSession(sessionID string) *FakeAgentSession {
return &FakeAgentSession{
sessionID: sessionID,
alive: true,
events: make([]core.Event, 0),
promptQueue: make([]string, 0),
}
}
// SetResponseDelay sets a delay before sending responses (for timeout testing).
func (s *FakeAgentSession) SetResponseDelay(delay time.Duration) *FakeAgentSession {
s.responseDelay = delay
return s
}
// SetResponses sets predefined responses to return.
func (s *FakeAgentSession) SetResponses(responses ...string) *FakeAgentSession {
s.responses = responses
return s
}
// AddTextEvent adds a text event to the event stream.
func (s *FakeAgentSession) AddTextEvent(content string) *FakeAgentSession {
s.events = append(s.events, TestTextEvent(content))
return s
}
// AddResultEvent adds a result event to the event stream.
func (s *FakeAgentSession) AddResultEvent(content string) *FakeAgentSession {
s.events = append(s.events, TestResultEvent(content))
return s
}
// AddErrorEvent adds an error event to the event stream.
func (s *FakeAgentSession) AddErrorEvent(err error) *FakeAgentSession {
s.events = append(s.events, TestErrorEvent(err))
return s
}
// AddThinkingEvent adds a thinking event to the event stream.
func (s *FakeAgentSession) AddThinkingEvent(content string) *FakeAgentSession {
s.events = append(s.events, TestThinkingEvent(content))
return s
}
// AddPermissionRequest adds a permission request event.
func (s *FakeAgentSession) AddPermissionRequest(requestID, toolName, toolInput string) *FakeAgentSession {
s.events = append(s.events, TestPermissionRequestEvent(requestID, toolName, toolInput))
return s
}
func (s *FakeAgentSession) Send(prompt string, images []core.ImageAttachment, files []core.FileAttachment) error {
s.mu.Lock()
if s.closed {
s.mu.Unlock()
return io.ErrClosedPipe
}
s.promptQueue = append(s.promptQueue, prompt)
if len(s.events) == 0 {
if len(s.responses) > 0 && s.responseIdx < len(s.responses) {
resp := s.responses[s.responseIdx]
s.responseIdx++
s.events = append(s.events, TestTextEvent(resp), TestResultEvent(resp))
} else {
s.events = append(s.events, TestTextEvent("Processing: "+prompt), TestResultEvent("Done"))
}
}
delay := s.responseDelay
s.mu.Unlock()
if delay > 0 {
time.Sleep(delay)
}
return nil
}
func (s *FakeAgentSession) RespondPermission(requestID string, result core.PermissionResult) error {
s.mu.Lock()
defer s.mu.Unlock()
s.events = append(s.events, core.Event{
Type: core.EventToolResult,
Content: "Permission " + result.Behavior,
})
return nil
}
func (s *FakeAgentSession) Events() <-chan core.Event {
s.mu.RLock()
defer s.mu.RUnlock()
needsDone := len(s.events) == 0 || !s.events[len(s.events)-1].Done
bufSize := len(s.events)
if needsDone {
bufSize++
}
ch := make(chan core.Event, bufSize)
for _, e := range s.events {
ch <- e
}
if needsDone {
ch <- core.Event{Type: core.EventResult, Done: true}
}
close(ch)
return ch
}
func (s *FakeAgentSession) CurrentSessionID() string {
return s.sessionID
}
func (s *FakeAgentSession) Alive() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.alive && !s.closed
}
func (s *FakeAgentSession) Close() error {
s.mu.Lock()
defer s.mu.Unlock()
s.alive = false
s.closed = true
return nil
}
// GetPrompts returns all prompts sent to this session (for verification).
func (s *FakeAgentSession) GetPrompts() []string {
s.mu.RLock()
defer s.mu.RUnlock()
return s.promptQueue
}
// FakeAgent is a fake implementation of Agent for testing.
type FakeAgent struct {
name string
sessionID string
session *FakeAgentSession
preConfiguredSession *FakeAgentSession // session from NewFakeAgentWithSession
sessions []core.AgentSessionInfo
stopped bool
}
func NewFakeAgent(name string) *FakeAgent {
return &FakeAgent{
name: name,
sessionID: "fake-session-001",
sessions: []core.AgentSessionInfo{
{ID: "fake-session-001", Summary: "Test session"},
},
}
}
func (a *FakeAgent) Name() string {
return a.name
}
func (a *FakeAgent) StartSession(ctx context.Context, sessionID string) (core.AgentSession, error) {
a.sessionID = sessionID
// Return pre-configured session on first call (from NewFakeAgentWithSession)
// then create fresh sessions for subsequent calls
if a.preConfiguredSession != nil {
a.session = a.preConfiguredSession
a.preConfiguredSession = nil
return a.session, nil
}
a.session = NewFakeAgentSession(sessionID)
return a.session, nil
}
func (a *FakeAgent) ListSessions(ctx context.Context) ([]core.AgentSessionInfo, error) {
return a.sessions, nil
}
func (a *FakeAgent) Stop() error {
a.stopped = true
if a.session != nil {
a.session.Close()
}
return nil
}
// GetSession returns the current fake session.
func (a *FakeAgent) GetSession() *FakeAgentSession {
return a.session
}
// NewFakeAgentWithSession creates a fake agent with a pre-configured session.
// The pre-configured session is returned on the first StartSession call.
// Subsequent StartSession calls create fresh sessions (simulating real agent behavior).
func NewFakeAgentWithSession(name, sessionID string, session *FakeAgentSession) *FakeAgent {
return &FakeAgent{
name: name,
preConfiguredSession: session,
sessionID: sessionID,
sessions: []core.AgentSessionInfo{
{ID: sessionID, Summary: "Test session"},
},
}
}