Files
boc/backend/automation/engine_test.go
T

195 lines
4.6 KiB
Go
Raw Normal View History

package automation
import (
"context"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCalculateNextRun(t *testing.T) {
e := &Engine{}
tests := []struct {
name string
cron string
tz string
wantHour int // approximate check
}{
{"daily midnight", "0 0 * * *", "UTC", 0},
{"every hour", "0 * * * *", "UTC", -1}, // any hour
{"every 5 min", "*/5 * * * *", "UTC", -1},
{"invalid fallback", "0 0 * * *", "Bad/Timezone", -1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := e.calculateNextRun(tt.cron, tt.tz)
require.NoError(t, err)
assert.True(t, got.After(time.Now()), "next run should be in the future")
if tt.wantHour >= 0 {
assert.Equal(t, tt.wantHour, got.Hour())
}
})
}
}
func TestCalculateNextRun_Invalid(t *testing.T) {
e := &Engine{}
_, err := e.calculateNextRun("not-a-cron", "UTC")
assert.Error(t, err)
}
func TestExecuteAction_SendEmail(t *testing.T) {
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
e := NewEngine(db, zerolog.New(nil))
ctx := context.Background()
// Valid email action
err = e.executeAction(ctx, "tenant-1", map[string]interface{}{
"type": "send_email",
"to": "test@example.com",
"subject": "Hello",
"body": "World",
})
assert.NoError(t, err)
// Missing required fields
err = e.executeAction(ctx, "tenant-1", map[string]interface{}{
"type": "send_email",
"to": "test@example.com",
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "requires")
// Unknown action
err = e.executeAction(ctx, "tenant-1", map[string]interface{}{
"type": "unknown_action",
})
assert.Error(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
func TestExecuteAction_CreateTask(t *testing.T) {
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
e := NewEngine(db, zerolog.New(nil))
ctx := context.Background()
mock.ExpectExec("INSERT INTO boc_tickets").
WithArgs("tenant-1", "Fix bug", "user-1").
WillReturnResult(sqlmock.NewResult(1, 1))
err = e.executeAction(ctx, "tenant-1", map[string]interface{}{
"type": "create_task",
"title": "Fix bug",
"assignee": "user-1",
})
assert.NoError(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
func TestExecuteAction_UpdateRecord(t *testing.T) {
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
e := NewEngine(db, zerolog.New(nil))
ctx := context.Background()
// Valid update
mock.ExpectExec("UPDATE boc_customers SET status = \\$(.+) WHERE id = \\$(.+) AND tenant_id = \\$(.+)").
WithArgs("active", "cust-1", "tenant-1").
WillReturnResult(sqlmock.NewResult(1, 1))
err = e.executeAction(ctx, "tenant-1", map[string]interface{}{
"type": "update_record",
"table": "boc_customers",
"record_id": "cust-1",
"field": "status",
"value": "active",
})
assert.NoError(t, err)
// Invalid table (SQL injection attempt)
err = e.executeAction(ctx, "tenant-1", map[string]interface{}{
"type": "update_record",
"table": "users; DROP TABLE boc_customers;--",
"record_id": "1",
"field": "name",
"value": "hacked",
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "not allowed")
assert.NoError(t, mock.ExpectationsWereMet())
}
func TestExecuteAction_Webhook(t *testing.T) {
db, mock, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
e := NewEngine(db, zerolog.New(nil))
ctx := context.Background()
// Valid webhook (currently just logs)
err = e.executeAction(ctx, "tenant-1", map[string]interface{}{
"type": "webhook",
"url": "https://example.com/webhook",
})
assert.NoError(t, err)
// Missing URL
err = e.executeAction(ctx, "tenant-1", map[string]interface{}{
"type": "webhook",
})
assert.Error(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
func TestEngine_StartStop(t *testing.T) {
db, _, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
e := NewEngine(db, zerolog.New(nil))
ctx, cancel := context.WithCancel(context.Background())
e.Start(ctx)
time.Sleep(50 * time.Millisecond) // Let it start
// Stop should not panic
cancel()
e.Stop()
}
func TestRunReportJob(t *testing.T) {
db, _, err := sqlmock.New()
require.NoError(t, err)
defer db.Close()
e := NewEngine(db, zerolog.New(nil))
ctx := context.Background()
job := ScheduledJob{
JobConfig: map[string]interface{}{"report_type": "monthly_sales"},
}
output, err := e.runReportJob(ctx, job)
assert.NoError(t, err)
assert.Equal(t, "monthly_sales", output["report_type"])
assert.Equal(t, "generated", output["status"])
}