Stop persisting notification receipts
This commit is contained in:
@@ -94,7 +94,6 @@ type ReportResult struct {
|
||||
LLMDebugPath string
|
||||
ReportPath string
|
||||
OutputPath string
|
||||
NotificationPath string
|
||||
Metadata state.Metadata
|
||||
MetadataPath string
|
||||
GeneratedTextRawPath string
|
||||
@@ -121,7 +120,6 @@ type BatchNotificationResult struct {
|
||||
PipelineID string `json:"pipelineId,omitempty"`
|
||||
BundleID string `json:"bundleId,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
IncludedReports []BatchNotificationReport `json:"includedReports,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
@@ -144,7 +142,6 @@ type BatchReportResult struct {
|
||||
NotificationRunID string `json:"notificationRunId,omitempty"`
|
||||
NotificationPipelineID string `json:"notificationPipelineId,omitempty"`
|
||||
NotificationError string `json:"notificationError,omitempty"`
|
||||
NotificationPath string `json:"notificationPath,omitempty"`
|
||||
GeneratedAt time.Time `json:"generatedAt"`
|
||||
ValidPeriod timeutil.Period `json:"validPeriod"`
|
||||
DataPackagePath string `json:"dataPackagePath,omitempty"`
|
||||
@@ -387,7 +384,7 @@ func RunBatchDetailed(ctx context.Context, req BatchRequest) (*BatchResult, erro
|
||||
result.Reports = append(result.Reports, item)
|
||||
}
|
||||
result.Total = len(result.Reports)
|
||||
batchNotification, err := notifyBatch(ctx, req.Config, req.Batch, batchRunID(startedAt, req.Batch), startedAt, result, plannedReports, store, req.Notifier)
|
||||
batchNotification, err := notifyBatch(ctx, req.Config, req.Batch, batchRunID(startedAt, req.Batch), startedAt, result, plannedReports, req.Notifier)
|
||||
if batchNotification != nil {
|
||||
result.Notification = batchNotification
|
||||
}
|
||||
@@ -408,7 +405,6 @@ func copyBatchReportPaths(item *BatchReportResult, result *ReportResult) {
|
||||
item.ReportPath = result.ReportPath
|
||||
item.OutputPath = result.OutputPath
|
||||
item.MetadataPath = result.MetadataPath
|
||||
item.NotificationPath = result.NotificationPath
|
||||
if result.Notification != nil {
|
||||
item.NotificationStatus = result.Notification.Status
|
||||
item.NotificationRunID = result.Notification.RunID
|
||||
@@ -617,33 +613,32 @@ func FetchAndSaveBundle(ctx context.Context, req FetchBundleRequest) (*weatherda
|
||||
}
|
||||
|
||||
type finalizeRenderedReportRequest struct {
|
||||
Config config.Config
|
||||
Store state.Store
|
||||
Resolved report.Resolved
|
||||
Metadata state.Metadata
|
||||
MetadataPath string
|
||||
ExecutionArtifact *state.PromptExecutionArtifact
|
||||
ManagedReportPath string
|
||||
OutputPath string
|
||||
Notifier Notifier
|
||||
GenerationErr error
|
||||
noNotify bool
|
||||
Config config.Config
|
||||
Store state.Store
|
||||
Resolved report.Resolved
|
||||
Metadata state.Metadata
|
||||
MetadataPath string
|
||||
ExecutionArtifact *state.PromptExecutionArtifact
|
||||
RenderedReportPath string
|
||||
OutputPath string
|
||||
Notifier Notifier
|
||||
GenerationErr error
|
||||
noNotify bool
|
||||
}
|
||||
|
||||
type finalizeRenderedReportResult struct {
|
||||
OutputPath string
|
||||
NotificationPath string
|
||||
Metadata state.Metadata
|
||||
MetadataPath string
|
||||
Notification *NotificationResult
|
||||
OutputPath string
|
||||
Metadata state.Metadata
|
||||
MetadataPath string
|
||||
Notification *NotificationResult
|
||||
}
|
||||
|
||||
func finalizeRenderedReport(ctx context.Context, req finalizeRenderedReportRequest) (finalizeRenderedReportResult, error) {
|
||||
if req.Store == nil {
|
||||
return finalizeRenderedReportResult{}, fmt.Errorf("state store is required")
|
||||
}
|
||||
if req.ManagedReportPath == "" {
|
||||
return finalizeRenderedReportResult{}, fmt.Errorf("managed report path is required for report %q", req.Resolved.Definition.ID)
|
||||
if req.RenderedReportPath == "" {
|
||||
return finalizeRenderedReportResult{}, fmt.Errorf("rendered report path is required for report %q", req.Resolved.Definition.ID)
|
||||
}
|
||||
if req.ExecutionArtifact == nil {
|
||||
return finalizeRenderedReportResult{}, fmt.Errorf("prompt execution artifact is required for report %q", req.Resolved.Definition.ID)
|
||||
@@ -651,8 +646,8 @@ func finalizeRenderedReport(ctx context.Context, req finalizeRenderedReportReque
|
||||
|
||||
result := finalizeRenderedReportResult{Metadata: req.Metadata, MetadataPath: req.MetadataPath}
|
||||
if req.OutputPath != "" && req.GenerationErr == nil {
|
||||
if req.OutputPath != req.ManagedReportPath {
|
||||
if err := fileutil.CopyFileAtomic(req.ManagedReportPath, req.OutputPath); err != nil {
|
||||
if req.OutputPath != req.RenderedReportPath {
|
||||
if err := fileutil.CopyFileAtomic(req.RenderedReportPath, req.OutputPath); err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.OutputPath = req.OutputPath
|
||||
@@ -667,7 +662,7 @@ func finalizeRenderedReport(ctx context.Context, req finalizeRenderedReportReque
|
||||
}
|
||||
|
||||
metadata := req.Metadata
|
||||
metadata.RenderedReportPath = req.ManagedReportPath
|
||||
metadata.RenderedReportPath = req.RenderedReportPath
|
||||
metadataPath, err := req.Store.SaveMetadata(ctx, metadata)
|
||||
if err != nil {
|
||||
return result, err
|
||||
@@ -681,24 +676,7 @@ func finalizeRenderedReport(ctx context.Context, req finalizeRenderedReportReque
|
||||
return result, nil
|
||||
}
|
||||
|
||||
notification, notificationPath, err := notifyReport(ctx, req.Config, req.Resolved, req.OutputPath, metadata, req.Notifier, req.Store)
|
||||
if notificationPath != "" {
|
||||
result.NotificationPath = notificationPath
|
||||
result.Notification = notification
|
||||
metadata.NotificationPath = notificationPath
|
||||
result.Metadata = metadata
|
||||
if saveErr := persistReachedPromptPath(ctx, req.Store, req.Resolved, req.ExecutionArtifact, func(paths *state.PromptExecutionPaths) {
|
||||
paths.NotificationPath = notificationPath
|
||||
}); saveErr != nil {
|
||||
return result, saveErr
|
||||
}
|
||||
metadataPath, saveErr := req.Store.SaveMetadata(ctx, metadata)
|
||||
if saveErr != nil {
|
||||
return result, saveErr
|
||||
}
|
||||
result.Metadata = metadata
|
||||
result.MetadataPath = metadataPath
|
||||
}
|
||||
notification, err := notifyReport(ctx, req.Config, req.Resolved, req.OutputPath, metadata.RunID, metadata.GeneratedAt, req.Notifier)
|
||||
result.Notification = notification
|
||||
if err != nil {
|
||||
return result, err
|
||||
@@ -706,31 +684,23 @@ func finalizeRenderedReport(ctx context.Context, req finalizeRenderedReportReque
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func notifyReport(ctx context.Context, cfg config.Config, resolved report.Resolved, reportPath string, metadata state.Metadata, notifier Notifier, store state.Store) (*NotificationResult, string, error) {
|
||||
func notifyReport(ctx context.Context, cfg config.Config, resolved report.Resolved, outputPath, runID string, generatedAt time.Time, notifier Notifier) (*NotificationResult, error) {
|
||||
notifier, enabled := reportNotifier(cfg, notifier)
|
||||
if !enabled {
|
||||
return nil, "", nil
|
||||
return nil, nil
|
||||
}
|
||||
notificationRequest, err := buildNotificationRequest(cfg, resolved, reportPath, metadata)
|
||||
notificationRequest, err := buildNotificationRequest(cfg, resolved, outputPath, runID, generatedAt)
|
||||
if err != nil {
|
||||
notificationPath, saveErr := saveNotificationArtifact(ctx, store, resolved, cfg, metadata, NotificationRequest{}, nil, err)
|
||||
if saveErr != nil {
|
||||
return nil, "", saveErr
|
||||
}
|
||||
return nil, notificationPath, err
|
||||
return nil, err
|
||||
}
|
||||
result, err := notifier.Notify(ctx, notificationRequest)
|
||||
notificationPath, saveErr := saveNotificationArtifact(ctx, store, resolved, cfg, metadata, notificationRequest, result, err)
|
||||
if saveErr != nil {
|
||||
return nil, "", saveErr
|
||||
}
|
||||
if err != nil {
|
||||
return result, notificationPath, &NotificationError{
|
||||
return result, &NotificationError{
|
||||
Request: notificationRequest,
|
||||
Err: fmt.Errorf("notify report %q run %q from output %q: %w", resolved.Definition.ID, metadata.RunID, reportPath, err),
|
||||
Err: fmt.Errorf("notify report %q run %q from output %q: %w", resolved.Definition.ID, runID, outputPath, err),
|
||||
}
|
||||
}
|
||||
return result, notificationPath, nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func reportNotifier(cfg config.Config, notifier Notifier) (Notifier, bool) {
|
||||
@@ -745,8 +715,8 @@ func reportNotifier(cfg config.Config, notifier Notifier) (Notifier, bool) {
|
||||
}, true
|
||||
}
|
||||
|
||||
func buildNotificationRequest(cfg config.Config, resolved report.Resolved, reportPath string, metadata state.Metadata) (NotificationRequest, error) {
|
||||
values, err := distributorTemplateValuesForReport(cfg, resolved, metadata.RunID, filepath.Base(reportPath))
|
||||
func buildNotificationRequest(cfg config.Config, resolved report.Resolved, outputPath, runID string, generatedAt time.Time) (NotificationRequest, error) {
|
||||
values, err := distributorTemplateValuesForReport(cfg, resolved, runID, filepath.Base(outputPath))
|
||||
if err != nil {
|
||||
return NotificationRequest{}, err
|
||||
}
|
||||
@@ -763,19 +733,19 @@ func buildNotificationRequest(cfg config.Config, resolved report.Resolved, repor
|
||||
if err != nil {
|
||||
return NotificationRequest{}, err
|
||||
}
|
||||
bundlePaths, err := renderDistributorReportBundlePaths(cfg, resolved, metadata.RunID, reportPath, values)
|
||||
bundlePaths, err := renderDistributorReportBundlePaths(cfg, resolved, runID, outputPath, values)
|
||||
if err != nil {
|
||||
return NotificationRequest{}, err
|
||||
}
|
||||
return NotificationRequest{
|
||||
ReportID: resolved.Definition.ID,
|
||||
RunID: metadata.RunID,
|
||||
RunID: runID,
|
||||
PipelineID: pipelineID,
|
||||
BundleID: bundleID,
|
||||
IdempotencyKey: idempotencyKey,
|
||||
ReportPath: reportPath,
|
||||
ReportPath: outputPath,
|
||||
BundlePaths: bundlePaths,
|
||||
CreatedAt: metadata.GeneratedAt,
|
||||
CreatedAt: generatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -849,54 +819,6 @@ func addDistributorValidPeriodValues(values *config.DistributorTemplateValues, p
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveNotificationArtifact(ctx context.Context, store state.Store, resolved report.Resolved, cfg config.Config, metadata state.Metadata, req NotificationRequest, result *NotificationResult, notifyErr error) (string, error) {
|
||||
if store == nil {
|
||||
return "", fmt.Errorf("state store is required")
|
||||
}
|
||||
artifact := state.DistributorNotificationArtifact{
|
||||
SchemaVersion: state.DistributorNotificationSchemaVersion,
|
||||
RunID: metadata.RunID,
|
||||
ReportID: resolved.Definition.ID,
|
||||
AttemptedAt: time.Now(),
|
||||
Endpoint: cfg.Notify.Distributor.Endpoint,
|
||||
PipelineID: req.PipelineID,
|
||||
BundleID: req.BundleID,
|
||||
IdempotencyKey: req.IdempotencyKey,
|
||||
SourcePath: req.ReportPath,
|
||||
BundlePaths: append([]string(nil), req.BundlePaths...),
|
||||
BundleCreated: req.CreatedAt,
|
||||
Status: "attempted",
|
||||
}
|
||||
if result != nil {
|
||||
artifact.Status = result.Status
|
||||
artifact.Upload = &state.DistributorUploadResult{
|
||||
RunID: result.RunID,
|
||||
Status: result.UploadStatus,
|
||||
}
|
||||
if result.PipelineID != "" || !result.AcceptedAt.IsZero() || result.StartedAt != nil || result.FinishedAt != nil || len(result.Report) > 0 || result.Error != "" {
|
||||
artifact.RunStatus = &state.DistributorRunStatus{
|
||||
RunID: result.RunID,
|
||||
PipelineID: result.PipelineID,
|
||||
Status: result.Status,
|
||||
AcceptedAt: result.AcceptedAt,
|
||||
StartedAt: result.StartedAt,
|
||||
FinishedAt: result.FinishedAt,
|
||||
Report: append([]byte(nil), result.Report...),
|
||||
Error: result.Error,
|
||||
}
|
||||
}
|
||||
artifact.StatusError = result.StatusError
|
||||
}
|
||||
if notifyErr != nil {
|
||||
artifact.Status = "failed"
|
||||
artifact.Error = notifyErr.Error()
|
||||
}
|
||||
if artifact.Status == "" {
|
||||
artifact.Status = "unknown"
|
||||
}
|
||||
return store.SaveDistributorNotification(ctx, resolved, artifact)
|
||||
}
|
||||
|
||||
type noopNotifier struct{}
|
||||
|
||||
func (noopNotifier) Notify(context.Context, NotificationRequest) (*NotificationResult, error) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
distributoradapter "gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/distributor"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/config"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/state"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
|
||||
)
|
||||
|
||||
@@ -47,7 +46,7 @@ func batchRunID(startedAt time.Time, batch BatchKind) string {
|
||||
return startedAt.UTC().Format(runIDTimestampLayout) + "_" + string(batch)
|
||||
}
|
||||
|
||||
func notifyBatch(ctx context.Context, cfg config.Config, batch BatchKind, runID string, startedAt time.Time, result *BatchResult, planned []plannedBatchReport, store state.Store, notifier Notifier) (*BatchNotificationResult, error) {
|
||||
func notifyBatch(ctx context.Context, cfg config.Config, batch BatchKind, runID string, startedAt time.Time, result *BatchResult, planned []plannedBatchReport, notifier Notifier) (*BatchNotificationResult, error) {
|
||||
if !cfg.Notify.Distributor.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -66,20 +65,12 @@ func notifyBatch(ctx context.Context, cfg config.Config, batch BatchKind, runID
|
||||
|
||||
req, err := buildBatchNotificationRequest(cfg, batch, runID, startedAt, result.Reports, planned)
|
||||
if err != nil {
|
||||
path, saveErr := saveBatchNotificationArtifact(ctx, store, cfg, batch, runID, startedAt, batchNotificationRequest{}, nil, err)
|
||||
if saveErr != nil {
|
||||
return nil, saveErr
|
||||
}
|
||||
return failedBatchNotificationResult(batchNotificationRequest{}, path, err), err
|
||||
return failedBatchNotificationResult(batchNotificationRequest{}, err), err
|
||||
}
|
||||
|
||||
batchNotifier, err := resolveBatchNotifier(cfg, notifier)
|
||||
if err != nil {
|
||||
path, saveErr := saveBatchNotificationArtifact(ctx, store, cfg, batch, runID, startedAt, req, nil, err)
|
||||
if saveErr != nil {
|
||||
return nil, saveErr
|
||||
}
|
||||
return failedBatchNotificationResult(req, path, err), err
|
||||
return failedBatchNotificationResult(req, err), err
|
||||
}
|
||||
|
||||
notification, notifyErr := batchNotifier.NotifyBatch(ctx, req)
|
||||
@@ -87,12 +78,7 @@ func notifyBatch(ctx context.Context, cfg config.Config, batch BatchKind, runID
|
||||
if notifyErr != nil {
|
||||
wrappedErr = fmt.Errorf("notify batch %q run %q bundle %q: %w", batch, runID, req.BundleID, notifyErr)
|
||||
}
|
||||
path, saveErr := saveBatchNotificationArtifact(ctx, store, cfg, batch, runID, startedAt, req, notification, wrappedErr)
|
||||
if saveErr != nil {
|
||||
return nil, saveErr
|
||||
}
|
||||
|
||||
batchResult := batchNotificationResult(req, notification, path)
|
||||
batchResult := batchNotificationResult(req, notification)
|
||||
if wrappedErr != nil {
|
||||
batchResult.Status = "failed"
|
||||
batchResult.Error = wrappedErr.Error()
|
||||
@@ -226,13 +212,12 @@ func batchDistributorUploadRequest(req batchNotificationRequest) distributoradap
|
||||
}
|
||||
}
|
||||
|
||||
func batchNotificationResult(req batchNotificationRequest, result *NotificationResult, path string) *BatchNotificationResult {
|
||||
func batchNotificationResult(req batchNotificationRequest, result *NotificationResult) *BatchNotificationResult {
|
||||
notification := &BatchNotificationResult{
|
||||
Status: "unknown",
|
||||
PipelineID: req.PipelineID,
|
||||
BundleID: req.BundleID,
|
||||
IdempotencyKey: req.IdempotencyKey,
|
||||
Path: path,
|
||||
IncludedReports: append([]BatchNotificationReport(nil), req.IncludedReports...),
|
||||
}
|
||||
if result != nil {
|
||||
@@ -257,8 +242,8 @@ func batchNotificationResult(req batchNotificationRequest, result *NotificationR
|
||||
return notification
|
||||
}
|
||||
|
||||
func failedBatchNotificationResult(req batchNotificationRequest, path string, err error) *BatchNotificationResult {
|
||||
notification := batchNotificationResult(req, nil, path)
|
||||
func failedBatchNotificationResult(req batchNotificationRequest, err error) *BatchNotificationResult {
|
||||
notification := batchNotificationResult(req, nil)
|
||||
notification.Status = "failed"
|
||||
if err != nil {
|
||||
notification.Error = err.Error()
|
||||
@@ -266,78 +251,6 @@ func failedBatchNotificationResult(req batchNotificationRequest, path string, er
|
||||
return notification
|
||||
}
|
||||
|
||||
func saveBatchNotificationArtifact(ctx context.Context, store state.Store, cfg config.Config, batch BatchKind, runID string, startedAt time.Time, req batchNotificationRequest, result *NotificationResult, notifyErr error) (string, error) {
|
||||
if store == nil {
|
||||
return "", fmt.Errorf("state store is required")
|
||||
}
|
||||
location, err := timeutil.LoadLocation(cfg.WeatherAPI.Timezone)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load batch notification timezone: %w", err)
|
||||
}
|
||||
artifact := state.BatchDistributorNotificationArtifact{
|
||||
SchemaVersion: state.BatchDistributorNotificationSchemaVersion,
|
||||
Batch: string(batch),
|
||||
BatchRunID: runID,
|
||||
AttemptedAt: time.Now(),
|
||||
Endpoint: cfg.Notify.Distributor.Endpoint,
|
||||
PipelineID: req.PipelineID,
|
||||
BundleID: req.BundleID,
|
||||
IdempotencyKey: req.IdempotencyKey,
|
||||
BundleCreated: req.CreatedAt,
|
||||
Reports: batchNotificationReportArtifacts(req.IncludedReports),
|
||||
Status: "attempted",
|
||||
}
|
||||
if result != nil {
|
||||
artifact.Status = result.Status
|
||||
artifact.Upload = &state.DistributorUploadResult{
|
||||
RunID: result.RunID,
|
||||
Status: result.UploadStatus,
|
||||
}
|
||||
if result.PipelineID != "" || !result.AcceptedAt.IsZero() || result.StartedAt != nil || result.FinishedAt != nil || len(result.Report) > 0 || result.Error != "" {
|
||||
artifact.RunStatus = &state.DistributorRunStatus{
|
||||
RunID: result.RunID,
|
||||
PipelineID: result.PipelineID,
|
||||
Status: result.Status,
|
||||
AcceptedAt: result.AcceptedAt,
|
||||
StartedAt: result.StartedAt,
|
||||
FinishedAt: result.FinishedAt,
|
||||
Report: append([]byte(nil), result.Report...),
|
||||
Error: result.Error,
|
||||
}
|
||||
}
|
||||
artifact.StatusError = result.StatusError
|
||||
}
|
||||
if notifyErr != nil {
|
||||
artifact.Status = "failed"
|
||||
artifact.Error = notifyErr.Error()
|
||||
}
|
||||
if artifact.Status == "" {
|
||||
artifact.Status = "unknown"
|
||||
}
|
||||
return store.SaveBatchDistributorNotification(ctx, state.BatchDistributorNotificationRef{
|
||||
Batch: string(batch),
|
||||
BatchRunID: runID,
|
||||
StartedAt: startedAt,
|
||||
Location: location,
|
||||
}, artifact)
|
||||
}
|
||||
|
||||
func batchNotificationReportArtifacts(reports []BatchNotificationReport) []state.BatchDistributorNotificationReportArtifact {
|
||||
if len(reports) == 0 {
|
||||
return nil
|
||||
}
|
||||
artifacts := make([]state.BatchDistributorNotificationReportArtifact, 0, len(reports))
|
||||
for _, item := range reports {
|
||||
artifacts = append(artifacts, state.BatchDistributorNotificationReportArtifact{
|
||||
ReportID: item.ReportID,
|
||||
RunID: item.RunID,
|
||||
SourcePath: item.SourcePath,
|
||||
BundlePaths: append([]string(nil), item.BundlePaths...),
|
||||
})
|
||||
}
|
||||
return artifacts
|
||||
}
|
||||
|
||||
func renderBatchNotificationIdentity(cfg config.Config, batch BatchKind, runID string, startedAt time.Time) (batchNotificationIdentity, error) {
|
||||
values, err := batchNotificationTemplateValues(cfg, batch, runID, startedAt)
|
||||
if err != nil {
|
||||
|
||||
@@ -169,12 +169,12 @@ func TestRunBatchDetailedExecutesRetainedReportsSequentially(t *testing.T) {
|
||||
t.Fatalf("output = %q, want %q", item.OutputPath, test.wantCopies[index])
|
||||
}
|
||||
assertBatchPathsExist(t, item.DataPackagePath, item.PreparationPath, item.ExecutionPath, item.LLMDebugPath, item.ReportPath, item.OutputPath, item.MetadataPath)
|
||||
managed, readErr := os.ReadFile(item.ReportPath)
|
||||
renderedReport, readErr := os.ReadFile(item.ReportPath)
|
||||
if readErr != nil {
|
||||
t.Fatalf("read managed report: %v", readErr)
|
||||
t.Fatalf("read rendered report: %v", readErr)
|
||||
}
|
||||
copied, readErr := os.ReadFile(item.OutputPath)
|
||||
if readErr != nil || !bytes.Equal(managed, copied) {
|
||||
if readErr != nil || !bytes.Equal(renderedReport, copied) {
|
||||
t.Fatalf("output mismatch/error = %v", readErr)
|
||||
}
|
||||
}
|
||||
@@ -263,15 +263,12 @@ func TestRunBatchDetailedNotificationLifecycle(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("RunBatchDetailed() error = %v", err)
|
||||
}
|
||||
if result.Failed != 0 || result.Notification == nil || result.Notification.Status != "succeeded" || result.Notification.Path == "" || len(notifier.reportRequests) != 0 || len(notifier.batchRequests) != 1 {
|
||||
if result.Failed != 0 || result.Notification == nil || result.Notification.Status != "succeeded" || len(notifier.reportRequests) != 0 || len(notifier.batchRequests) != 1 {
|
||||
t.Fatalf("notification result/requests = %#v/%d/%d", result.Notification, len(notifier.reportRequests), len(notifier.batchRequests))
|
||||
}
|
||||
outputPaths := make(map[string]struct{}, len(result.Reports))
|
||||
for _, item := range result.Reports {
|
||||
outputPaths[item.OutputPath] = struct{}{}
|
||||
if item.NotificationPath != "" {
|
||||
t.Fatalf("report item contains per-report notification path: %#v", item)
|
||||
}
|
||||
}
|
||||
request := notifier.batchRequests[0]
|
||||
if len(request.IncludedReports) != len(result.Reports) {
|
||||
@@ -282,10 +279,6 @@ func TestRunBatchDetailedNotificationLifecycle(t *testing.T) {
|
||||
t.Fatalf("notification file = %#v, want selected Markdown output source", file)
|
||||
}
|
||||
}
|
||||
artifact := readBatchNotificationArtifact(t, result.Notification.Path)
|
||||
if artifact.Status != "succeeded" || artifact.Upload == nil || artifact.Upload.RunID != "batch-notification-run" || artifact.RunStatus == nil || len(artifact.Reports) != len(result.Reports) {
|
||||
t.Fatalf("notification artifact = %#v", artifact)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("upload failure", func(t *testing.T) {
|
||||
@@ -299,7 +292,7 @@ func TestRunBatchDetailedNotificationLifecycle(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("RunBatchDetailed() error = %v", err)
|
||||
}
|
||||
if result.Succeeded != 2 || result.Failed != 1 || result.Notification == nil || result.Notification.Status != "failed" || result.Notification.Path == "" {
|
||||
if result.Succeeded != 2 || result.Failed != 1 || result.Notification == nil || result.Notification.Status != "failed" {
|
||||
t.Fatalf("result = %#v, want successful reports and failed notification", result)
|
||||
}
|
||||
for _, item := range result.Reports {
|
||||
@@ -307,9 +300,8 @@ func TestRunBatchDetailedNotificationLifecycle(t *testing.T) {
|
||||
t.Fatalf("report item = %#v, want success despite notification failure", item)
|
||||
}
|
||||
}
|
||||
artifact := readBatchNotificationArtifact(t, result.Notification.Path)
|
||||
if artifact.Status != "failed" || !strings.Contains(artifact.Error, "batch upload rejected") {
|
||||
t.Fatalf("notification artifact = %#v", artifact)
|
||||
if !strings.Contains(result.Notification.Error, "batch upload rejected") {
|
||||
t.Fatalf("notification error = %#v", result.Notification)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -324,12 +316,11 @@ func TestRunBatchDetailedNotificationLifecycle(t *testing.T) {
|
||||
Config: cfg, Batch: BatchEvening, Now: workflowTime("2026-05-29T18:00:00-05:00"), WorkingDir: t.TempDir(),
|
||||
Collector: &workflowCollector{result: &collect.Result{Bundle: &bundle}}, Executor: newAssembledBatchExecutor(), Notifier: notifier,
|
||||
})
|
||||
if err != nil || result.Failed != 0 || result.Notification == nil || result.Notification.Path == "" {
|
||||
if err != nil || result.Failed != 0 || result.Notification == nil {
|
||||
t.Fatalf("result/error = %#v/%v", result, err)
|
||||
}
|
||||
artifact := readBatchNotificationArtifact(t, result.Notification.Path)
|
||||
if artifact.StatusError != "status lookup unavailable" || artifact.RunStatus == nil || !bytes.Contains(artifact.RunStatus.Report, []byte("replace_older")) {
|
||||
t.Fatalf("notification artifact = %#v", artifact)
|
||||
if result.Notification.Status != "accepted" || result.Notification.RunID != "batch-notification-run" {
|
||||
t.Fatalf("notification = %#v", result.Notification)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -439,25 +430,11 @@ func assertBatchItemMatchesMetadata(t *testing.T, item BatchReportResult) {
|
||||
}
|
||||
if item.ReportID != metadata.ReportID || item.RunID != metadata.RunID ||
|
||||
item.DataPackagePath != metadata.DataPackagePath || item.PreparationPath != metadata.PreparationPath ||
|
||||
item.ExecutionPath != metadata.ExecutionPath || item.ReportPath != metadata.RenderedReportPath ||
|
||||
item.NotificationPath != metadata.NotificationPath {
|
||||
item.ExecutionPath != metadata.ExecutionPath || item.ReportPath != metadata.RenderedReportPath {
|
||||
t.Fatalf("batch item paths do not exactly match metadata: item=%#v metadata=%#v", item, metadata)
|
||||
}
|
||||
}
|
||||
|
||||
func readBatchNotificationArtifact(t *testing.T, path string) state.BatchDistributorNotificationArtifact {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read batch notification artifact: %v", err)
|
||||
}
|
||||
var artifact state.BatchDistributorNotificationArtifact
|
||||
if err := json.Unmarshal(data, &artifact); err != nil {
|
||||
t.Fatalf("decode batch notification artifact: %v", err)
|
||||
}
|
||||
return artifact
|
||||
}
|
||||
|
||||
func loadBatchDataPackage(t *testing.T, path string) promptinput.Package {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(path)
|
||||
|
||||
@@ -20,12 +20,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
failPromptExecution = "prompt execution"
|
||||
failMetadata = "metadata"
|
||||
failGeneratedText = "generated text"
|
||||
failRenderContext = "render context"
|
||||
failRenderedReportPath = "rendered report path"
|
||||
failDistributorNotification = "distributor notification"
|
||||
failPromptExecution = "prompt execution"
|
||||
failMetadata = "metadata"
|
||||
failGeneratedText = "generated text"
|
||||
failRenderContext = "render context"
|
||||
failRenderedReportPath = "rendered report path"
|
||||
)
|
||||
|
||||
type failingPersistenceStore struct {
|
||||
@@ -67,13 +66,6 @@ func (s *failingPersistenceStore) PrepareRenderedReport(ctx context.Context, res
|
||||
return s.Store.PrepareRenderedReport(ctx, resolved)
|
||||
}
|
||||
|
||||
func (s *failingPersistenceStore) SaveDistributorNotification(ctx context.Context, resolved report.Resolved, artifact state.DistributorNotificationArtifact) (string, error) {
|
||||
if s.failOperation == failDistributorNotification {
|
||||
return "", errors.New("injected notification persistence failure")
|
||||
}
|
||||
return s.Store.SaveDistributorNotification(ctx, resolved, artifact)
|
||||
}
|
||||
|
||||
func (s *failingPersistenceStore) SaveMetadata(ctx context.Context, metadata state.Metadata) (string, error) {
|
||||
s.metadataCalls++
|
||||
if s.failOperation == failMetadata && s.metadataCalls == s.failMetadataCall {
|
||||
@@ -151,9 +143,8 @@ func TestGeneratePromptReportReturnsOnlyReachedArtifactPaths(t *testing.T) {
|
||||
{name: "execution then metadata", failOperation: failMetadata, failMetadataCall: 2, want: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true}},
|
||||
{name: "normalized output then metadata", failOperation: failMetadata, failMetadataCall: 3, want: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true}},
|
||||
{name: "render context then metadata", failOperation: failMetadata, failMetadataCall: 4, want: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true}},
|
||||
{name: "managed report then metadata", failOperation: failMetadata, failMetadataCall: 5, want: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true}},
|
||||
{name: "rendered report then metadata", failOperation: failMetadata, failMetadataCall: 5, want: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true}},
|
||||
{name: "output then metadata", failOperation: failMetadata, failMetadataCall: 5, output: true, want: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true, output: true}},
|
||||
{name: "notification then metadata", failOperation: failMetadata, failMetadataCall: 6, notify: true, want: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true, notification: true}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -250,7 +241,7 @@ func TestCompletedExecutionArtifactTracksDownstreamLifecycle(t *testing.T) {
|
||||
want := state.PromptExecutionPaths{
|
||||
RawOutputPath: paths.GeneratedTextRaw, GeneratedTextPath: paths.GeneratedText,
|
||||
RenderContextPath: paths.RenderContext, RenderedReportPath: paths.RenderedReport,
|
||||
OutputPath: paths.output, NotificationPath: paths.Notification,
|
||||
OutputPath: paths.output,
|
||||
}
|
||||
assertPersistedExecutionPaths(t, req.Store, result.ExecutionPath, want)
|
||||
|
||||
@@ -314,12 +305,12 @@ func TestCompletedExecutionArtifactRetainsLastPersistedCheckpoint(t *testing.T)
|
||||
wantResult: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true},
|
||||
},
|
||||
{
|
||||
name: "managed report write", failOperation: failRenderedReportPath,
|
||||
name: "rendered report write", failOperation: failRenderedReportPath,
|
||||
wantExecution: reachedExecutionArtifacts{raw: true, normalized: true, renderContext: true},
|
||||
wantResult: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true},
|
||||
},
|
||||
{
|
||||
name: "managed report checkpoint", failOperation: failPromptExecution, failExecutionCall: 4,
|
||||
name: "rendered report checkpoint", failOperation: failPromptExecution, failExecutionCall: 4,
|
||||
wantExecution: reachedExecutionArtifacts{raw: true, normalized: true, renderContext: true},
|
||||
wantResult: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true},
|
||||
},
|
||||
@@ -339,25 +330,10 @@ func TestCompletedExecutionArtifactRetainsLastPersistedCheckpoint(t *testing.T)
|
||||
wantResult: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true, output: true},
|
||||
},
|
||||
{
|
||||
name: "notification artifact write", failOperation: failDistributorNotification, requestOutput: true, notify: true,
|
||||
name: "notification operation", requestOutput: true, notify: true, notificationFailure: true,
|
||||
wantExecution: reachedExecutionArtifacts{raw: true, normalized: true, renderContext: true, report: true, output: true},
|
||||
wantResult: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true, output: true},
|
||||
},
|
||||
{
|
||||
name: "notification checkpoint", failOperation: failPromptExecution, failExecutionCall: 6, requestOutput: true, notify: true,
|
||||
wantExecution: reachedExecutionArtifacts{raw: true, normalized: true, renderContext: true, report: true, output: true},
|
||||
wantResult: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true, output: true, notification: true},
|
||||
},
|
||||
{
|
||||
name: "notification metadata", failOperation: failMetadata, failMetadataCall: 6, requestOutput: true, notify: true,
|
||||
wantExecution: reachedExecutionArtifacts{raw: true, normalized: true, renderContext: true, report: true, output: true, notification: true},
|
||||
wantResult: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true, output: true, notification: true},
|
||||
},
|
||||
{
|
||||
name: "notification operation", requestOutput: true, notify: true, notificationFailure: true,
|
||||
wantExecution: reachedExecutionArtifacts{raw: true, normalized: true, renderContext: true, report: true, output: true, notification: true},
|
||||
wantResult: reachedPromptArtifacts{preparation: true, execution: true, metadata: true, raw: true, normalized: true, renderContext: true, report: true, output: true, notification: true},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -409,7 +385,6 @@ type reachedExecutionArtifacts struct {
|
||||
renderContext bool
|
||||
report bool
|
||||
output bool
|
||||
notification bool
|
||||
}
|
||||
|
||||
func executionPathsFor(paths promptArtifactPaths, reached reachedExecutionArtifacts) state.PromptExecutionPaths {
|
||||
@@ -429,9 +404,6 @@ func executionPathsFor(paths promptArtifactPaths, reached reachedExecutionArtifa
|
||||
if reached.output {
|
||||
result.OutputPath = paths.output
|
||||
}
|
||||
if reached.notification {
|
||||
result.NotificationPath = paths.Notification
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -466,7 +438,6 @@ type reachedPromptArtifacts struct {
|
||||
renderContext bool
|
||||
report bool
|
||||
output bool
|
||||
notification bool
|
||||
}
|
||||
|
||||
func promptArtifactRequest(t *testing.T, executor promptexec.Executor) (promptReportRequest, promptArtifactPaths) {
|
||||
@@ -534,7 +505,6 @@ func assertReachedPromptArtifacts(t *testing.T, result *ReportResult, paths prom
|
||||
{"render context", result.RenderContextPath, result.Metadata.RenderContextPath, true, paths.RenderContext, want.renderContext},
|
||||
{"report", result.ReportPath, result.Metadata.RenderedReportPath, true, paths.RenderedReport, want.report},
|
||||
{"output", result.OutputPath, "", false, paths.output, want.output},
|
||||
{"notification", result.NotificationPath, result.Metadata.NotificationPath, true, paths.Notification, want.notification},
|
||||
}
|
||||
for _, check := range checks {
|
||||
if check.want && check.got != check.path {
|
||||
|
||||
@@ -344,10 +344,10 @@ func (w *promptReportWorkflow) finalizeReport(rendered []byte) (*ReportResult, e
|
||||
}
|
||||
finalized, err := finalizeRenderedReport(w.ctx, finalizeRenderedReportRequest{
|
||||
Config: w.req.Config, Store: w.store, Resolved: w.req.Resolved, Metadata: w.metadata, MetadataPath: w.result.MetadataPath,
|
||||
ExecutionArtifact: &w.executionArtifact, ManagedReportPath: reportPath, OutputPath: w.req.OutputPath,
|
||||
ExecutionArtifact: &w.executionArtifact, RenderedReportPath: reportPath, OutputPath: w.req.OutputPath,
|
||||
Notifier: w.req.Notifier, noNotify: w.req.noNotify,
|
||||
})
|
||||
w.result.OutputPath, w.result.NotificationPath = finalized.OutputPath, finalized.NotificationPath
|
||||
w.result.OutputPath = finalized.OutputPath
|
||||
w.result.Metadata, w.result.MetadataPath, w.result.Notification = finalized.Metadata, finalized.MetadataPath, finalized.Notification
|
||||
return w.result, err
|
||||
}
|
||||
|
||||
@@ -197,12 +197,12 @@ func TestGenerateDetailedCompletesRetainedReportWorkflows(t *testing.T) {
|
||||
if !bytes.Equal(executor.request.DataPackage, persisted) {
|
||||
t.Fatal("executor data package differs from exact persisted YAML bytes")
|
||||
}
|
||||
managed, readErr := os.ReadFile(result.ReportPath)
|
||||
if readErr != nil || !strings.Contains(string(managed), test.wantOutput) {
|
||||
t.Fatalf("managed report = %q, error %v, want generated template output %q", managed, readErr, test.wantOutput)
|
||||
renderedReport, readErr := os.ReadFile(result.ReportPath)
|
||||
if readErr != nil || !strings.Contains(string(renderedReport), test.wantOutput) {
|
||||
t.Fatalf("rendered report = %q, error %v, want generated template output %q", renderedReport, readErr, test.wantOutput)
|
||||
}
|
||||
copied, readErr := os.ReadFile(outputPath)
|
||||
if readErr != nil || !bytes.Equal(copied, managed) || result.OutputPath != outputPath {
|
||||
if readErr != nil || !bytes.Equal(copied, renderedReport) || result.OutputPath != outputPath {
|
||||
t.Fatalf("output mismatch/error/path = %v/%q", readErr, result.OutputPath)
|
||||
}
|
||||
if len(notifier.requests) != 1 || notifier.requests[0].ReportPath != outputPath {
|
||||
@@ -217,9 +217,9 @@ func TestGenerateDetailedCompletesRetainedReportWorkflows(t *testing.T) {
|
||||
if strings.Join(notifier.requests[0].BundlePaths, "\n") != strings.Join(wantBundlePaths, "\n") {
|
||||
t.Fatalf("bundle paths = %#v, want %#v", notifier.requests[0].BundlePaths, wantBundlePaths)
|
||||
}
|
||||
managedName := filepath.Base(result.ReportPath)
|
||||
if !strings.HasPrefix(managedName, "report.") || !strings.Contains(managedName, "_"+test.name) || !strings.HasSuffix(managedName, ".md") || filepath.Base(result.OutputPath) != test.name+".md" {
|
||||
t.Fatalf("output names = managed %q copy %q", result.ReportPath, result.OutputPath)
|
||||
renderedName := filepath.Base(result.ReportPath)
|
||||
if !strings.HasPrefix(renderedName, "report.") || !strings.Contains(renderedName, "_"+test.name) || !strings.HasSuffix(renderedName, ".md") || filepath.Base(result.OutputPath) != test.name+".md" {
|
||||
t.Fatalf("output names = rendered %q copy %q", result.ReportPath, result.OutputPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -465,7 +465,6 @@ func TestGenerateDetailedRetainsInspectableArtifactsAfterApplicationFailures(t *
|
||||
wantContext bool
|
||||
wantReport bool
|
||||
wantOutput bool
|
||||
wantNotify bool
|
||||
}{
|
||||
{name: "generated text decode", raw: `{`, wantRaw: true},
|
||||
{name: "generated text domain", raw: `{}`, wantRaw: true},
|
||||
@@ -484,7 +483,7 @@ func TestGenerateDetailedRetainsInspectableArtifactsAfterApplicationFailures(t *
|
||||
}, wantRaw: true, wantNormalized: true, wantContext: true},
|
||||
{name: "notification", raw: validDailyWorkflowJSON(), configure: func(_ *GenerateRequest, notifier *workflowNotifier) {
|
||||
notifier.err = errors.New("notification rejected")
|
||||
}, wantRaw: true, wantNormalized: true, wantContext: true, wantReport: true, wantOutput: true, wantNotify: true},
|
||||
}, wantRaw: true, wantNormalized: true, wantContext: true, wantReport: true, wantOutput: true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -512,8 +511,8 @@ func TestGenerateDetailedRetainsInspectableArtifactsAfterApplicationFailures(t *
|
||||
}
|
||||
if (result.GeneratedTextRawPath != "") != test.wantRaw || (result.GeneratedTextPath != "") != test.wantNormalized ||
|
||||
(result.RenderContextPath != "") != test.wantContext || (result.ReportPath != "") != test.wantReport ||
|
||||
(result.OutputPath != "") != test.wantOutput || (result.NotificationPath != "") != test.wantNotify {
|
||||
t.Fatalf("reached paths = raw %q normalized %q context %q report %q output %q notification %q", result.GeneratedTextRawPath, result.GeneratedTextPath, result.RenderContextPath, result.ReportPath, result.OutputPath, result.NotificationPath)
|
||||
(result.OutputPath != "") != test.wantOutput {
|
||||
t.Fatalf("reached paths = raw %q normalized %q context %q report %q output %q", result.GeneratedTextRawPath, result.GeneratedTextPath, result.RenderContextPath, result.ReportPath, result.OutputPath)
|
||||
}
|
||||
if test.wantRaw {
|
||||
persisted, readErr := os.ReadFile(result.GeneratedTextRawPath)
|
||||
@@ -524,6 +523,9 @@ func TestGenerateDetailedRetainsInspectableArtifactsAfterApplicationFailures(t *
|
||||
if test.name == "notification" && (len(notifier.requests) != 1 || notifier.requests[0].ReportPath != result.OutputPath) {
|
||||
t.Fatalf("notification requests = %#v", notifier.requests)
|
||||
}
|
||||
if test.name == "notification" && result.Metadata.NotificationPath != "" {
|
||||
t.Fatalf("notification metadata retains a receipt path: %#v", result.Metadata)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +63,6 @@ func writeBatchStatus(stderr io.Writer, result *app.BatchResult) {
|
||||
if result.Notification.BundleID != "" {
|
||||
_, _ = fmt.Fprintf(stderr, " bundleId=%q", result.Notification.BundleID)
|
||||
}
|
||||
if result.Notification.Path != "" {
|
||||
_, _ = fmt.Fprintf(stderr, " path=%q", result.Notification.Path)
|
||||
}
|
||||
if result.Notification.Error != "" {
|
||||
_, _ = fmt.Fprintf(stderr, " error=%q", result.Notification.Error)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ type generateSummary struct {
|
||||
GeneratedTextRawPath string `json:"generatedTextRawPath,omitempty"`
|
||||
GeneratedTextPath string `json:"generatedTextPath,omitempty"`
|
||||
RenderContextPath string `json:"renderContextPath,omitempty"`
|
||||
NotificationPath string `json:"notificationPath,omitempty"`
|
||||
Notification *generateNotificationSummary `json:"notification,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
@@ -92,7 +91,6 @@ func newGenerateSummary(result *app.ReportResult, err error) generateSummary {
|
||||
summary.GeneratedTextRawPath = result.GeneratedTextRawPath
|
||||
summary.GeneratedTextPath = result.GeneratedTextPath
|
||||
summary.RenderContextPath = result.RenderContextPath
|
||||
summary.NotificationPath = result.NotificationPath
|
||||
summary.Notification = newGenerateNotificationSummary(result.Notification)
|
||||
if err != nil {
|
||||
summary.Status = summaryStatusFailed
|
||||
|
||||
@@ -29,7 +29,6 @@ func TestNewGenerateSummaryForGeneratedTextReport(t *testing.T) {
|
||||
GeneratedTextRawPath: "/runs/hourly/generated_text_raw.json",
|
||||
GeneratedTextPath: "/runs/hourly/generated_text.json",
|
||||
RenderContextPath: "/runs/hourly/render_context.json",
|
||||
NotificationPath: "/runs/hourly/notification.json",
|
||||
Metadata: state.Metadata{
|
||||
ReportID: report.Hourly,
|
||||
PromptID: "weather.hourly_generated_text",
|
||||
@@ -99,8 +98,8 @@ func TestNewGenerateSummaryOmitsNotificationWhenNotAttempted(t *testing.T) {
|
||||
if summary.ReportID != report.Daily || summary.ReportName != "Daily Report" || summary.Status != "succeeded" {
|
||||
t.Fatalf("summary = %#v, want successful daily summary", summary)
|
||||
}
|
||||
if summary.Notification != nil || summary.NotificationPath != "" {
|
||||
t.Fatalf("notification summary/path = %#v/%q, want omitted", summary.Notification, summary.NotificationPath)
|
||||
if summary.Notification != nil {
|
||||
t.Fatalf("notification summary = %#v, want omitted", summary.Notification)
|
||||
}
|
||||
data, err := json.Marshal(summary)
|
||||
if err != nil {
|
||||
@@ -141,12 +140,11 @@ func TestNewGenerateSummaryOmitsUnreachedArtifactPaths(t *testing.T) {
|
||||
func TestNewGenerateSummaryForNotificationFailure(t *testing.T) {
|
||||
generatedAt := time.Date(2026, 5, 29, 13, 30, 0, 0, time.UTC)
|
||||
result := &app.ReportResult{
|
||||
DataPackagePath: "/runs/hourly/data_package.yaml",
|
||||
PreparationPath: "/runs/hourly/preparation.json",
|
||||
ReportPath: "/runs/hourly/report.md",
|
||||
OutputPath: "/copies/hourly.md",
|
||||
MetadataPath: "/runs/hourly/metadata.json",
|
||||
NotificationPath: "/runs/hourly/notification.json",
|
||||
DataPackagePath: "/runs/hourly/data_package.yaml",
|
||||
PreparationPath: "/runs/hourly/preparation.json",
|
||||
ReportPath: "/runs/hourly/report.md",
|
||||
OutputPath: "/copies/hourly.md",
|
||||
MetadataPath: "/runs/hourly/metadata.json",
|
||||
Metadata: state.Metadata{
|
||||
ReportID: report.Hourly,
|
||||
PromptID: "weather.hourly_generated_text",
|
||||
@@ -162,8 +160,15 @@ func TestNewGenerateSummaryForNotificationFailure(t *testing.T) {
|
||||
if summary.Status != "failed" || summary.Error != err.Error() {
|
||||
t.Fatalf("status/error = %q/%q, want failed notification error", summary.Status, summary.Error)
|
||||
}
|
||||
if summary.NotificationPath != "/runs/hourly/notification.json" || summary.ReportPath == "" || summary.MetadataPath == "" {
|
||||
t.Fatalf("artifact paths = report %q metadata %q notification %q, want inspectable paths", summary.ReportPath, summary.MetadataPath, summary.NotificationPath)
|
||||
if summary.ReportPath == "" || summary.MetadataPath == "" {
|
||||
t.Fatalf("artifact paths = report %q metadata %q, want retained output provenance", summary.ReportPath, summary.MetadataPath)
|
||||
}
|
||||
data, marshalErr := json.Marshal(summary)
|
||||
if marshalErr != nil {
|
||||
t.Fatalf("Marshal() error = %v", marshalErr)
|
||||
}
|
||||
if strings.Contains(string(data), "notificationPath") {
|
||||
t.Fatalf("summary JSON includes a notification receipt path:\n%s", data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user