Refactor Delivery Structures and Enhance Documentation
- Updated the DeliveryResult and ChannelResult structures to include new fields such as RequestID, SentAt, and improved content handling. - Revised EmailTarget and WebhookTarget structures to clarify their configurations, including the addition of Template and Method fields. - Enhanced the DeliveryPreferences structure to support Email, Webhook, and Process configurations, improving the flexibility of delivery options. - Updated DESIGN.md and TECHNICAL.md to reflect these changes, ensuring comprehensive documentation of the new delivery architecture. - Marked the completion of related tasks in TODO.md, confirming the integration of new features and structures.
This commit is contained in:
parent
8d88042489
commit
ad7e0dbf85
8 changed files with 393 additions and 97 deletions
|
|
@ -619,22 +619,23 @@ type DeliveryAgentOutput struct {
|
|||
```go
|
||||
// DeliveryResult - returned by Delivery Center
|
||||
type DeliveryResult struct {
|
||||
RequestID string `json:"request_id"` // Delivery request ID
|
||||
Content *DeliveryContent `json:"content"` // What was delivered
|
||||
Success bool `json:"success"` // All channels succeeded
|
||||
Results []ChannelResult `json:"results"` // Per-channel results
|
||||
Error string `json:"error,omitempty"`
|
||||
RequestID string `json:"request_id"` // Delivery request ID
|
||||
Content *DeliveryContent `json:"content"` // Agent-generated content
|
||||
Results []ChannelResult `json:"results,omitempty"` // Results per channel
|
||||
Success bool `json:"success"` // Overall success
|
||||
Error string `json:"error,omitempty"` // Error if failed
|
||||
SentAt *time.Time `json:"sent_at,omitempty"` // When delivery completed
|
||||
}
|
||||
|
||||
// ChannelResult - result for a single delivery target
|
||||
type ChannelResult struct {
|
||||
Type DeliveryType `json:"type"` // email | webhook | process | notify
|
||||
Target string `json:"target,omitempty"` // Target identifier
|
||||
Success bool `json:"success"`
|
||||
Recipients []string `json:"recipients,omitempty"` // For email
|
||||
Type DeliveryType `json:"type"` // email | webhook | process
|
||||
Target string `json:"target"` // Target identifier (email, URL, process name)
|
||||
Success bool `json:"success"` // Whether delivery succeeded
|
||||
Recipients []string `json:"recipients,omitempty"` // Who received (for email)
|
||||
Details interface{} `json:"details,omitempty"` // Channel-specific response
|
||||
Error string `json:"error,omitempty"`
|
||||
SentAt *time.Time `json:"sent_at,omitempty"`
|
||||
Error string `json:"error,omitempty"` // Error message if failed
|
||||
SentAt *time.Time `json:"sent_at,omitempty"` // When this target was delivered
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -857,9 +858,10 @@ type EmailPreference struct {
|
|||
}
|
||||
|
||||
type EmailTarget struct {
|
||||
To []string `json:"to"`
|
||||
CC []string `json:"cc,omitempty"`
|
||||
SubjectTemplate string `json:"subject_template,omitempty"`
|
||||
To []string `json:"to"` // Recipient addresses
|
||||
CC []string `json:"cc,omitempty"` // CC addresses
|
||||
Template string `json:"template,omitempty"` // Email template ID
|
||||
Subject string `json:"subject,omitempty"` // Subject template
|
||||
}
|
||||
|
||||
type WebhookPreference struct {
|
||||
|
|
@ -868,8 +870,10 @@ type WebhookPreference struct {
|
|||
}
|
||||
|
||||
type WebhookTarget struct {
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
URL string `json:"url"` // Webhook URL
|
||||
Method string `json:"method,omitempty"` // HTTP method (default: POST)
|
||||
Headers map[string]string `json:"headers,omitempty"` // Custom headers
|
||||
Secret string `json:"secret,omitempty"` // Signing secret
|
||||
}
|
||||
|
||||
type ProcessPreference struct {
|
||||
|
|
@ -878,8 +882,8 @@ type ProcessPreference struct {
|
|||
}
|
||||
|
||||
type ProcessTarget struct {
|
||||
Name string `json:"name"` // Process name, e.g., "orders.UpdateStatus"
|
||||
Args []any `json:"args,omitempty"`
|
||||
Process string `json:"process"` // Yao Process name, e.g., "orders.UpdateStatus"
|
||||
Args []any `json:"args,omitempty"` // Additional arguments
|
||||
}
|
||||
|
||||
// ExecutorMode - executor mode enum
|
||||
|
|
|
|||
|
|
@ -1447,9 +1447,10 @@ type EmailPreference struct {
|
|||
}
|
||||
|
||||
type EmailTarget struct {
|
||||
To []string `json:"to"`
|
||||
CC []string `json:"cc,omitempty"`
|
||||
SubjectTemplate string `json:"subject_template,omitempty"` // Optional, default: content.Summary
|
||||
To []string `json:"to"` // Recipient addresses
|
||||
CC []string `json:"cc,omitempty"` // CC addresses
|
||||
Template string `json:"template,omitempty"` // Email template ID
|
||||
Subject string `json:"subject,omitempty"` // Subject template (default: content.Summary)
|
||||
}
|
||||
|
||||
// WebhookPreference - multiple webhook targets
|
||||
|
|
@ -1459,8 +1460,10 @@ type WebhookPreference struct {
|
|||
}
|
||||
|
||||
type WebhookTarget struct {
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
URL string `json:"url"` // Webhook URL
|
||||
Method string `json:"method,omitempty"` // HTTP method (default: POST)
|
||||
Headers map[string]string `json:"headers,omitempty"` // Custom headers
|
||||
Secret string `json:"secret,omitempty"` // Signing secret
|
||||
}
|
||||
|
||||
// ProcessPreference - multiple Yao Process targets
|
||||
|
|
@ -1470,28 +1473,29 @@ type ProcessPreference struct {
|
|||
}
|
||||
|
||||
type ProcessTarget struct {
|
||||
Name string `json:"name"` // Process name, e.g., "orders.UpdateStatus"
|
||||
Args []any `json:"args,omitempty"` // Additional args (DeliveryContent passed as first arg)
|
||||
Process string `json:"process"` // Yao Process name, e.g., "orders.UpdateStatus"
|
||||
Args []any `json:"args,omitempty"` // Additional args (DeliveryContent passed as first arg)
|
||||
}
|
||||
|
||||
// DeliveryResult - P4 delivery output (returned by Delivery Center)
|
||||
type DeliveryResult struct {
|
||||
RequestID string `json:"request_id"` // Delivery request ID
|
||||
Content *DeliveryContent `json:"content,omitempty"` // What was delivered
|
||||
Success bool `json:"success"` // All channels succeeded
|
||||
Results []ChannelResult `json:"results,omitempty"` // Per-channel results
|
||||
Error string `json:"error,omitempty"` // Overall error if any
|
||||
RequestID string `json:"request_id"` // Delivery request ID
|
||||
Content *DeliveryContent `json:"content"` // Agent-generated content
|
||||
Results []ChannelResult `json:"results,omitempty"` // Results per channel
|
||||
Success bool `json:"success"` // Overall success
|
||||
Error string `json:"error,omitempty"` // Error if failed
|
||||
SentAt *time.Time `json:"sent_at,omitempty"` // When delivery completed
|
||||
}
|
||||
|
||||
// ChannelResult - result for a single delivery target
|
||||
type ChannelResult struct {
|
||||
Type DeliveryType `json:"type"` // email | webhook | process | notify
|
||||
Target string `json:"target,omitempty"` // Target identifier (email, URL, process name)
|
||||
Success bool `json:"success"`
|
||||
Type DeliveryType `json:"type"` // email | webhook | process
|
||||
Target string `json:"target"` // Target identifier (email, URL, process name)
|
||||
Success bool `json:"success"` // Whether delivery succeeded
|
||||
Recipients []string `json:"recipients,omitempty"` // Who received (for email)
|
||||
Details interface{} `json:"details,omitempty"` // Channel-specific response
|
||||
Error string `json:"error,omitempty"`
|
||||
SentAt *time.Time `json:"sent_at,omitempty"`
|
||||
Error string `json:"error,omitempty"` // Error message if failed
|
||||
SentAt *time.Time `json:"sent_at,omitempty"` // When this target was delivered
|
||||
}
|
||||
|
||||
// LearningEntry - knowledge to save
|
||||
|
|
@ -2315,16 +2319,16 @@ func (dc *DeliveryCenter) callProcess(ctx context.Context, content *DeliveryCont
|
|||
// DeliveryContent as first arg, then additional args
|
||||
args := append([]interface{}{content}, target.Args...)
|
||||
|
||||
proc := process.Of(target.Name, args...)
|
||||
proc := process.Of(target.Process, args...)
|
||||
result, err := proc.Execute()
|
||||
|
||||
now := time.Now()
|
||||
return ChannelResult{
|
||||
Type: DeliveryProcess,
|
||||
Target: target.Name,
|
||||
Target: target.Process,
|
||||
Success: err == nil,
|
||||
Details: map[string]interface{}{
|
||||
"process": target.Name,
|
||||
"process": target.Process,
|
||||
"result": result,
|
||||
},
|
||||
Error: errStr(err),
|
||||
|
|
|
|||
|
|
@ -954,24 +954,24 @@ Supported channels:
|
|||
- [x] SMS - No attachment (text only)
|
||||
- [x] WhatsApp - TBD
|
||||
|
||||
### 10.3 Type Updates (Prerequisite)
|
||||
### 10.3 Type Updates (Prerequisite) ✅
|
||||
|
||||
- [ ] Update `types/enums.go` - Update `DeliveryType` enum
|
||||
- [ ] Remove `DeliveryFile`
|
||||
- [ ] Add `DeliveryProcess`
|
||||
- [ ] Update `types/robot.go` - Delivery types for new architecture
|
||||
- [ ] `DeliveryResult` - update to new structure (RequestID, Content, Results[])
|
||||
- [ ] Add `DeliveryContent` struct
|
||||
- [ ] Add `DeliveryAttachment` struct
|
||||
- [ ] Add `DeliveryRequest` struct
|
||||
- [ ] Add `DeliveryContext` struct
|
||||
- [ ] Add `DeliveryPreferences` struct (with Email, Webhook, Process)
|
||||
- [ ] Add `EmailPreference`, `EmailTarget` structs
|
||||
- [ ] Add `WebhookPreference`, `WebhookTarget` structs
|
||||
- [ ] Add `ProcessPreference`, `ProcessTarget` structs
|
||||
- [ ] Add `ChannelResult` struct (with Target field)
|
||||
- [ ] Update `types/enums_test.go` - Update DeliveryType tests
|
||||
- [ ] Update `types/robot_test.go` - Update delivery result tests
|
||||
- [x] Update `types/enums.go` - Update `DeliveryType` enum
|
||||
- [x] Remove `DeliveryFile`
|
||||
- [x] Add `DeliveryProcess`
|
||||
- [x] Update `types/robot.go` - Delivery types for new architecture
|
||||
- [x] `DeliveryResult` - update to new structure (RequestID, Content, Results[])
|
||||
- [x] Add `DeliveryContent` struct
|
||||
- [x] Add `DeliveryAttachment` struct
|
||||
- [x] Add `DeliveryRequest` struct
|
||||
- [x] Add `DeliveryContext` struct
|
||||
- [x] Add `DeliveryPreferences` struct (with Email, Webhook, Process)
|
||||
- [x] Add `EmailPreference`, `EmailTarget` structs
|
||||
- [x] Add `WebhookPreference`, `WebhookTarget` structs
|
||||
- [x] Add `ProcessPreference`, `ProcessTarget` structs
|
||||
- [x] Add `ChannelResult` struct (with Target field)
|
||||
- [x] Update `types/enums_test.go` - Update DeliveryType tests
|
||||
- [x] Update `types/robot_test.go` - Update delivery result tests
|
||||
|
||||
### 10.4 Delivery Agent Setup
|
||||
|
||||
|
|
|
|||
|
|
@ -7,17 +7,17 @@ import (
|
|||
|
||||
// Config - robot_config in __yao.member
|
||||
type Config struct {
|
||||
Triggers *Triggers `json:"triggers,omitempty"`
|
||||
Clock *Clock `json:"clock,omitempty"`
|
||||
Identity *Identity `json:"identity"`
|
||||
Quota *Quota `json:"quota,omitempty"`
|
||||
KB *KB `json:"kb,omitempty"` // shared knowledge base (same as assistant)
|
||||
DB *DB `json:"db,omitempty"` // shared database (same as assistant)
|
||||
Learn *Learn `json:"learn,omitempty"` // learning config for private KB
|
||||
Resources *Resources `json:"resources,omitempty"`
|
||||
Delivery *Delivery `json:"delivery,omitempty"`
|
||||
Events []Event `json:"events,omitempty"`
|
||||
Executor *ExecutorConfig `json:"executor,omitempty"` // executor mode settings
|
||||
Triggers *Triggers `json:"triggers,omitempty"`
|
||||
Clock *Clock `json:"clock,omitempty"`
|
||||
Identity *Identity `json:"identity"`
|
||||
Quota *Quota `json:"quota,omitempty"`
|
||||
KB *KB `json:"kb,omitempty"` // shared knowledge base (same as assistant)
|
||||
DB *DB `json:"db,omitempty"` // shared database (same as assistant)
|
||||
Learn *Learn `json:"learn,omitempty"` // learning config for private KB
|
||||
Resources *Resources `json:"resources,omitempty"`
|
||||
Delivery *DeliveryPreferences `json:"delivery,omitempty"` // delivery preferences (see robot.go)
|
||||
Events []Event `json:"events,omitempty"`
|
||||
Executor *ExecutorConfig `json:"executor,omitempty"` // executor mode settings
|
||||
}
|
||||
|
||||
// ExecutorConfig - executor settings
|
||||
|
|
@ -224,12 +224,6 @@ type MCPConfig struct {
|
|||
Tools []string `json:"tools,omitempty"` // empty = all
|
||||
}
|
||||
|
||||
// Delivery - output delivery
|
||||
type Delivery struct {
|
||||
Type DeliveryType `json:"type"`
|
||||
Opts map[string]interface{} `json:"opts,omitempty"`
|
||||
}
|
||||
|
||||
// Event - event trigger config
|
||||
type Event struct {
|
||||
Type EventSource `json:"type"` // webhook | database
|
||||
|
|
|
|||
|
|
@ -111,10 +111,10 @@ type DeliveryType string
|
|||
|
||||
// DeliveryType constants define the output delivery types
|
||||
const (
|
||||
DeliveryEmail DeliveryType = "email"
|
||||
DeliveryFile DeliveryType = "file"
|
||||
DeliveryWebhook DeliveryType = "webhook"
|
||||
DeliveryNotify DeliveryType = "notify"
|
||||
DeliveryEmail DeliveryType = "email" // Send via yao/messenger
|
||||
DeliveryWebhook DeliveryType = "webhook" // POST to external URL
|
||||
DeliveryProcess DeliveryType = "process" // Call Yao Process
|
||||
DeliveryNotify DeliveryType = "notify" // In-app notification (future, auto by subscriptions)
|
||||
)
|
||||
|
||||
// DedupResult - deduplication result
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ func TestPriorityEnum(t *testing.T) {
|
|||
|
||||
func TestDeliveryTypeEnum(t *testing.T) {
|
||||
assert.Equal(t, types.DeliveryType("email"), types.DeliveryEmail)
|
||||
assert.Equal(t, types.DeliveryType("file"), types.DeliveryFile)
|
||||
assert.Equal(t, types.DeliveryType("webhook"), types.DeliveryWebhook)
|
||||
assert.Equal(t, types.DeliveryType("process"), types.DeliveryProcess)
|
||||
assert.Equal(t, types.DeliveryType("notify"), types.DeliveryNotify)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -262,15 +262,101 @@ type ValidationResult struct {
|
|||
ReplyContent string `json:"reply_content,omitempty"` // content for next turn (if NeedReply)
|
||||
}
|
||||
|
||||
// DeliveryResult - P4 delivery output
|
||||
// DeliveryResult - P4 delivery output (new architecture)
|
||||
type DeliveryResult struct {
|
||||
Type DeliveryType `json:"type"`
|
||||
Success bool `json:"success"`
|
||||
Recipients []string `json:"recipients,omitempty"` // who received
|
||||
Content string `json:"content,omitempty"` // formatted content that was delivered
|
||||
Details interface{} `json:"details,omitempty"` // channel-specific response
|
||||
Error string `json:"error,omitempty"`
|
||||
SentAt *time.Time `json:"sent_at,omitempty"`
|
||||
RequestID string `json:"request_id"` // Delivery request ID
|
||||
Content *DeliveryContent `json:"content"` // Agent-generated content
|
||||
Results []ChannelResult `json:"results,omitempty"` // Results per channel
|
||||
Success bool `json:"success"` // Overall success
|
||||
Error string `json:"error,omitempty"` // Error if failed
|
||||
SentAt *time.Time `json:"sent_at,omitempty"` // When delivery completed
|
||||
}
|
||||
|
||||
// DeliveryContent - Content generated by Delivery Agent (only content, no channels)
|
||||
type DeliveryContent struct {
|
||||
Summary string `json:"summary"` // Brief 1-2 sentence summary
|
||||
Body string `json:"body"` // Full markdown report
|
||||
Attachments []DeliveryAttachment `json:"attachments,omitempty"` // Output artifacts from P3
|
||||
}
|
||||
|
||||
// DeliveryAttachment - Task output attachment with metadata
|
||||
type DeliveryAttachment struct {
|
||||
Title string `json:"title"` // Human-readable title
|
||||
Description string `json:"description,omitempty"` // What this artifact is
|
||||
TaskID string `json:"task_id,omitempty"` // Which task produced this
|
||||
File string `json:"file"` // Wrapper: __<uploader>://<fileID>
|
||||
}
|
||||
|
||||
// DeliveryRequest - pushed to Delivery Center (no channels - center decides based on preferences)
|
||||
type DeliveryRequest struct {
|
||||
Content *DeliveryContent `json:"content"` // Agent-generated content
|
||||
Context *DeliveryContext `json:"context"` // Tracking info
|
||||
}
|
||||
|
||||
// DeliveryContext - tracking and audit info
|
||||
type DeliveryContext struct {
|
||||
MemberID string `json:"member_id"` // Robot member ID (globally unique)
|
||||
ExecutionID string `json:"execution_id"` // Execution ID
|
||||
TriggerType TriggerType `json:"trigger_type"` // clock | human | event
|
||||
TeamID string `json:"team_id"` // Team ID
|
||||
}
|
||||
|
||||
// DeliveryPreferences - Robot/User delivery preferences (from Config)
|
||||
type DeliveryPreferences struct {
|
||||
Email *EmailPreference `json:"email,omitempty"` // Email delivery settings
|
||||
Webhook *WebhookPreference `json:"webhook,omitempty"` // Webhook delivery settings
|
||||
Process *ProcessPreference `json:"process,omitempty"` // Process delivery settings
|
||||
}
|
||||
|
||||
// EmailPreference - Email delivery configuration
|
||||
type EmailPreference struct {
|
||||
Enabled bool `json:"enabled"` // Whether email delivery is enabled
|
||||
Targets []EmailTarget `json:"targets,omitempty"` // Multiple email targets
|
||||
}
|
||||
|
||||
// EmailTarget - Single email target
|
||||
type EmailTarget struct {
|
||||
To []string `json:"to"` // Recipient addresses
|
||||
CC []string `json:"cc,omitempty"` // CC addresses
|
||||
Template string `json:"template,omitempty"` // Email template ID
|
||||
Subject string `json:"subject,omitempty"` // Subject template
|
||||
}
|
||||
|
||||
// WebhookPreference - Webhook delivery configuration
|
||||
type WebhookPreference struct {
|
||||
Enabled bool `json:"enabled"` // Whether webhook delivery is enabled
|
||||
Targets []WebhookTarget `json:"targets,omitempty"` // Multiple webhook targets
|
||||
}
|
||||
|
||||
// WebhookTarget - Single webhook target
|
||||
type WebhookTarget struct {
|
||||
URL string `json:"url"` // Webhook URL
|
||||
Method string `json:"method,omitempty"` // HTTP method (default: POST)
|
||||
Headers map[string]string `json:"headers,omitempty"` // Custom headers
|
||||
Secret string `json:"secret,omitempty"` // Signing secret
|
||||
}
|
||||
|
||||
// ProcessPreference - Process delivery configuration
|
||||
type ProcessPreference struct {
|
||||
Enabled bool `json:"enabled"` // Whether process delivery is enabled
|
||||
Targets []ProcessTarget `json:"targets,omitempty"` // Multiple process targets
|
||||
}
|
||||
|
||||
// ProcessTarget - Single process target
|
||||
type ProcessTarget struct {
|
||||
Process string `json:"process"` // Yao Process name
|
||||
Args []any `json:"args,omitempty"` // Process arguments
|
||||
}
|
||||
|
||||
// ChannelResult - Result of delivery to a single channel target
|
||||
type ChannelResult struct {
|
||||
Type DeliveryType `json:"type"` // email | webhook | process
|
||||
Target string `json:"target"` // Target identifier (email, URL, process name)
|
||||
Success bool `json:"success"` // Whether delivery succeeded
|
||||
Recipients []string `json:"recipients,omitempty"` // Who received (for email)
|
||||
Details interface{} `json:"details,omitempty"` // Channel-specific response
|
||||
Error string `json:"error,omitempty"` // Error message if failed
|
||||
SentAt *time.Time `json:"sent_at,omitempty"` // When this target was delivered
|
||||
}
|
||||
|
||||
// LearningEntry - knowledge to save
|
||||
|
|
|
|||
|
|
@ -536,25 +536,233 @@ func TestValidationResultMultiTurnFields(t *testing.T) {
|
|||
func TestDeliveryResultStructure(t *testing.T) {
|
||||
sentAt := time.Now()
|
||||
delivery := &types.DeliveryResult{
|
||||
Type: types.DeliveryEmail,
|
||||
Success: true,
|
||||
Recipients: []string{"user@example.com", "manager@example.com"},
|
||||
Content: "# Weekly Report\n\nSales increased by 20%...",
|
||||
Details: map[string]interface{}{
|
||||
"message_id": "msg-12345",
|
||||
"subject": "Daily Report",
|
||||
RequestID: "req-12345",
|
||||
Content: &types.DeliveryContent{
|
||||
Summary: "Weekly sales report completed",
|
||||
Body: "# Weekly Report\n\nSales increased by 20%...",
|
||||
Attachments: []types.DeliveryAttachment{
|
||||
{
|
||||
Title: "Sales Report",
|
||||
Description: "Detailed sales analysis",
|
||||
TaskID: "task-1",
|
||||
File: "__s3://report-12345.pdf",
|
||||
},
|
||||
},
|
||||
},
|
||||
SentAt: &sentAt,
|
||||
Results: []types.ChannelResult{
|
||||
{
|
||||
Type: types.DeliveryEmail,
|
||||
Target: "user@example.com",
|
||||
Success: true,
|
||||
Details: map[string]interface{}{
|
||||
"message_id": "msg-12345",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: types.DeliveryWebhook,
|
||||
Target: "https://webhook.example.com/notify",
|
||||
Success: true,
|
||||
},
|
||||
},
|
||||
Success: true,
|
||||
SentAt: &sentAt,
|
||||
}
|
||||
|
||||
assert.Equal(t, types.DeliveryEmail, delivery.Type)
|
||||
assert.Equal(t, "req-12345", delivery.RequestID)
|
||||
assert.True(t, delivery.Success)
|
||||
assert.Len(t, delivery.Recipients, 2)
|
||||
assert.Contains(t, delivery.Content, "Weekly Report")
|
||||
assert.NotNil(t, delivery.Details)
|
||||
assert.NotNil(t, delivery.Content)
|
||||
assert.Equal(t, "Weekly sales report completed", delivery.Content.Summary)
|
||||
assert.Contains(t, delivery.Content.Body, "Weekly Report")
|
||||
assert.Len(t, delivery.Content.Attachments, 1)
|
||||
assert.Equal(t, "__s3://report-12345.pdf", delivery.Content.Attachments[0].File)
|
||||
assert.Len(t, delivery.Results, 2)
|
||||
assert.Equal(t, types.DeliveryEmail, delivery.Results[0].Type)
|
||||
assert.NotNil(t, delivery.SentAt)
|
||||
}
|
||||
|
||||
func TestDeliveryContentStructure(t *testing.T) {
|
||||
content := &types.DeliveryContent{
|
||||
Summary: "Task execution completed successfully",
|
||||
Body: "# Execution Report\n\n## Summary\n- 3 tasks completed\n- 1 task failed",
|
||||
Attachments: []types.DeliveryAttachment{
|
||||
{
|
||||
Title: "Analysis Results",
|
||||
Description: "JSON data from analysis task",
|
||||
TaskID: "task-analysis",
|
||||
File: "__local://files/analysis-result.json",
|
||||
},
|
||||
{
|
||||
Title: "Generated Chart",
|
||||
TaskID: "task-chart",
|
||||
File: "__s3://charts/sales-chart.png",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.NotEmpty(t, content.Summary)
|
||||
assert.Contains(t, content.Body, "Execution Report")
|
||||
assert.Len(t, content.Attachments, 2)
|
||||
assert.Equal(t, "Analysis Results", content.Attachments[0].Title)
|
||||
assert.Equal(t, "task-analysis", content.Attachments[0].TaskID)
|
||||
}
|
||||
|
||||
func TestDeliveryAttachmentStructure(t *testing.T) {
|
||||
attachment := &types.DeliveryAttachment{
|
||||
Title: "Sales Report PDF",
|
||||
Description: "Monthly sales analysis report",
|
||||
TaskID: "task-report",
|
||||
File: "__s3://reports/sales-2024-01.pdf",
|
||||
}
|
||||
|
||||
assert.Equal(t, "Sales Report PDF", attachment.Title)
|
||||
assert.Equal(t, "Monthly sales analysis report", attachment.Description)
|
||||
assert.Equal(t, "task-report", attachment.TaskID)
|
||||
assert.Contains(t, attachment.File, "__s3://")
|
||||
}
|
||||
|
||||
func TestDeliveryRequestStructure(t *testing.T) {
|
||||
request := &types.DeliveryRequest{
|
||||
Content: &types.DeliveryContent{
|
||||
Summary: "Report ready",
|
||||
Body: "# Report\n\nDetails...",
|
||||
},
|
||||
Context: &types.DeliveryContext{
|
||||
MemberID: "member-123",
|
||||
ExecutionID: "exec-456",
|
||||
TriggerType: types.TriggerClock,
|
||||
TeamID: "team-789",
|
||||
},
|
||||
}
|
||||
|
||||
assert.NotNil(t, request.Content)
|
||||
assert.NotNil(t, request.Context)
|
||||
assert.Equal(t, "member-123", request.Context.MemberID)
|
||||
assert.Equal(t, "exec-456", request.Context.ExecutionID)
|
||||
assert.Equal(t, types.TriggerClock, request.Context.TriggerType)
|
||||
}
|
||||
|
||||
func TestDeliveryPreferencesStructure(t *testing.T) {
|
||||
prefs := &types.DeliveryPreferences{
|
||||
Email: &types.EmailPreference{
|
||||
Enabled: true,
|
||||
Targets: []types.EmailTarget{
|
||||
{
|
||||
To: []string{"team@example.com"},
|
||||
CC: []string{"manager@example.com"},
|
||||
Template: "weekly-report",
|
||||
Subject: "Weekly Report - {{.Date}}",
|
||||
},
|
||||
{
|
||||
To: []string{"backup@example.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Webhook: &types.WebhookPreference{
|
||||
Enabled: true,
|
||||
Targets: []types.WebhookTarget{
|
||||
{
|
||||
URL: "https://api.example.com/webhook",
|
||||
Method: "POST",
|
||||
Headers: map[string]string{
|
||||
"X-API-Key": "secret-key",
|
||||
},
|
||||
Secret: "signing-secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
Process: &types.ProcessPreference{
|
||||
Enabled: true,
|
||||
Targets: []types.ProcessTarget{
|
||||
{
|
||||
Process: "scripts.notify.slack",
|
||||
Args: []any{"#general", "Report ready"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Email
|
||||
assert.True(t, prefs.Email.Enabled)
|
||||
assert.Len(t, prefs.Email.Targets, 2)
|
||||
assert.Equal(t, "weekly-report", prefs.Email.Targets[0].Template)
|
||||
assert.Len(t, prefs.Email.Targets[0].To, 1)
|
||||
assert.Len(t, prefs.Email.Targets[0].CC, 1)
|
||||
|
||||
// Webhook
|
||||
assert.True(t, prefs.Webhook.Enabled)
|
||||
assert.Len(t, prefs.Webhook.Targets, 1)
|
||||
assert.Equal(t, "https://api.example.com/webhook", prefs.Webhook.Targets[0].URL)
|
||||
assert.Equal(t, "POST", prefs.Webhook.Targets[0].Method)
|
||||
|
||||
// Process
|
||||
assert.True(t, prefs.Process.Enabled)
|
||||
assert.Len(t, prefs.Process.Targets, 1)
|
||||
assert.Equal(t, "scripts.notify.slack", prefs.Process.Targets[0].Process)
|
||||
assert.Len(t, prefs.Process.Targets[0].Args, 2)
|
||||
}
|
||||
|
||||
func TestChannelResultStructure(t *testing.T) {
|
||||
t.Run("email result with recipients", func(t *testing.T) {
|
||||
sentAt := time.Now()
|
||||
result := &types.ChannelResult{
|
||||
Type: types.DeliveryEmail,
|
||||
Target: "user@example.com",
|
||||
Success: true,
|
||||
Recipients: []string{"user@example.com", "manager@example.com"},
|
||||
Details: map[string]interface{}{
|
||||
"message_id": "msg-123",
|
||||
},
|
||||
SentAt: &sentAt,
|
||||
}
|
||||
assert.Equal(t, types.DeliveryEmail, result.Type)
|
||||
assert.Equal(t, "user@example.com", result.Target)
|
||||
assert.True(t, result.Success)
|
||||
assert.Len(t, result.Recipients, 2)
|
||||
assert.NotNil(t, result.SentAt)
|
||||
})
|
||||
|
||||
t.Run("webhook result", func(t *testing.T) {
|
||||
sentAt := time.Now()
|
||||
result := &types.ChannelResult{
|
||||
Type: types.DeliveryWebhook,
|
||||
Target: "https://api.example.com/webhook",
|
||||
Success: true,
|
||||
Details: map[string]interface{}{
|
||||
"status_code": 200,
|
||||
"response": "OK",
|
||||
},
|
||||
SentAt: &sentAt,
|
||||
}
|
||||
assert.Equal(t, types.DeliveryWebhook, result.Type)
|
||||
assert.True(t, result.Success)
|
||||
assert.NotNil(t, result.SentAt)
|
||||
})
|
||||
|
||||
t.Run("process result", func(t *testing.T) {
|
||||
result := &types.ChannelResult{
|
||||
Type: types.DeliveryProcess,
|
||||
Target: "scripts.notify.slack",
|
||||
Success: true,
|
||||
Details: map[string]interface{}{
|
||||
"output": "Message sent",
|
||||
},
|
||||
}
|
||||
assert.Equal(t, types.DeliveryProcess, result.Type)
|
||||
assert.Equal(t, "scripts.notify.slack", result.Target)
|
||||
})
|
||||
|
||||
t.Run("failed result", func(t *testing.T) {
|
||||
result := &types.ChannelResult{
|
||||
Type: types.DeliveryWebhook,
|
||||
Target: "https://api.example.com/webhook",
|
||||
Success: false,
|
||||
Error: "Connection refused",
|
||||
}
|
||||
assert.False(t, result.Success)
|
||||
assert.Equal(t, "Connection refused", result.Error)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeliveryTargetStructure(t *testing.T) {
|
||||
delivery := &types.DeliveryTarget{
|
||||
Type: types.DeliveryEmail,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue