Write reports to operator-selected outputs
This commit is contained in:
129
internal/app/output_test.go
Normal file
129
internal/app/output_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/collect"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/config"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
||||
)
|
||||
|
||||
func TestResolveReportOutputPath(t *testing.T) {
|
||||
workingDir := t.TempDir()
|
||||
cfg := config.Defaults()
|
||||
now := workflowTime("2026-05-29T08:30:00-05:00")
|
||||
|
||||
for _, test := range []struct {
|
||||
report ReportKind
|
||||
date string
|
||||
name string
|
||||
}{
|
||||
{report: ReportDaily, date: "2026-05-30T12:00:00-05:00", name: "daily-2026-05-30.md"},
|
||||
{report: ReportToday, date: "2026-05-29T12:00:00-05:00", name: "today.md"},
|
||||
{report: ReportTomorrow, name: "tomorrow.md"},
|
||||
{report: ReportHourly, name: "hourly.md"},
|
||||
} {
|
||||
t.Run(string(test.report), func(t *testing.T) {
|
||||
req := GenerateRequest{Config: cfg, Report: test.report}
|
||||
if test.date != "" {
|
||||
req.Date = workflowTime(test.date)
|
||||
}
|
||||
resolved, err := ResolveGenerate(req, now)
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||
}
|
||||
path, err := resolveReportOutputPath(workingDir, "", resolved)
|
||||
if err != nil {
|
||||
t.Fatalf("resolveReportOutputPath() error = %v", err)
|
||||
}
|
||||
if path != filepath.Join(workingDir, test.name) {
|
||||
t.Fatalf("path = %q, want %q", path, filepath.Join(workingDir, test.name))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
resolved, err := ResolveGenerate(GenerateRequest{
|
||||
Config: cfg, Report: ReportDaily, Date: workflowTime("2026-05-30T12:00:00-05:00"),
|
||||
}, now)
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||
}
|
||||
absoluteDir := t.TempDir()
|
||||
for _, test := range []struct {
|
||||
override string
|
||||
want string
|
||||
}{
|
||||
{override: filepath.Join("reports", "custom.md"), want: filepath.Join(workingDir, "reports", "custom.md")},
|
||||
{override: filepath.Join(absoluteDir, "custom.md"), want: filepath.Join(absoluteDir, "custom.md")},
|
||||
} {
|
||||
path, err := resolveReportOutputPath(workingDir, test.override, resolved)
|
||||
if err != nil {
|
||||
t.Fatalf("resolveReportOutputPath(%q) error = %v", test.override, err)
|
||||
}
|
||||
if path != filepath.Clean(test.want) {
|
||||
t.Fatalf("path = %q, want %q", path, filepath.Clean(test.want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateDetailedRejectsInvalidOutputBeforeCollection(t *testing.T) {
|
||||
cfg := workflowConfig(t)
|
||||
collector := &workflowCollector{err: errors.New("collection must not run")}
|
||||
_, err := GenerateDetailed(context.Background(), GenerateRequest{
|
||||
Config: cfg, Report: ReportDaily, Date: workflowTime("2026-05-29T12:00:00-05:00"),
|
||||
Now: workflowTime("2026-05-29T08:30:00-05:00"), WorkingDir: t.TempDir(), OutputPath: t.TempDir(),
|
||||
Collector: collector,
|
||||
})
|
||||
if err == nil || collector.calls != 0 {
|
||||
t.Fatalf("GenerateDetailed() error/calls = %v/%d, want invalid output before collection", err, collector.calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateDetailedPreservesExistingOutputWhenGenerationFails(t *testing.T) {
|
||||
cfg := workflowConfig(t)
|
||||
cfg.Notify.Distributor.Enabled = false
|
||||
workingDir := t.TempDir()
|
||||
outputPath := filepath.Join(workingDir, "daily-2026-05-29.md")
|
||||
if err := os.WriteFile(outputPath, []byte("existing report"), 0o600); err != nil {
|
||||
t.Fatalf("write existing output: %v", err)
|
||||
}
|
||||
bundle := workflowBundle(t)
|
||||
result, err := GenerateDetailed(context.Background(), GenerateRequest{
|
||||
Config: cfg, Report: ReportDaily, Date: workflowTime("2026-05-29T12:00:00-05:00"),
|
||||
Now: workflowTime("2026-05-29T08:30:00-05:00"), WorkingDir: workingDir,
|
||||
Collector: &workflowCollector{result: &collect.Result{Bundle: &bundle}},
|
||||
Executor: &workflowExecutor{definition: report.DefaultRegistry().MustLookup(report.Daily), raw: []byte(`{}`)},
|
||||
})
|
||||
if err == nil || result == nil || result.OutputPath != "" {
|
||||
t.Fatalf("result/error/output = %#v/%v/%q, want failed generation without output publication", result, err, result.OutputPath)
|
||||
}
|
||||
data, readErr := os.ReadFile(outputPath)
|
||||
if readErr != nil || string(data) != "existing report" {
|
||||
t.Fatalf("output after failure = %q, error %v, want preserved content", data, readErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunBatchDetailedUsesWorkingDirectoryForOutput(t *testing.T) {
|
||||
cfg := assembledBatchConfig(t, false)
|
||||
workingDir := t.TempDir()
|
||||
bundle := assembledBatchBundle(t, "2026-05-31")
|
||||
result, err := RunBatchDetailed(context.Background(), BatchRequest{
|
||||
Config: cfg, Batch: BatchEvening, Now: workflowTime("2026-05-29T18:00:00-05:00"), WorkingDir: workingDir,
|
||||
Collector: &workflowCollector{result: &collect.Result{Bundle: &bundle}},
|
||||
Executor: newAssembledBatchExecutor(),
|
||||
})
|
||||
if err != nil || result == nil || result.Failed != 0 {
|
||||
t.Fatalf("RunBatchDetailed() result/error = %#v/%v", result, err)
|
||||
}
|
||||
if len(result.Reports) != 2 {
|
||||
t.Fatalf("reports = %#v, want Tomorrow and Daily", result.Reports)
|
||||
}
|
||||
if result.Reports[0].OutputPath != filepath.Join(workingDir, "tomorrow.md") ||
|
||||
result.Reports[1].OutputPath != filepath.Join(workingDir, "daily-2026-05-31.md") {
|
||||
t.Fatalf("output paths = %#v, want working-directory defaults", result.Reports)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user