Add app collection seam
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/scriptorium"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/collect"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/config"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/promptinput"
|
||||
@@ -81,6 +82,61 @@ func TestFetchAndSaveBundleRequiresOutputPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateUsesProvidedCollector(t *testing.T) {
|
||||
cfg := config.Defaults()
|
||||
cfg.WeatherAPI.BaseURL = ""
|
||||
cfg.Workspace.Root = t.TempDir()
|
||||
collector := &recordingCollector{err: errors.New("provided collector failed")}
|
||||
|
||||
err := Generate(context.Background(), GenerateRequest{
|
||||
Config: cfg,
|
||||
Report: ReportDaily,
|
||||
Date: mustParse("2026-05-29T12:00:00-05:00"),
|
||||
Now: mustParse("2026-05-29T05:00:00-05:00"),
|
||||
Collector: collector,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Generate() error = nil, want collector error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "provided collector failed") {
|
||||
t.Fatalf("Generate() error = %q, want provided collector error", err.Error())
|
||||
}
|
||||
if len(collector.requests) != 1 {
|
||||
t.Fatalf("collector requests = %d, want 1", len(collector.requests))
|
||||
}
|
||||
if collector.requests[0].Config.WeatherAPI.BaseURL != "" {
|
||||
t.Fatalf("collector base URL = %q, want request config", collector.requests[0].Config.WeatherAPI.BaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateCollectionFailureStopsBeforeReportExecution(t *testing.T) {
|
||||
cfg := config.Defaults()
|
||||
cfg.WeatherAPI.BaseURL = ""
|
||||
cfg.Workspace.Root = t.TempDir()
|
||||
markerPath := filepath.Join(t.TempDir(), "scriptorium-called")
|
||||
binaryPath := filepath.Join(t.TempDir(), "scriptorium")
|
||||
script := fmt.Sprintf("#!/bin/sh\nprintf called > %q\nexit 0\n", markerPath)
|
||||
if err := os.WriteFile(binaryPath, []byte(script), 0o755); err != nil {
|
||||
t.Fatalf("write scriptorium marker script: %v", err)
|
||||
}
|
||||
cfg.Scriptorium.Binary = binaryPath
|
||||
collector := &recordingCollector{err: errors.New("collection unavailable")}
|
||||
|
||||
err := Generate(context.Background(), GenerateRequest{
|
||||
Config: cfg,
|
||||
Report: ReportDaily,
|
||||
Date: mustParse("2026-05-29T12:00:00-05:00"),
|
||||
Now: mustParse("2026-05-29T05:00:00-05:00"),
|
||||
Collector: collector,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Generate() error = nil, want collector error")
|
||||
}
|
||||
if _, statErr := os.Stat(markerPath); !errors.Is(statErr, os.ErrNotExist) {
|
||||
t.Fatalf("scriptorium marker stat error = %v, want marker absent after collection failure", statErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateReportWritesReportAndPreflight(t *testing.T) {
|
||||
server := dailyBundleServer(t)
|
||||
cfg := dailyWorkspaceConfig(t, server)
|
||||
@@ -2318,6 +2374,39 @@ func TestBatchOutputPathUsesHourlyOutputName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunBatchDetailedUsesProvidedCollector(t *testing.T) {
|
||||
cfg := config.Defaults()
|
||||
cfg.WeatherAPI.BaseURL = ""
|
||||
cfg.Workspace.Root = t.TempDir()
|
||||
collector := &recordingCollector{err: errors.New("batch collector failed")}
|
||||
renderer := &recordingRenderer{}
|
||||
|
||||
result, err := RunBatchDetailed(context.Background(), BatchRequest{
|
||||
Config: cfg,
|
||||
Batch: BatchMorning,
|
||||
Now: mustParse("2026-05-29T05:00:00-05:00"),
|
||||
Collector: collector,
|
||||
Renderer: renderer,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("RunBatchDetailed() error = %v", err)
|
||||
}
|
||||
if result.Total == 0 || result.Failed != result.Total {
|
||||
t.Fatalf("batch result = %#v, want every report failed by collector", result)
|
||||
}
|
||||
if len(collector.requests) != result.Total {
|
||||
t.Fatalf("collector requests = %d, want %d", len(collector.requests), result.Total)
|
||||
}
|
||||
for _, reportResult := range result.Reports {
|
||||
if !strings.Contains(reportResult.Error, "batch collector failed") {
|
||||
t.Fatalf("report error = %q, want fake collector error", reportResult.Error)
|
||||
}
|
||||
}
|
||||
if renderer.renderCalls != 0 || renderer.runCalls != 0 || renderer.structuredRunCalls != 0 {
|
||||
t.Fatalf("renderer calls render=%d run=%d structured=%d, want none after collection failure", renderer.renderCalls, renderer.runCalls, renderer.structuredRunCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func mustParse(value string) time.Time {
|
||||
parsed, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
@@ -2790,6 +2879,20 @@ type recordingRenderer struct {
|
||||
structuredRunBody string
|
||||
}
|
||||
|
||||
type recordingCollector struct {
|
||||
result *collect.Result
|
||||
err error
|
||||
requests []collect.Request
|
||||
}
|
||||
|
||||
func (c *recordingCollector) Run(_ context.Context, req collect.Request) (*collect.Result, error) {
|
||||
c.requests = append(c.requests, req)
|
||||
if c.err != nil {
|
||||
return nil, c.err
|
||||
}
|
||||
return c.result, nil
|
||||
}
|
||||
|
||||
type recordingStore struct {
|
||||
state.Store
|
||||
calls []string
|
||||
|
||||
Reference in New Issue
Block a user