Add configurable external rules pipeline for ingress #19

Merged
thuanle merged 8 commits from feat/external-rules-pipeline into main 2026-04-28 07:14:45 +07:00
3 changed files with 53 additions and 0 deletions
Showing only changes of commit c127018bc9 - Show all commits
+5
View File
@@ -196,6 +196,11 @@ func (d *Dispatcher) buildHistory(threadID string) ([]historyEntry, error) {
return history, nil
}
// ExportBuildDispatchMetadata exports buildDispatchMetadata for testing.
func ExportBuildDispatchMetadata(task *database.Task) map[string]string {
return buildDispatchMetadata(task)
}
// buildDispatchMetadata constructs the metadata map for OpenClaw dispatch,
// merging the task UUID with any DispatchContext from the rules pipeline.
func buildDispatchMetadata(task *database.Task) map[string]string {
+47
View File
@@ -12,6 +12,53 @@ import (
"thuanle.me/claw-email-bridge/internal/database"
)
func TestBuildDispatchMetadata_ForwardsContext(t *testing.T) {
task := &database.Task{
TaskUUID: "uuid-ctx-test",
DispatchContext: `{"rule":"whitelist","source":"env"}`,
}
metadata := ai_client.ExportBuildDispatchMetadata(task)
if metadata["task_uuid"] != "uuid-ctx-test" {
t.Errorf("expected task_uuid=uuid-ctx-test, got %q", metadata["task_uuid"])
}
if metadata["rule"] != "whitelist" {
t.Errorf("expected rule=whitelist, got %q", metadata["rule"])
}
if metadata["source"] != "env" {
t.Errorf("expected source=env, got %q", metadata["source"])
}
}
func TestBuildDispatchMetadata_ProtectsTaskUUID(t *testing.T) {
task := &database.Task{
TaskUUID: "original-uuid",
DispatchContext: `{"task_uuid":"malicious-override","extra":"data"}`,
}
metadata := ai_client.ExportBuildDispatchMetadata(task)
if metadata["task_uuid"] != "original-uuid" {
t.Errorf("task_uuid should not be overwritten, got %q", metadata["task_uuid"])
}
if metadata["extra"] != "data" {
t.Errorf("expected extra=data, got %q", metadata["extra"])
}
}
func TestBuildDispatchMetadata_NoContext(t *testing.T) {
task := &database.Task{
TaskUUID: "uuid-no-ctx",
}
metadata := ai_client.ExportBuildDispatchMetadata(task)
if len(metadata) != 1 {
t.Errorf("expected 1 key, got %d", len(metadata))
}
if metadata["task_uuid"] != "uuid-no-ctx" {
t.Errorf("expected task_uuid=uuid-no-ctx, got %q", metadata["task_uuid"])
}
}
// Test matrix #2: OpenClaw fail 2 lần, lần 3 thành công → COMPLETED, attempt_openclaw = 3.
func TestDispatch_RetryThenSuccess(t *testing.T) {
tdb := database.NewTestDB(t)
+1
View File
@@ -47,6 +47,7 @@ Bảng `tasks`:
- `status` TEXT
- `attempt_openclaw` INTEGER DEFAULT 0
- `attempt_smtp` INTEGER DEFAULT 0
- `dispatch_context` TEXT (JSON metadata từ external rules pipeline, truyền qua OpenClaw dispatch)
- `last_error` TEXT NULL
- `next_retry_at` DATETIME NULL (chỉ cần nếu retry xử lý theo scheduler/worker)
- `created_at` DATETIME