Notify distributor after report generation
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
distributoradapter "gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/distributor"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/scriptorium"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/scriptorium"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/weatherapi"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/weatherapi"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
|
||||||
@@ -45,6 +46,7 @@ type GenerateRequest struct {
|
|||||||
Date time.Time
|
Date time.Time
|
||||||
StormStart time.Time
|
StormStart time.Time
|
||||||
StormEnd time.Time
|
StormEnd time.Time
|
||||||
|
Notifier Notifier
|
||||||
}
|
}
|
||||||
|
|
||||||
type BatchRequest struct {
|
type BatchRequest struct {
|
||||||
@@ -54,6 +56,7 @@ type BatchRequest struct {
|
|||||||
OutputDir string
|
OutputDir string
|
||||||
Renderer Renderer
|
Renderer Renderer
|
||||||
Store state.Store
|
Store state.Store
|
||||||
|
Notifier Notifier
|
||||||
}
|
}
|
||||||
|
|
||||||
type FetchBundleRequest struct {
|
type FetchBundleRequest struct {
|
||||||
@@ -73,6 +76,7 @@ type ReportRequest struct {
|
|||||||
OutputPath string
|
OutputPath string
|
||||||
Renderer Renderer
|
Renderer Renderer
|
||||||
Store state.Store
|
Store state.Store
|
||||||
|
Notifier Notifier
|
||||||
}
|
}
|
||||||
|
|
||||||
type BriefingResult struct {
|
type BriefingResult struct {
|
||||||
@@ -94,6 +98,7 @@ type ReportResult struct {
|
|||||||
RecentChanges []changes.Change
|
RecentChanges []changes.Change
|
||||||
RenderResult *scriptorium.RenderResult
|
RenderResult *scriptorium.RenderResult
|
||||||
RunResult *scriptorium.RunResult
|
RunResult *scriptorium.RunResult
|
||||||
|
Notification *NotificationResult
|
||||||
}
|
}
|
||||||
|
|
||||||
type BatchResult struct {
|
type BatchResult struct {
|
||||||
@@ -139,6 +144,26 @@ type Renderer interface {
|
|||||||
Run(context.Context, scriptorium.RunRequest) (*scriptorium.RunResult, error)
|
Run(context.Context, scriptorium.RunRequest) (*scriptorium.RunResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Notifier interface {
|
||||||
|
Notify(context.Context, NotificationRequest) (*NotificationResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotificationRequest struct {
|
||||||
|
ReportID report.ID
|
||||||
|
RunID string
|
||||||
|
BundleID string
|
||||||
|
IdempotencyKey string
|
||||||
|
ReportPath string
|
||||||
|
BundlePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotificationResult struct {
|
||||||
|
BundleID string
|
||||||
|
IdempotencyKey string
|
||||||
|
RunID string
|
||||||
|
Status string
|
||||||
|
}
|
||||||
|
|
||||||
func Generate(ctx context.Context, req GenerateRequest) error {
|
func Generate(ctx context.Context, req GenerateRequest) error {
|
||||||
now := req.Now
|
now := req.Now
|
||||||
if now.IsZero() {
|
if now.IsZero() {
|
||||||
@@ -153,6 +178,7 @@ func Generate(ctx context.Context, req GenerateRequest) error {
|
|||||||
Config: req.Config,
|
Config: req.Config,
|
||||||
Resolved: resolved,
|
Resolved: resolved,
|
||||||
OutputPath: req.OutputPath,
|
OutputPath: req.OutputPath,
|
||||||
|
Notifier: req.Notifier,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -211,6 +237,7 @@ func RunBatchDetailed(ctx context.Context, req BatchRequest) (*BatchResult, erro
|
|||||||
OutputPath: outputPath,
|
OutputPath: outputPath,
|
||||||
Renderer: req.Renderer,
|
Renderer: req.Renderer,
|
||||||
Store: store,
|
Store: store,
|
||||||
|
Notifier: req.Notifier,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
item.Status = "failed"
|
item.Status = "failed"
|
||||||
@@ -481,6 +508,11 @@ func GenerateReport(ctx context.Context, req ReportRequest) (*ReportResult, erro
|
|||||||
return nil, runErr
|
return nil, runErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notification, err := notifyReport(ctx, req.Config, req.Resolved, reportPath, metadata, req.Notifier)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &ReportResult{
|
return &ReportResult{
|
||||||
Briefing: briefingPackage,
|
Briefing: briefingPackage,
|
||||||
BriefingPath: briefingPath,
|
BriefingPath: briefingPath,
|
||||||
@@ -495,6 +527,94 @@ func GenerateReport(ctx context.Context, req ReportRequest) (*ReportResult, erro
|
|||||||
RecentChanges: recentChanges,
|
RecentChanges: recentChanges,
|
||||||
RenderResult: renderResult,
|
RenderResult: renderResult,
|
||||||
RunResult: runResult,
|
RunResult: runResult,
|
||||||
|
Notification: notification,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyReport(ctx context.Context, cfg config.Config, resolved report.Resolved, reportPath string, metadata state.Metadata, notifier Notifier) (*NotificationResult, error) {
|
||||||
|
notifier, enabled := reportNotifier(cfg, notifier)
|
||||||
|
if !enabled {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
notificationRequest, err := buildNotificationRequest(cfg, resolved, reportPath, metadata)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result, err := notifier.Notify(ctx, notificationRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("notify report %q run %q from managed report %q: %w", resolved.Definition.ID, metadata.RunID, reportPath, err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reportNotifier(cfg config.Config, notifier Notifier) (Notifier, bool) {
|
||||||
|
if !cfg.Notify.Distributor.Enabled {
|
||||||
|
return noopNotifier{}, false
|
||||||
|
}
|
||||||
|
if notifier != nil {
|
||||||
|
return notifier, true
|
||||||
|
}
|
||||||
|
return distributorNotifier{
|
||||||
|
client: distributoradapter.New(cfg.Notify.Distributor),
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildNotificationRequest(cfg config.Config, resolved report.Resolved, reportPath string, metadata state.Metadata) (NotificationRequest, error) {
|
||||||
|
values := config.DistributorTemplateValues{
|
||||||
|
LocationID: cfg.Location.ID,
|
||||||
|
ReportID: string(resolved.Definition.ID),
|
||||||
|
RunID: metadata.RunID,
|
||||||
|
ArtifactGroup: resolved.Definition.ArtifactGroup,
|
||||||
|
BatchOutputName: resolved.Definition.BatchOutputName,
|
||||||
|
}
|
||||||
|
bundleID, err := config.RenderDistributorBundleID(cfg.Notify.Distributor.BundleIDTemplate, values)
|
||||||
|
if err != nil {
|
||||||
|
return NotificationRequest{}, err
|
||||||
|
}
|
||||||
|
values.BundleID = bundleID
|
||||||
|
idempotencyKey, err := config.RenderDistributorIdempotencyKey(cfg.Notify.Distributor.IdempotencyKeyTemplate, values)
|
||||||
|
if err != nil {
|
||||||
|
return NotificationRequest{}, err
|
||||||
|
}
|
||||||
|
bundlePath, err := config.RenderDistributorReportPath(cfg.Notify.Distributor.ReportPathTemplate, values)
|
||||||
|
if err != nil {
|
||||||
|
return NotificationRequest{}, err
|
||||||
|
}
|
||||||
|
return NotificationRequest{
|
||||||
|
ReportID: resolved.Definition.ID,
|
||||||
|
RunID: metadata.RunID,
|
||||||
|
BundleID: bundleID,
|
||||||
|
IdempotencyKey: idempotencyKey,
|
||||||
|
ReportPath: reportPath,
|
||||||
|
BundlePath: bundlePath,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type noopNotifier struct{}
|
||||||
|
|
||||||
|
func (noopNotifier) Notify(context.Context, NotificationRequest) (*NotificationResult, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type distributorNotifier struct {
|
||||||
|
client *distributoradapter.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n distributorNotifier) Notify(ctx context.Context, req NotificationRequest) (*NotificationResult, error) {
|
||||||
|
result, err := n.client.Upload(ctx, distributoradapter.UploadRequest{
|
||||||
|
BundleID: req.BundleID,
|
||||||
|
IdempotencyKey: req.IdempotencyKey,
|
||||||
|
SourcePath: req.ReportPath,
|
||||||
|
BundlePath: req.BundlePath,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &NotificationResult{
|
||||||
|
BundleID: req.BundleID,
|
||||||
|
IdempotencyKey: req.IdempotencyKey,
|
||||||
|
RunID: result.RunID,
|
||||||
|
Status: result.Status,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,209 @@ func TestGenerateReportWritesReportAndPreflight(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenerateReportDisabledNotificationDoesNotCallNotifier(t *testing.T) {
|
||||||
|
server := dailyBundleServer(t)
|
||||||
|
cfg := dailyTestConfig(t, server)
|
||||||
|
cfg.Workspace.Root = t.TempDir()
|
||||||
|
resolved, err := ResolveGenerate(GenerateRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Report: ReportDaily,
|
||||||
|
Date: mustParse("2026-05-29T12:00:00-05:00"),
|
||||||
|
}, mustParse("2026-05-29T05:00:00-05:00"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||||
|
}
|
||||||
|
notifier := &recordingNotifier{}
|
||||||
|
|
||||||
|
_, err = GenerateReport(context.Background(), ReportRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Resolved: resolved,
|
||||||
|
Renderer: successfulRenderer("# Daily Report\n"),
|
||||||
|
Notifier: notifier,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GenerateReport() error = %v", err)
|
||||||
|
}
|
||||||
|
if len(notifier.requests) != 0 {
|
||||||
|
t.Fatalf("notification requests = %#v, want none when disabled", notifier.requests)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateReportNotifiesManagedReportPath(t *testing.T) {
|
||||||
|
server := dailyBundleServer(t)
|
||||||
|
cfg := dailyTestConfig(t, server)
|
||||||
|
cfg.Workspace.Root = t.TempDir()
|
||||||
|
cfg.Notify.Distributor.Enabled = true
|
||||||
|
resolved, err := ResolveGenerate(GenerateRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Report: ReportDaily,
|
||||||
|
Date: mustParse("2026-05-29T12:00:00-05:00"),
|
||||||
|
}, mustParse("2026-05-29T05:00:00-05:00"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||||
|
}
|
||||||
|
notifier := &recordingNotifier{
|
||||||
|
result: &NotificationResult{RunID: "distributor-run", Status: "accepted"},
|
||||||
|
}
|
||||||
|
outputPath := filepath.Join(t.TempDir(), "daily-copy.md")
|
||||||
|
|
||||||
|
result, err := GenerateReport(context.Background(), ReportRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Resolved: resolved,
|
||||||
|
OutputPath: outputPath,
|
||||||
|
Renderer: successfulRenderer("# Daily Report\n"),
|
||||||
|
Notifier: notifier,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GenerateReport() error = %v", err)
|
||||||
|
}
|
||||||
|
if result.Notification == nil {
|
||||||
|
t.Fatal("Notification = nil, want notification result")
|
||||||
|
}
|
||||||
|
if result.Notification.RunID != "distributor-run" || result.Notification.Status != "accepted" {
|
||||||
|
t.Fatalf("Notification = %#v, want accepted distributor run", result.Notification)
|
||||||
|
}
|
||||||
|
if len(notifier.requests) != 1 {
|
||||||
|
t.Fatalf("notification requests = %d, want 1", len(notifier.requests))
|
||||||
|
}
|
||||||
|
req := notifier.requests[0]
|
||||||
|
if req.ReportPath != result.ReportPath {
|
||||||
|
t.Fatalf("notification ReportPath = %q, want managed path %q", req.ReportPath, result.ReportPath)
|
||||||
|
}
|
||||||
|
if req.ReportPath == outputPath {
|
||||||
|
t.Fatalf("notification used output copy %q, want managed report path", outputPath)
|
||||||
|
}
|
||||||
|
if req.BundlePath != "daily.md" {
|
||||||
|
t.Fatalf("notification BundlePath = %q, want daily.md", req.BundlePath)
|
||||||
|
}
|
||||||
|
if req.BundleID != "weatherreporter.home.daily_today."+result.Metadata.RunID {
|
||||||
|
t.Fatalf("notification BundleID = %q, want default template", req.BundleID)
|
||||||
|
}
|
||||||
|
if req.IdempotencyKey != req.BundleID {
|
||||||
|
t.Fatalf("IdempotencyKey = %q, want bundle id %q", req.IdempotencyKey, req.BundleID)
|
||||||
|
}
|
||||||
|
if req.RunID != result.Metadata.RunID {
|
||||||
|
t.Fatalf("notification RunID = %q, want report run id %q", req.RunID, result.Metadata.RunID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateReportNotificationFailureFailsReport(t *testing.T) {
|
||||||
|
server := dailyBundleServer(t)
|
||||||
|
cfg := dailyTestConfig(t, server)
|
||||||
|
cfg.Workspace.Root = t.TempDir()
|
||||||
|
cfg.Notify.Distributor.Enabled = true
|
||||||
|
resolved, err := ResolveGenerate(GenerateRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Report: ReportDaily,
|
||||||
|
Date: mustParse("2026-05-29T12:00:00-05:00"),
|
||||||
|
}, mustParse("2026-05-29T05:00:00-05:00"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||||
|
}
|
||||||
|
notifier := &recordingNotifier{err: errors.New("upload rejected")}
|
||||||
|
|
||||||
|
_, err = GenerateReport(context.Background(), ReportRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Resolved: resolved,
|
||||||
|
Renderer: successfulRenderer("# Daily Report\n"),
|
||||||
|
Notifier: notifier,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("GenerateReport() error = nil, want notification error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "notify report") || !strings.Contains(err.Error(), "upload rejected") {
|
||||||
|
t.Fatalf("error = %q, want notification context", err.Error())
|
||||||
|
}
|
||||||
|
if len(notifier.requests) != 1 {
|
||||||
|
t.Fatalf("notification requests = %d, want one attempted notification", len(notifier.requests))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateReportDoesNotNotifyAfterRenderOrRunFailure(t *testing.T) {
|
||||||
|
server := dailyBundleServer(t)
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
renderer Renderer
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Render",
|
||||||
|
renderer: &recordingRenderer{
|
||||||
|
renderResult: &scriptorium.RenderResult{ExitCode: 1, Stderr: "render failed"},
|
||||||
|
err: errors.New("render failed"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Run",
|
||||||
|
renderer: &recordingRenderer{
|
||||||
|
renderResult: &scriptorium.RenderResult{ExitCode: 0},
|
||||||
|
runResult: &scriptorium.RunResult{ExitCode: 2, Stderr: "run failed"},
|
||||||
|
runErr: errors.New("run failed"),
|
||||||
|
runBody: "# Daily Report\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cfg := dailyTestConfig(t, server)
|
||||||
|
cfg.Workspace.Root = t.TempDir()
|
||||||
|
cfg.Notify.Distributor.Enabled = true
|
||||||
|
resolved, err := ResolveGenerate(GenerateRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Report: ReportDaily,
|
||||||
|
Date: mustParse("2026-05-29T12:00:00-05:00"),
|
||||||
|
}, mustParse("2026-05-29T05:00:00-05:00"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||||
|
}
|
||||||
|
notifier := &recordingNotifier{}
|
||||||
|
|
||||||
|
_, err = GenerateReport(context.Background(), ReportRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Resolved: resolved,
|
||||||
|
Renderer: tt.renderer,
|
||||||
|
Notifier: notifier,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("GenerateReport() error = nil, want generation error")
|
||||||
|
}
|
||||||
|
if len(notifier.requests) != 0 {
|
||||||
|
t.Fatalf("notification requests = %#v, want none after generation failure", notifier.requests)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateReportDoesNotNotifyAfterFetchFailure(t *testing.T) {
|
||||||
|
cfg := config.Defaults()
|
||||||
|
cfg.WeatherAPI.BaseURL = ""
|
||||||
|
cfg.WeatherAPI.Timezone = "America/Chicago"
|
||||||
|
cfg.Workspace.Root = t.TempDir()
|
||||||
|
cfg.Notify.Distributor.Enabled = true
|
||||||
|
resolved, err := ResolveGenerate(GenerateRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Report: ReportDaily,
|
||||||
|
Date: mustParse("2026-05-29T12:00:00-05:00"),
|
||||||
|
}, mustParse("2026-05-29T05:00:00-05:00"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||||
|
}
|
||||||
|
notifier := &recordingNotifier{}
|
||||||
|
|
||||||
|
_, err = GenerateReport(context.Background(), ReportRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Resolved: resolved,
|
||||||
|
Renderer: successfulRenderer("# Daily Report\n"),
|
||||||
|
Notifier: notifier,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("GenerateReport() error = nil, want fetch setup error")
|
||||||
|
}
|
||||||
|
if len(notifier.requests) != 0 {
|
||||||
|
t.Fatalf("notification requests = %#v, want none after fetch failure", notifier.requests)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGenerateReportPersistsFailedPreflight(t *testing.T) {
|
func TestGenerateReportPersistsFailedPreflight(t *testing.T) {
|
||||||
server := dailyBundleServer(t)
|
server := dailyBundleServer(t)
|
||||||
cfg := config.Defaults()
|
cfg := config.Defaults()
|
||||||
@@ -1008,6 +1211,53 @@ func TestRunBatchContinuesAfterReportFailure(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunBatchContinuesAfterNotificationFailure(t *testing.T) {
|
||||||
|
server := dailyBundleServer(t)
|
||||||
|
cfg := config.Defaults()
|
||||||
|
cfg.WeatherAPI.BaseURL = server.URL + "/"
|
||||||
|
cfg.WeatherAPI.Timezone = "America/Chicago"
|
||||||
|
cfg.Workspace.Root = t.TempDir()
|
||||||
|
cfg.Notify.Distributor.Enabled = true
|
||||||
|
notifier := &recordingNotifier{
|
||||||
|
errByReport: map[report.ID]error{
|
||||||
|
report.ThreeDay: errors.New("distributor unavailable"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := RunBatchDetailed(context.Background(), BatchRequest{
|
||||||
|
Config: cfg,
|
||||||
|
Batch: BatchMorning,
|
||||||
|
Now: mustParse("2026-05-29T05:00:00-05:00"),
|
||||||
|
Renderer: &selectiveRenderer{runBody: "# Batch Report\n"},
|
||||||
|
Notifier: notifier,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RunBatchDetailed() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.Total != 3 || result.Succeeded != 2 || result.Failed != 1 {
|
||||||
|
t.Fatalf("summary total/succeeded/failed = %d/%d/%d, want 3/2/1", result.Total, result.Succeeded, result.Failed)
|
||||||
|
}
|
||||||
|
if len(notifier.requests) != 3 {
|
||||||
|
t.Fatalf("notification requests = %d, want one per generated report", len(notifier.requests))
|
||||||
|
}
|
||||||
|
var failedThreeDay bool
|
||||||
|
for _, item := range result.Reports {
|
||||||
|
if item.ReportID == report.ThreeDay {
|
||||||
|
if item.Status == "failed" && strings.Contains(item.Error, "notify report") && strings.Contains(item.Error, "distributor unavailable") {
|
||||||
|
failedThreeDay = true
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if item.Status != "succeeded" {
|
||||||
|
t.Fatalf("report %s status = %s, want succeeded", item.ReportID, item.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !failedThreeDay {
|
||||||
|
t.Fatalf("reports = %#v, want notification failure on 3-day item", result.Reports)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunBatchUsesOutputDirectory(t *testing.T) {
|
func TestRunBatchUsesOutputDirectory(t *testing.T) {
|
||||||
server := dailyBundleServer(t)
|
server := dailyBundleServer(t)
|
||||||
cfg := config.Defaults()
|
cfg := config.Defaults()
|
||||||
@@ -1164,6 +1414,14 @@ type recordingRenderer struct {
|
|||||||
runBody string
|
runBody string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func successfulRenderer(body string) *recordingRenderer {
|
||||||
|
return &recordingRenderer{
|
||||||
|
renderResult: &scriptorium.RenderResult{ExitCode: 0},
|
||||||
|
runResult: &scriptorium.RunResult{ExitCode: 0},
|
||||||
|
runBody: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type selectiveRenderer struct {
|
type selectiveRenderer struct {
|
||||||
renderCalls int
|
renderCalls int
|
||||||
runCalls int
|
runCalls int
|
||||||
@@ -1171,6 +1429,38 @@ type selectiveRenderer struct {
|
|||||||
runBody string
|
runBody string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type recordingNotifier struct {
|
||||||
|
requests []NotificationRequest
|
||||||
|
result *NotificationResult
|
||||||
|
err error
|
||||||
|
errByReport map[report.ID]error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *recordingNotifier) Notify(_ context.Context, req NotificationRequest) (*NotificationResult, error) {
|
||||||
|
n.requests = append(n.requests, req)
|
||||||
|
if err := n.errByReport[req.ReportID]; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n.err != nil {
|
||||||
|
return nil, n.err
|
||||||
|
}
|
||||||
|
if n.result != nil {
|
||||||
|
result := *n.result
|
||||||
|
if result.BundleID == "" {
|
||||||
|
result.BundleID = req.BundleID
|
||||||
|
}
|
||||||
|
if result.IdempotencyKey == "" {
|
||||||
|
result.IdempotencyKey = req.IdempotencyKey
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
return &NotificationResult{
|
||||||
|
BundleID: req.BundleID,
|
||||||
|
IdempotencyKey: req.IdempotencyKey,
|
||||||
|
Status: "accepted",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *selectiveRenderer) Render(_ context.Context, req scriptorium.RenderRequest) (*scriptorium.RenderResult, error) {
|
func (r *selectiveRenderer) Render(_ context.Context, req scriptorium.RenderRequest) (*scriptorium.RenderResult, error) {
|
||||||
r.renderCalls++
|
r.renderCalls++
|
||||||
if req.PromptID == r.failRenderPrompt {
|
if req.PromptID == r.failRenderPrompt {
|
||||||
|
|||||||
Reference in New Issue
Block a user