2026-05-29 17:40:38 +00:00
package state
import (
"context"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
"gitea.maximumdirect.net/eric/weatherreporter/internal/config"
2026-06-09 20:58:35 +00:00
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
2026-05-29 17:40:38 +00:00
"gitea.maximumdirect.net/eric/weatherreporter/internal/promptinput"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
"gitea.maximumdirect.net/eric/weatherreporter/internal/timeutil"
)
func TestPathsUseRunIDAndWorkspace ( t * testing . T ) {
store := newTestStore ( t )
resolved := resolveDailyAt ( t , "2026-05-29T05:00:00-05:00" )
paths , err := store . Paths ( resolved )
if err != nil {
t . Fatalf ( "Paths() error = %v" , err )
}
for _ , want := range [ ] string {
2026-06-20 13:29:41 +00:00
filepath . Join ( "snapshots" , "daily" , "2026-05-29" , "modules.20260529T100000.000000000Z_daily_2026-05-29.json" ) ,
filepath . Join ( "snapshots" , "daily" , "2026-05-29" , "metadata.20260529T100000.000000000Z_daily_2026-05-29.json" ) ,
filepath . Join ( "data-packages" , "daily" , "2026-05-29" , "data_package.20260529T100000.000000000Z_daily_2026-05-29.yaml" ) ,
filepath . Join ( "preflight" , "daily" , "2026-05-29" , "render.20260529T100000.000000000Z_daily_2026-05-29.json" ) ,
filepath . Join ( "notifications" , "daily" , "2026-05-29" , "distributor.20260529T100000.000000000Z_daily_2026-05-29.json" ) ,
filepath . Join ( "reports" , "daily" , "2026-05-29" , "report.20260529T100000.000000000Z_daily_2026-05-29.md" ) ,
2026-05-29 17:40:38 +00:00
} {
if ! strings . Contains ( pathsString ( paths ) , want ) {
t . Fatalf ( "paths = %#v, want component %q" , paths , want )
}
}
}
2026-06-17 14:39:53 -05:00
func TestDailyPathsUseRunIDValidDateDisambiguator ( t * testing . T ) {
store := newTestStore ( t )
first := resolveDailyForDateAt ( t , "2026-05-29T05:00:00-05:00" , "2026-05-31T12:00:00-05:00" )
second := resolveDailyForDateAt ( t , "2026-05-29T05:00:00-05:00" , "2026-06-01T12:00:00-05:00" )
firstPaths , err := store . Paths ( first )
if err != nil {
t . Fatalf ( "Paths(first) error = %v" , err )
}
secondPaths , err := store . Paths ( second )
if err != nil {
t . Fatalf ( "Paths(second) error = %v" , err )
}
if first . Metadata ( ) . RunID == second . Metadata ( ) . RunID {
t . Fatalf ( "RunIDs both = %q, want distinct daily run ids" , first . Metadata ( ) . RunID )
}
for name , values := range map [ string ] [ 2 ] string {
"RenderedReport" : { firstPaths . RenderedReport , secondPaths . RenderedReport } ,
"Metadata" : { firstPaths . Metadata , secondPaths . Metadata } ,
"DataPackage" : { firstPaths . DataPackage , secondPaths . DataPackage } ,
"Notification" : { firstPaths . Notification , secondPaths . Notification } ,
} {
if values [ 0 ] == values [ 1 ] {
t . Fatalf ( "%s paths both = %q, want distinct daily artifact paths" , name , values [ 0 ] )
}
}
}
2026-06-17 20:39:22 +00:00
func TestBatchDistributorNotificationPathUsesWorkspaceBatchDateAndRunID ( t * testing . T ) {
store := newTestStore ( t )
location := mustLoadStateLocation ( t , "America/Chicago" )
startedAt := time . Date ( 2026 , 6 , 18 , 3 , 30 , 0 , 123456789 , time . UTC )
path , err := store . BatchDistributorNotificationPath ( BatchDistributorNotificationRef {
Batch : "evening" ,
BatchRunID : "20260618T033000.123456789Z_evening" ,
StartedAt : startedAt ,
Location : location ,
} )
if err != nil {
t . Fatalf ( "BatchDistributorNotificationPath() error = %v" , err )
}
2026-06-20 13:29:41 +00:00
want := filepath . Join ( "notifications" , "batches" , "evening" , "2026-06-17" , "distributor.20260618T033000.123456789Z_evening.json" )
2026-06-17 20:39:22 +00:00
if ! strings . Contains ( path , want ) {
t . Fatalf ( "path = %q, want component %q" , path , want )
}
if ! strings . HasPrefix ( path , store . root ) {
t . Fatalf ( "path = %q, want workspace root prefix %q" , path , store . root )
}
}
func TestSaveBatchDistributorNotificationRoundTrip ( t * testing . T ) {
store := newTestStore ( t )
location := mustLoadStateLocation ( t , "America/Chicago" )
startedAt := time . Date ( 2026 , 6 , 17 , 12 , 0 , 0 , 0 , time . UTC )
bundleCreated := startedAt . Add ( 2 * time . Second )
attemptedAt := startedAt . Add ( 3 * time . Second )
acceptedAt := startedAt . Add ( 4 * time . Second )
finishedAt := startedAt . Add ( 5 * time . Second )
ref := BatchDistributorNotificationRef {
Batch : "morning" ,
BatchRunID : "20260617T120000.000000000Z_morning" ,
StartedAt : startedAt ,
Location : location ,
}
path , err := store . SaveBatchDistributorNotification ( context . Background ( ) , ref , BatchDistributorNotificationArtifact {
AttemptedAt : attemptedAt ,
Endpoint : "https://distributor.example.test" ,
PipelineID : "weatherreporter" ,
BundleID : "weatherreporter.home.morning" ,
IdempotencyKey : "weatherreporter.home.morning.20260617T120000.000000000Z_morning" ,
BundleCreated : bundleCreated ,
Reports : [ ] BatchDistributorNotificationReportArtifact {
{
ReportID : report . Today ,
RunID : "20260617T120000.000000000Z_today" ,
2026-06-20 13:29:41 +00:00
SourcePath : "/workspace/reports/today/2026-06-17/report.20260617T120000.000000000Z_today.md" ,
2026-06-17 20:39:22 +00:00
BundlePaths : [ ] string { "2026-06-17/today/report.md" } ,
} ,
{
ReportID : report . Daily ,
RunID : "20260617T120000.000000000Z_daily_2026-06-19" ,
2026-06-20 13:29:41 +00:00
SourcePath : "/workspace/reports/daily/2026-06-19/report.20260617T120000.000000000Z_daily_2026-06-19.md" ,
2026-06-17 20:39:22 +00:00
BundlePaths : [ ] string { "2026-06-19/daily/report.md" } ,
} ,
} ,
Status : "failed" ,
Upload : & DistributorUploadResult {
RunID : "distributor-run" ,
Status : "accepted" ,
} ,
RunStatus : & DistributorRunStatus {
RunID : "distributor-run" ,
PipelineID : "weatherreporter" ,
Status : "failed" ,
AcceptedAt : acceptedAt ,
FinishedAt : & finishedAt ,
Report : json . RawMessage ( ` { "actions":[ { "action":"failed"}]} ` ) ,
Error : "destination conflict" ,
} ,
StatusError : "status lookup failed" ,
Error : "batch upload failed" ,
} )
if err != nil {
t . Fatalf ( "SaveBatchDistributorNotification() error = %v" , err )
}
2026-06-20 13:29:41 +00:00
wantPath := filepath . Join ( "notifications" , "batches" , "morning" , "2026-06-17" , "distributor.20260617T120000.000000000Z_morning.json" )
2026-06-17 20:39:22 +00:00
if ! strings . Contains ( path , wantPath ) {
t . Fatalf ( "path = %q, want component %q" , path , wantPath )
}
data , err := os . ReadFile ( path )
if err != nil {
t . Fatalf ( "read batch notification: %v" , err )
}
var artifact BatchDistributorNotificationArtifact
if err := json . Unmarshal ( data , & artifact ) ; err != nil {
t . Fatalf ( "decode batch notification: %v" , err )
}
if artifact . SchemaVersion != BatchDistributorNotificationSchemaVersion {
t . Fatalf ( "SchemaVersion = %q, want %q" , artifact . SchemaVersion , BatchDistributorNotificationSchemaVersion )
}
if artifact . Batch != "morning" || artifact . BatchRunID != ref . BatchRunID {
t . Fatalf ( "artifact batch identity = %q/%q, want ref values" , artifact . Batch , artifact . BatchRunID )
}
if artifact . Endpoint != "https://distributor.example.test" || artifact . PipelineID != "weatherreporter" || artifact . BundleID != "weatherreporter.home.morning" || artifact . IdempotencyKey == "" {
t . Fatalf ( "artifact identity = %#v, want distributor identity" , artifact )
}
if len ( artifact . Reports ) != 2 || artifact . Reports [ 0 ] . ReportID != report . Today || strings . Join ( artifact . Reports [ 1 ] . BundlePaths , "," ) != "2026-06-19/daily/report.md" {
t . Fatalf ( "Reports = %#v, want included report records" , artifact . Reports )
}
if artifact . Upload == nil || artifact . Upload . RunID != "distributor-run" {
t . Fatalf ( "Upload = %#v, want accepted upload result" , artifact . Upload )
}
if artifact . RunStatus == nil || artifact . RunStatus . Status != "failed" || ! strings . Contains ( string ( artifact . RunStatus . Report ) , "failed" ) || artifact . RunStatus . FinishedAt == nil {
t . Fatalf ( "RunStatus = %#v, want failed run status with raw report" , artifact . RunStatus )
}
if artifact . StatusError != "status lookup failed" || artifact . Error != "batch upload failed" {
t . Fatalf ( "errors = %q/%q, want persisted error fields" , artifact . StatusError , artifact . Error )
}
}
func TestBatchDistributorNotificationPathRejectsInvalidIdentity ( t * testing . T ) {
store := newTestStore ( t )
location := mustLoadStateLocation ( t , "America/Chicago" )
valid := BatchDistributorNotificationRef {
Batch : "morning" ,
BatchRunID : "20260617T120000.000000000Z_morning" ,
StartedAt : time . Date ( 2026 , 6 , 17 , 12 , 0 , 0 , 0 , time . UTC ) ,
Location : location ,
}
tests := [ ] struct {
name string
mutate func ( * BatchDistributorNotificationRef )
wantErr string
} {
{
name : "Batch" ,
mutate : func ( ref * BatchDistributorNotificationRef ) {
ref . Batch = ""
} ,
wantErr : "batch kind is required" ,
} ,
{
name : "BatchSeparator" ,
mutate : func ( ref * BatchDistributorNotificationRef ) {
ref . Batch = "../morning"
} ,
wantErr : "batch kind must not contain path separators" ,
} ,
{
name : "BatchRunID" ,
mutate : func ( ref * BatchDistributorNotificationRef ) {
ref . BatchRunID = ""
} ,
wantErr : "batch run id is required" ,
} ,
{
name : "BatchRunIDSeparator" ,
mutate : func ( ref * BatchDistributorNotificationRef ) {
ref . BatchRunID = "nested/run"
} ,
wantErr : "batch run id must not contain path separators" ,
} ,
{
name : "StartedAt" ,
mutate : func ( ref * BatchDistributorNotificationRef ) {
ref . StartedAt = time . Time { }
} ,
wantErr : "batch started time is required" ,
} ,
{
name : "Location" ,
mutate : func ( ref * BatchDistributorNotificationRef ) {
ref . Location = nil
} ,
wantErr : "batch location is required" ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
ref := valid
tt . mutate ( & ref )
_ , err := store . BatchDistributorNotificationPath ( ref )
if err == nil {
t . Fatal ( "BatchDistributorNotificationPath() error = nil, want error" )
}
if ! strings . Contains ( err . Error ( ) , tt . wantErr ) {
t . Fatalf ( "error = %q, want %q" , err . Error ( ) , tt . wantErr )
}
} )
}
}
2026-06-14 04:51:52 +00:00
func TestGeneratedTextArtifactPathsUseSnapshotTree ( t * testing . T ) {
store := newTestStore ( t )
2026-06-14 23:49:18 +00:00
tests := [ ] struct {
name string
resolved report . Resolved
group string
validDate string
runID string
} {
2026-06-15 16:43:06 +00:00
{
name : "daily" ,
resolved : resolveDailyAt ( t , "2026-05-29T05:00:00-05:00" ) ,
group : "daily" ,
validDate : "2026-05-29" ,
2026-06-17 14:39:53 -05:00
runID : "20260529T100000.000000000Z_daily_2026-05-29" ,
2026-06-15 16:43:06 +00:00
} ,
2026-06-14 23:49:18 +00:00
{
name : "hourly" ,
resolved : resolveHourlyAt ( t , "2026-05-29T05:00:00-05:00" ) ,
group : "hourly" ,
validDate : "2026-05-29" ,
runID : "20260529T100000.000000000Z_hourly" ,
} ,
{
name : "tomorrow" ,
resolved : resolveTomorrowAt ( t , "2026-05-29T18:00:00-05:00" ) ,
group : "tomorrow" ,
validDate : "2026-05-30" ,
runID : "20260529T230000.000000000Z_tomorrow" ,
} ,
2026-06-15 14:54:37 +00:00
{
name : "today" ,
resolved : resolveTodayAt ( t , "2026-05-29T05:00:00-05:00" ) ,
group : "today" ,
validDate : "2026-05-29" ,
runID : "20260529T100000.000000000Z_today" ,
} ,
2026-06-14 04:51:52 +00:00
}
2026-06-14 23:49:18 +00:00
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
paths , err := store . Paths ( tt . resolved )
if err != nil {
t . Fatalf ( "Paths() error = %v" , err )
}
wants := map [ string ] string {
2026-06-20 13:29:41 +00:00
"ModuleSnapshot" : filepath . Join ( "snapshots" , tt . group , tt . validDate , "modules." + tt . runID + ".json" ) ,
"Metadata" : filepath . Join ( "snapshots" , tt . group , tt . validDate , "metadata." + tt . runID + ".json" ) ,
"DataPackage" : filepath . Join ( "data-packages" , tt . group , tt . validDate , "data_package." + tt . runID + ".yaml" ) ,
"Preflight" : filepath . Join ( "preflight" , tt . group , tt . validDate , "render." + tt . runID + ".json" ) ,
"Notification" : filepath . Join ( "notifications" , tt . group , tt . validDate , "distributor." + tt . runID + ".json" ) ,
"RenderedReport" : filepath . Join ( "reports" , tt . group , tt . validDate , "report." + tt . runID + ".md" ) ,
"GeneratedTextRaw" : filepath . Join ( "snapshots" , tt . group , tt . validDate , "generated_text_raw." + tt . runID + ".json" ) ,
"GeneratedTextResult" : filepath . Join ( "snapshots" , tt . group , tt . validDate , "generated_text_result." + tt . runID + ".json" ) ,
"GeneratedText" : filepath . Join ( "snapshots" , tt . group , tt . validDate , "generated_text." + tt . runID + ".json" ) ,
"RenderContext" : filepath . Join ( "snapshots" , tt . group , tt . validDate , "render_context." + tt . runID + ".json" ) ,
2026-06-14 23:49:18 +00:00
}
got := map [ string ] string {
2026-06-20 13:29:41 +00:00
"ModuleSnapshot" : paths . ModuleSnapshot ,
"Metadata" : paths . Metadata ,
2026-06-14 23:49:18 +00:00
"DataPackage" : paths . DataPackage ,
2026-06-20 13:29:41 +00:00
"Preflight" : paths . Preflight ,
"Notification" : paths . Notification ,
2026-06-14 23:49:18 +00:00
"RenderedReport" : paths . RenderedReport ,
"GeneratedTextRaw" : paths . GeneratedTextRaw ,
"GeneratedTextResult" : paths . GeneratedTextResult ,
"GeneratedText" : paths . GeneratedText ,
"RenderContext" : paths . RenderContext ,
}
for name , want := range wants {
if ! strings . Contains ( got [ name ] , want ) {
t . Fatalf ( "%s path = %q, want component %q" , name , got [ name ] , want )
}
}
} )
2026-06-14 04:51:52 +00:00
}
}
2026-05-29 17:40:38 +00:00
func TestSaveArtifactsAndMetadataRoundTrip ( t * testing . T ) {
store := newTestStore ( t )
resolved := resolveDailyAt ( t , "2026-05-29T05:00:00-05:00" )
2026-06-09 21:33:34 +00:00
briefingMetadata := stateBriefingMetadata ( resolved )
2026-06-15 20:23:02 +00:00
snapshot , err := module . NewSnapshot ( [ ] module . Output { {
ID : module . Metadata ,
StanzaName : "metadata" ,
Value : map [ string ] string { "run_id" : resolved . Metadata ( ) . RunID } ,
PromptValue : map [ string ] string { "prompt_run_id" : resolved . Metadata ( ) . RunID } ,
} } )
2026-06-09 21:08:17 +00:00
if err != nil {
t . Fatalf ( "NewSnapshot() error = %v" , err )
}
dataPackage , err := promptinput . Build ( promptinput . BuildRequest {
Metadata : promptinput . Metadata {
RunID : resolved . Metadata ( ) . RunID ,
ReportID : resolved . Definition . ID ,
2026-06-09 21:33:34 +00:00
Variant : briefingMetadata . Variant ,
2026-06-09 21:08:17 +00:00
PromptID : resolved . Definition . PromptID ,
GeneratedAt : resolved . GeneratedAt ,
Timezone : resolved . Timezone ,
ValidPeriod : resolved . ValidPeriod ,
} ,
Modules : snapshot ,
} )
2026-05-29 17:40:38 +00:00
if err != nil {
t . Fatalf ( "Build() error = %v" , err )
}
dataPackagePath , err := store . SaveDataPackage ( context . Background ( ) , resolved , dataPackage )
if err != nil {
t . Fatalf ( "SaveDataPackage() error = %v" , err )
}
2026-06-09 20:58:35 +00:00
moduleSnapshotPath , err := store . SaveModuleSnapshot ( context . Background ( ) , resolved , snapshot )
if err != nil {
t . Fatalf ( "SaveModuleSnapshot() error = %v" , err )
}
2026-05-29 20:33:35 +00:00
preflightPath , err := store . SavePreflight ( context . Background ( ) , resolved , PreflightArtifact { Stdout : ` { "ok":true} ` } )
2026-05-29 17:40:38 +00:00
if err != nil {
t . Fatalf ( "SavePreflight() error = %v" , err )
}
2026-06-07 20:21:26 -05:00
notificationPath , err := store . SaveDistributorNotification ( context . Background ( ) , resolved , DistributorNotificationArtifact {
RunID : resolved . Metadata ( ) . RunID ,
ReportID : resolved . Definition . ID ,
AttemptedAt : resolved . GeneratedAt ,
Endpoint : "https://distributor.example.test" ,
2026-06-08 07:04:31 -05:00
PipelineID : "weatherreporter.daily" ,
2026-06-07 20:21:26 -05:00
BundleID : "weatherreporter.home.daily.run" ,
IdempotencyKey : "weatherreporter.home.daily.run" ,
SourcePath : "/tmp/report.md" ,
2026-06-09 11:26:50 -05:00
BundlePaths : [ ] string { "2026-05-29/daily/report.md" } ,
2026-06-07 20:21:26 -05:00
BundleCreated : resolved . GeneratedAt ,
Status : "succeeded" ,
RunStatus : & DistributorRunStatus { RunID : "distributor-run" , Status : "succeeded" } ,
} )
if err != nil {
t . Fatalf ( "SaveDistributorNotification() error = %v" , err )
}
2026-05-29 17:47:42 +00:00
renderedReportPath , err := store . PrepareRenderedReport ( context . Background ( ) , resolved )
if err != nil {
t . Fatalf ( "PrepareRenderedReport() error = %v" , err )
}
if err := os . WriteFile ( renderedReportPath , [ ] byte ( "# Daily Report\n" ) , 0 o600 ) ; err != nil {
t . Fatalf ( "write rendered report: %v" , err )
}
2026-05-29 20:33:35 +00:00
var preflight PreflightArtifact
preflightData , err := os . ReadFile ( preflightPath )
if err != nil {
t . Fatalf ( "read preflight: %v" , err )
}
if err := json . Unmarshal ( preflightData , & preflight ) ; err != nil {
t . Fatalf ( "decode preflight: %v" , err )
}
if preflight . Stdout != ` { "ok":true} ` {
t . Fatalf ( "preflight stdout = %q, want render stdout" , preflight . Stdout )
}
2026-06-07 20:21:26 -05:00
var notification DistributorNotificationArtifact
notificationData , err := os . ReadFile ( notificationPath )
if err != nil {
t . Fatalf ( "read notification: %v" , err )
}
if err := json . Unmarshal ( notificationData , & notification ) ; err != nil {
t . Fatalf ( "decode notification: %v" , err )
}
2026-06-09 11:26:50 -05:00
if notification . SchemaVersion != DistributorNotificationSchemaVersion || notification . PipelineID != "weatherreporter.daily" || len ( notification . BundlePaths ) != 1 || notification . RunStatus == nil || notification . RunStatus . Status != "succeeded" {
2026-06-07 20:21:26 -05:00
t . Fatalf ( "notification = %#v, want persisted distributor status" , notification )
}
2026-05-29 17:40:38 +00:00
paths , err := store . Paths ( resolved )
if err != nil {
t . Fatalf ( "Paths() error = %v" , err )
}
2026-06-09 21:33:34 +00:00
metadata := BuildMetadataFromBriefingMetadata ( resolved , briefingMetadata , ArtifactPaths {
2026-06-09 20:58:35 +00:00
ModuleSnapshot : moduleSnapshotPath ,
2026-05-29 17:40:38 +00:00
Metadata : paths . Metadata ,
DataPackage : dataPackagePath ,
Preflight : preflightPath ,
2026-05-29 17:47:42 +00:00
RenderedReport : renderedReportPath ,
2026-05-29 17:40:38 +00:00
} )
metadataPath , err := store . SaveMetadata ( context . Background ( ) , metadata )
if err != nil {
t . Fatalf ( "SaveMetadata() error = %v" , err )
}
2026-06-09 21:33:34 +00:00
for _ , path := range [ ] string { moduleSnapshotPath , dataPackagePath , preflightPath , notificationPath , renderedReportPath , metadataPath } {
2026-05-29 17:40:38 +00:00
if _ , err := os . Stat ( path ) ; err != nil {
t . Fatalf ( "expected artifact %q: %v" , path , err )
}
}
2026-06-09 20:58:35 +00:00
loadedSnapshot , err := store . LoadModuleSnapshot ( context . Background ( ) , moduleSnapshotPath )
if err != nil {
t . Fatalf ( "LoadModuleSnapshot() error = %v" , err )
}
if loadedSnapshot . SchemaVersion != module . SnapshotSchemaVersion || len ( loadedSnapshot . Outputs ) != 1 {
t . Fatalf ( "loaded module snapshot = %#v, want one metadata output" , loadedSnapshot )
}
2026-06-15 20:23:02 +00:00
if loadedSnapshot . Outputs [ 0 ] . PromptValue != nil {
t . Fatalf ( "loaded module snapshot PromptValue = %#v, want omitted runtime value" , loadedSnapshot . Outputs [ 0 ] . PromptValue )
}
snapshotData , err := os . ReadFile ( moduleSnapshotPath )
if err != nil {
t . Fatalf ( "read module snapshot: %v" , err )
}
if strings . Contains ( string ( snapshotData ) , "prompt_run_id" ) || strings . Contains ( string ( snapshotData ) , "promptValue" ) || strings . Contains ( string ( snapshotData ) , "PromptValue" ) {
t . Fatalf ( "module snapshot JSON includes runtime-only prompt value:\n%s" , string ( snapshotData ) )
}
2026-06-09 21:08:17 +00:00
loadedDataPackage , err := store . LoadDataPackage ( context . Background ( ) , dataPackagePath )
if err != nil {
t . Fatalf ( "LoadDataPackage() error = %v" , err )
}
if loadedDataPackage . SchemaVersion != promptinput . SchemaVersion || loadedDataPackage . Briefing . Order [ 0 ] != "metadata" {
t . Fatalf ( "loaded data package = %#v, want YAML package with metadata stanza" , loadedDataPackage )
}
2026-06-15 20:27:28 +00:00
metadataStanza , ok := loadedDataPackage . Briefing . Values [ "metadata" ] . ( map [ string ] any )
if ! ok || metadataStanza [ "prompt_run_id" ] != resolved . Metadata ( ) . RunID {
t . Fatalf ( "loaded data package metadata = %#v, want runtime prompt value" , loadedDataPackage . Briefing . Values [ "metadata" ] )
}
2026-05-29 17:40:38 +00:00
var decoded Metadata
data , err := os . ReadFile ( metadataPath )
if err != nil {
t . Fatalf ( "read metadata: %v" , err )
}
if err := json . Unmarshal ( data , & decoded ) ; err != nil {
t . Fatalf ( "decode metadata: %v" , err )
}
if decoded . RunID != resolved . Metadata ( ) . RunID {
t . Fatalf ( "RunID = %q, want %q" , decoded . RunID , resolved . Metadata ( ) . RunID )
}
2026-06-09 21:33:34 +00:00
if decoded . ModuleSnapshotPath != moduleSnapshotPath || decoded . DataPackagePath != dataPackagePath || decoded . PreflightPath != preflightPath {
2026-05-29 17:40:38 +00:00
t . Fatalf ( "metadata paths = %#v, want saved artifact paths" , decoded )
}
2026-05-29 17:47:42 +00:00
if decoded . RenderedReportPath != renderedReportPath {
t . Fatalf ( "RenderedReportPath = %q, want %q" , decoded . RenderedReportPath , renderedReportPath )
}
2026-05-29 19:57:08 -05:00
if decoded . Location == nil || decoded . Location . Name != "Brentwood" || decoded . Location . Timezone != "America/Chicago" {
t . Fatalf ( "metadata location = %#v, want briefing location" , decoded . Location )
}
2026-05-29 18:25:38 -05:00
if strings . Contains ( string ( data ) , "MetadataPath" ) || strings . Contains ( string ( data ) , "metadataPath" ) {
t . Fatalf ( "metadata JSON includes runtime-only MetadataPath:\n%s" , string ( data ) )
}
2026-06-15 16:43:06 +00:00
if decoded . GeneratedTextSchemaID != "daily" {
t . Fatalf ( "GeneratedTextSchemaID = %q, want daily" , decoded . GeneratedTextSchemaID )
}
for _ , unexpected := range [ ] string { "generatedTextRawPath" , "generatedTextResultPath" , "generatedTextPath" , "renderContextPath" } {
2026-06-14 04:51:52 +00:00
if strings . Contains ( string ( data ) , unexpected ) {
2026-06-15 16:43:06 +00:00
t . Fatalf ( "metadata JSON includes unsaved generated-text path field %q:\n%s" , unexpected , string ( data ) )
2026-06-14 04:51:52 +00:00
}
}
}
func TestSaveGeneratedTextArtifactsAndMetadataRoundTrip ( t * testing . T ) {
store := newTestStore ( t )
resolved := resolveHourlyAt ( t , "2026-05-29T05:00:00-05:00" )
briefingMetadata := stateBriefingMetadata ( resolved )
rawPath , err := store . SaveGeneratedTextRaw ( context . Background ( ) , resolved , [ ] byte ( ` { "summary":"raw"} ` ) )
if err != nil {
t . Fatalf ( "SaveGeneratedTextRaw() error = %v" , err )
}
resultPath , err := store . SaveGeneratedTextResult ( context . Background ( ) , resolved , map [ string ] any {
"command" : [ ] string { "scriptorium" , "run" } ,
"status" : "succeeded" ,
} )
if err != nil {
t . Fatalf ( "SaveGeneratedTextResult() error = %v" , err )
}
2026-06-14 12:54:32 -05:00
generatedPath , err := store . SaveGeneratedText ( context . Background ( ) , resolved , [ ] byte ( ` { "summary":"Storm chances increase.","forecast_discussion":"A front will keep the region unsettled."} ` ) )
2026-06-14 04:51:52 +00:00
if err != nil {
t . Fatalf ( "SaveGeneratedText() error = %v" , err )
}
contextPath , err := store . SaveRenderContext ( context . Background ( ) , resolved , struct {
ReportTitle string ` json:"reportTitle" `
Location string ` json:"location" `
} {
ReportTitle : "Hourly Report" ,
Location : "Brentwood" ,
} )
if err != nil {
t . Fatalf ( "SaveRenderContext() error = %v" , err )
}
for _ , path := range [ ] string { rawPath , resultPath , generatedPath , contextPath } {
if _ , err := os . Stat ( path ) ; err != nil {
t . Fatalf ( "expected generated-text artifact %q: %v" , path , err )
}
}
rawData , err := store . LoadGeneratedText ( context . Background ( ) , rawPath )
if err != nil {
t . Fatalf ( "LoadGeneratedText() raw error = %v" , err )
}
if string ( rawData ) != ` { "summary":"raw"} ` {
t . Fatalf ( "raw data = %q, want saved bytes" , rawData )
}
generatedData , err := store . LoadGeneratedText ( context . Background ( ) , generatedPath )
if err != nil {
t . Fatalf ( "LoadGeneratedText() generated error = %v" , err )
}
2026-06-14 12:54:32 -05:00
if ! strings . Contains ( string ( generatedData ) , ` "forecast_discussion":"A front will keep the region unsettled." ` ) {
2026-06-14 04:51:52 +00:00
t . Fatalf ( "generated text = %q, want saved normalized JSON" , generatedData )
}
var runResult struct {
Command [ ] string ` json:"command" `
Status string ` json:"status" `
}
if err := store . LoadGeneratedTextResult ( context . Background ( ) , resultPath , & runResult ) ; err != nil {
t . Fatalf ( "LoadGeneratedTextResult() error = %v" , err )
}
if runResult . Status != "succeeded" || strings . Join ( runResult . Command , " " ) != "scriptorium run" {
t . Fatalf ( "run result = %#v, want saved result" , runResult )
}
var renderContext struct {
ReportTitle string ` json:"reportTitle" `
Location string ` json:"location" `
}
if err := store . LoadRenderContext ( context . Background ( ) , contextPath , & renderContext ) ; err != nil {
t . Fatalf ( "LoadRenderContext() error = %v" , err )
}
if renderContext . ReportTitle != "Hourly Report" || renderContext . Location != "Brentwood" {
t . Fatalf ( "render context = %#v, want saved context" , renderContext )
}
paths , err := store . Paths ( resolved )
if err != nil {
t . Fatalf ( "Paths() error = %v" , err )
}
metadata := BuildMetadataFromBriefingMetadata ( resolved , briefingMetadata , ArtifactPaths {
ModuleSnapshot : paths . ModuleSnapshot ,
Metadata : paths . Metadata ,
DataPackage : paths . DataPackage ,
Preflight : paths . Preflight ,
RenderedReport : paths . RenderedReport ,
GeneratedTextRaw : rawPath ,
GeneratedTextResult : resultPath ,
GeneratedText : generatedPath ,
RenderContext : contextPath ,
} )
metadataPath , err := store . SaveMetadata ( context . Background ( ) , metadata )
if err != nil {
t . Fatalf ( "SaveMetadata() error = %v" , err )
}
var decoded Metadata
metadataData , err := os . ReadFile ( metadataPath )
if err != nil {
t . Fatalf ( "read metadata: %v" , err )
}
if err := json . Unmarshal ( metadataData , & decoded ) ; err != nil {
t . Fatalf ( "decode metadata: %v" , err )
}
if decoded . GeneratedTextSchemaID != "hourly" {
t . Fatalf ( "GeneratedTextSchemaID = %q, want hourly" , decoded . GeneratedTextSchemaID )
}
if decoded . GeneratedTextRawPath != rawPath || decoded . GeneratedTextResultPath != resultPath || decoded . GeneratedTextPath != generatedPath || decoded . RenderContextPath != contextPath {
t . Fatalf ( "metadata generated-text paths = %#v, want saved artifact paths" , decoded )
}
2026-05-29 18:25:38 -05:00
}
func TestSaveMetadataUsesExplicitMetadataPath ( t * testing . T ) {
store := newTestStore ( t )
resolved := resolveDailyAt ( t , "2026-05-29T05:00:00-05:00" )
2026-06-09 21:33:34 +00:00
briefingMetadata := stateBriefingMetadata ( resolved )
2026-05-29 18:25:38 -05:00
paths , err := store . Paths ( resolved )
if err != nil {
t . Fatalf ( "Paths() error = %v" , err )
}
otherDir := filepath . Join ( t . TempDir ( ) , "other-artifacts" )
2026-06-20 13:29:41 +00:00
derivedMetadataPath := filepath . Join ( otherDir , "metadata." + resolved . Metadata ( ) . RunID + ".json" )
2026-05-29 18:25:38 -05:00
2026-06-09 21:33:34 +00:00
metadata := BuildMetadataFromBriefingMetadata ( resolved , briefingMetadata , ArtifactPaths {
2026-06-09 20:58:35 +00:00
ModuleSnapshot : paths . ModuleSnapshot ,
2026-05-29 18:25:38 -05:00
Metadata : paths . Metadata ,
DataPackage : paths . DataPackage ,
Preflight : paths . Preflight ,
RenderedReport : paths . RenderedReport ,
} )
metadataPath , err := store . SaveMetadata ( context . Background ( ) , metadata )
if err != nil {
t . Fatalf ( "SaveMetadata() error = %v" , err )
}
if metadataPath != paths . Metadata {
t . Fatalf ( "SaveMetadata() path = %q, want explicit metadata path %q" , metadataPath , paths . Metadata )
}
if _ , err := os . Stat ( paths . Metadata ) ; err != nil {
t . Fatalf ( "expected explicit metadata path %q: %v" , paths . Metadata , err )
}
if _ , err := os . Stat ( derivedMetadataPath ) ; ! os . IsNotExist ( err ) {
t . Fatalf ( "derived metadata path stat error = %v, want not exist" , err )
}
2026-05-29 17:40:38 +00:00
}
2026-05-29 20:44:25 +00:00
func TestFindPriorSnapshot ( t * testing . T ) {
2026-05-29 17:40:38 +00:00
store := newTestStore ( t )
first := resolveDailyAt ( t , "2026-05-29T05:00:00-05:00" )
second := resolveDailyAt ( t , "2026-05-29T08:00:00-05:00" )
2026-06-09 21:33:34 +00:00
paths := savePriorMetadata ( t , store , first , stateBriefingMetadata ( first ) )
2026-05-29 17:40:38 +00:00
2026-05-29 20:44:25 +00:00
prior , err := store . FindPriorSnapshot ( context . Background ( ) , second )
2026-05-29 17:40:38 +00:00
if err != nil {
2026-05-29 20:44:25 +00:00
t . Fatalf ( "FindPriorSnapshot() error = %v" , err )
2026-05-29 17:40:38 +00:00
}
if prior == nil {
2026-05-29 20:44:25 +00:00
t . Fatal ( "FindPriorSnapshot() = nil, want prior snapshot" )
2026-05-29 17:40:38 +00:00
}
if prior . Metadata . RunID != first . Metadata ( ) . RunID {
t . Fatalf ( "RunID = %q, want %q" , prior . Metadata . RunID , first . Metadata ( ) . RunID )
}
2026-06-09 21:20:53 +00:00
if prior . ModuleSnapshotPath != paths . ModuleSnapshot {
t . Fatalf ( "ModuleSnapshotPath = %q, want %q" , prior . ModuleSnapshotPath , paths . ModuleSnapshot )
}
2026-05-29 17:40:38 +00:00
}
2026-06-15 14:54:37 +00:00
func TestFindPriorSnapshotSupportsToday ( t * testing . T ) {
store := newTestStore ( t )
first := resolveTodayAt ( t , "2026-05-29T05:00:00-05:00" )
second := resolveTodayAt ( t , "2026-05-29T08:00:00-05:00" )
savePriorMetadata ( t , store , first , stateBriefingMetadata ( first ) )
prior , err := store . FindPriorSnapshot ( context . Background ( ) , second )
if err != nil {
t . Fatalf ( "FindPriorSnapshot() error = %v" , err )
}
if prior == nil {
t . Fatal ( "FindPriorSnapshot() = nil, want prior Today snapshot" )
}
if prior . Metadata . RunID != first . Metadata ( ) . RunID {
t . Fatalf ( "RunID = %q, want %q" , prior . Metadata . RunID , first . Metadata ( ) . RunID )
}
}
2026-05-29 20:44:25 +00:00
func TestFindPriorSnapshotUsesValidDate ( t * testing . T ) {
2026-05-29 17:53:03 +00:00
store := newTestStore ( t )
previousDate := resolveDailyAt ( t , "2026-05-28T05:00:00-05:00" )
currentDate := resolveDailyAt ( t , "2026-05-29T05:00:00-05:00" )
2026-06-09 21:33:34 +00:00
savePriorMetadata ( t , store , previousDate , stateBriefingMetadata ( previousDate ) )
2026-05-29 17:53:03 +00:00
2026-05-29 20:44:25 +00:00
prior , err := store . FindPriorSnapshot ( context . Background ( ) , currentDate )
2026-05-29 17:53:03 +00:00
if err != nil {
2026-05-29 20:44:25 +00:00
t . Fatalf ( "FindPriorSnapshot() error = %v" , err )
2026-05-29 17:53:03 +00:00
}
if prior != nil {
2026-05-29 20:44:25 +00:00
t . Fatalf ( "FindPriorSnapshot() = %#v, want nil for different valid date" , prior )
2026-05-29 17:53:03 +00:00
}
}
2026-05-29 18:08:50 +00:00
func TestFindPriorSnapshotSupportsThreeDay ( t * testing . T ) {
store := newTestStore ( t )
first := resolveThreeDayAt ( t , "2026-05-29T05:00:00-05:00" )
second := resolveThreeDayAt ( t , "2026-05-29T08:00:00-05:00" )
2026-06-09 21:33:34 +00:00
savePriorMetadata ( t , store , first , stateBriefingMetadata ( first ) )
2026-05-29 18:08:50 +00:00
prior , err := store . FindPriorSnapshot ( context . Background ( ) , second )
if err != nil {
t . Fatalf ( "FindPriorSnapshot() error = %v" , err )
}
if prior == nil {
t . Fatal ( "FindPriorSnapshot() = nil, want prior 3-day snapshot" )
}
if prior . Metadata . RunID != first . Metadata ( ) . RunID {
t . Fatalf ( "RunID = %q, want %q" , prior . Metadata . RunID , first . Metadata ( ) . RunID )
}
}
2026-05-29 18:16:10 +00:00
func TestFindPriorSnapshotSupportsWeekend ( t * testing . T ) {
store := newTestStore ( t )
first := resolveWeekendAt ( t , "2026-05-29T05:00:00-05:00" )
second := resolveWeekendAt ( t , "2026-05-29T08:00:00-05:00" )
2026-06-09 21:33:34 +00:00
savePriorMetadata ( t , store , first , stateBriefingMetadata ( first ) )
2026-05-29 18:16:10 +00:00
prior , err := store . FindPriorSnapshot ( context . Background ( ) , second )
if err != nil {
t . Fatalf ( "FindPriorSnapshot() error = %v" , err )
}
if prior == nil {
t . Fatal ( "FindPriorSnapshot() = nil, want prior weekend snapshot" )
}
if prior . Metadata . RunID != first . Metadata ( ) . RunID {
t . Fatalf ( "RunID = %q, want %q" , prior . Metadata . RunID , first . Metadata ( ) . RunID )
}
}
func TestFindPriorSnapshotSupportsNarrowedWeekendPeriod ( t * testing . T ) {
store := newTestStore ( t )
first := resolveWeekendAt ( t , "2026-05-29T19:00:00-05:00" )
second := resolveWeekendAt ( t , "2026-05-30T08:00:00-05:00" )
2026-06-09 21:33:34 +00:00
savePriorMetadata ( t , store , first , stateBriefingMetadata ( first ) )
2026-05-29 18:16:10 +00:00
prior , err := store . FindPriorSnapshot ( context . Background ( ) , second )
if err != nil {
t . Fatalf ( "FindPriorSnapshot() error = %v" , err )
}
if prior == nil {
t . Fatal ( "FindPriorSnapshot() = nil, want prior narrowed weekend snapshot" )
}
if prior . Metadata . RunID != first . Metadata ( ) . RunID {
t . Fatalf ( "RunID = %q, want %q" , prior . Metadata . RunID , first . Metadata ( ) . RunID )
}
}
2026-06-12 17:28:26 +00:00
func TestFindPriorSnapshotIgnoresRollingWindowReports ( t * testing . T ) {
store := newTestStore ( t )
2026-06-14 04:26:05 +00:00
first := resolveHourlyAt ( t , "2026-05-29T05:00:00-05:00" )
second := resolveHourlyAt ( t , "2026-05-29T06:00:00-05:00" )
2026-06-12 17:28:26 +00:00
savePriorMetadata ( t , store , first , stateBriefingMetadata ( first ) )
prior , err := store . FindPriorSnapshot ( context . Background ( ) , second )
if err != nil {
t . Fatalf ( "FindPriorSnapshot() error = %v" , err )
}
if prior != nil {
t . Fatalf ( "FindPriorSnapshot() = %#v, want nil for rolling-window comparison" , prior )
}
}
2026-05-29 17:40:38 +00:00
func TestFilesystemStoreRejectsUnsafeDirs ( t * testing . T ) {
cfg := config . Defaults ( ) . Workspace
cfg . Root = t . TempDir ( )
cfg . SnapshotsDir = "../snapshots"
_ , err := NewFilesystemStore ( cfg )
if err == nil {
t . Fatal ( "NewFilesystemStore() error = nil, want unsafe path error" )
}
if ! strings . Contains ( err . Error ( ) , "within workspace root" ) {
t . Fatalf ( "error = %q, want path safety context" , err . Error ( ) )
}
}
func newTestStore ( t * testing . T ) * FilesystemStore {
t . Helper ( )
cfg := config . Defaults ( ) . Workspace
cfg . Root = t . TempDir ( )
store , err := NewFilesystemStore ( cfg )
if err != nil {
t . Fatalf ( "NewFilesystemStore() error = %v" , err )
}
return store
}
2026-06-17 20:39:22 +00:00
func mustLoadStateLocation ( t * testing . T , name string ) * time . Location {
t . Helper ( )
location , err := time . LoadLocation ( name )
if err != nil {
t . Fatalf ( "LoadLocation(%q) error = %v" , name , err )
}
return location
}
2026-05-29 17:40:38 +00:00
func resolveDailyAt ( t * testing . T , value string ) report . Resolved {
2026-06-17 14:39:53 -05:00
t . Helper ( )
return resolveDailyForDateAt ( t , value , value )
}
func resolveDailyForDateAt ( t * testing . T , nowValue string , dateValue string ) report . Resolved {
2026-05-29 17:40:38 +00:00
t . Helper ( )
location , err := timeutil . LoadLocation ( "America/Chicago" )
if err != nil {
t . Fatalf ( "LoadLocation() error = %v" , err )
}
2026-06-17 14:39:53 -05:00
now , err := time . Parse ( time . RFC3339 , nowValue )
if err != nil {
t . Fatalf ( "parse now time: %v" , err )
}
date , err := time . Parse ( time . RFC3339 , dateValue )
2026-05-29 17:40:38 +00:00
if err != nil {
2026-06-17 14:39:53 -05:00
t . Fatalf ( "parse date time: %v" , err )
2026-05-29 17:40:38 +00:00
}
2026-06-15 16:43:06 +00:00
resolved , err := report . DefaultRegistry ( ) . Resolve ( report . Daily , report . ResolveRequest {
2026-05-29 17:40:38 +00:00
Now : now ,
Location : location ,
2026-06-17 14:39:53 -05:00
Date : date ,
2026-05-29 17:40:38 +00:00
} )
if err != nil {
t . Fatalf ( "Resolve() error = %v" , err )
}
return resolved
}
2026-06-15 14:54:37 +00:00
func resolveTodayAt ( t * testing . T , value string ) report . Resolved {
t . Helper ( )
location , err := timeutil . LoadLocation ( "America/Chicago" )
if err != nil {
t . Fatalf ( "LoadLocation() error = %v" , err )
}
now , err := time . Parse ( time . RFC3339 , value )
if err != nil {
t . Fatalf ( "parse time: %v" , err )
}
resolved , err := report . DefaultRegistry ( ) . Resolve ( report . Today , report . ResolveRequest {
Now : now ,
Location : location ,
} )
if err != nil {
t . Fatalf ( "Resolve() error = %v" , err )
}
return resolved
}
2026-05-29 18:08:50 +00:00
func resolveThreeDayAt ( t * testing . T , value string ) report . Resolved {
t . Helper ( )
location , err := timeutil . LoadLocation ( "America/Chicago" )
if err != nil {
t . Fatalf ( "LoadLocation() error = %v" , err )
}
now , err := time . Parse ( time . RFC3339 , value )
if err != nil {
t . Fatalf ( "parse time: %v" , err )
}
resolved , err := report . DefaultRegistry ( ) . Resolve ( report . ThreeDay , report . ResolveRequest {
Now : now ,
Location : location ,
} )
if err != nil {
t . Fatalf ( "Resolve() error = %v" , err )
}
return resolved
}
2026-05-29 18:16:10 +00:00
func resolveWeekendAt ( t * testing . T , value string ) report . Resolved {
t . Helper ( )
location , err := timeutil . LoadLocation ( "America/Chicago" )
if err != nil {
t . Fatalf ( "LoadLocation() error = %v" , err )
}
now , err := time . Parse ( time . RFC3339 , value )
if err != nil {
t . Fatalf ( "parse time: %v" , err )
}
resolved , err := report . DefaultRegistry ( ) . Resolve ( report . Weekend , report . ResolveRequest {
Now : now ,
Location : location ,
} )
if err != nil {
t . Fatalf ( "Resolve() error = %v" , err )
}
return resolved
}
2026-06-14 04:26:05 +00:00
func resolveHourlyAt ( t * testing . T , value string ) report . Resolved {
2026-06-12 17:28:26 +00:00
t . Helper ( )
location , err := timeutil . LoadLocation ( "America/Chicago" )
if err != nil {
t . Fatalf ( "LoadLocation() error = %v" , err )
}
now , err := time . Parse ( time . RFC3339 , value )
if err != nil {
t . Fatalf ( "parse time: %v" , err )
}
2026-06-14 04:26:05 +00:00
resolved , err := report . DefaultRegistry ( ) . Resolve ( report . Hourly , report . ResolveRequest {
2026-06-12 17:28:26 +00:00
Now : now ,
Location : location ,
} )
if err != nil {
t . Fatalf ( "Resolve() error = %v" , err )
}
return resolved
}
2026-06-14 23:49:18 +00:00
func resolveTomorrowAt ( t * testing . T , value string ) report . Resolved {
t . Helper ( )
location , err := timeutil . LoadLocation ( "America/Chicago" )
if err != nil {
t . Fatalf ( "LoadLocation() error = %v" , err )
}
now , err := time . Parse ( time . RFC3339 , value )
if err != nil {
t . Fatalf ( "parse time: %v" , err )
}
resolved , err := report . DefaultRegistry ( ) . Resolve ( report . Tomorrow , report . ResolveRequest {
Now : now ,
Location : location ,
} )
if err != nil {
t . Fatalf ( "Resolve() error = %v" , err )
}
return resolved
}
2026-06-09 21:33:34 +00:00
func stateBriefingMetadata ( resolved report . Resolved ) briefing . Metadata {
return briefing . Metadata {
RunID : resolved . Metadata ( ) . RunID ,
ReportID : resolved . Definition . ID ,
Variant : "today" ,
PromptID : resolved . Definition . PromptID ,
GeneratedAt : resolved . GeneratedAt ,
Units : "us" ,
Timezone : resolved . Timezone ,
Location : & briefing . LocationContext {
ID : "home" ,
Name : "Brentwood" ,
Region : "St. Louis Metro" ,
Timezone : resolved . Timezone ,
2026-05-29 17:40:38 +00:00
} ,
2026-06-09 21:33:34 +00:00
ValidPeriod : resolved . ValidPeriod ,
2026-05-29 17:40:38 +00:00
}
}
2026-06-09 21:33:34 +00:00
func savePriorMetadata ( t * testing . T , store * FilesystemStore , resolved report . Resolved , metadata briefing . Metadata ) ArtifactPaths {
t . Helper ( )
paths , err := store . Paths ( resolved )
if err != nil {
t . Fatalf ( "Paths() error = %v" , err )
2026-05-29 18:08:50 +00:00
}
2026-06-09 21:33:34 +00:00
_ , err = store . SaveMetadata ( context . Background ( ) , BuildMetadataFromBriefingMetadata ( resolved , metadata , ArtifactPaths {
ModuleSnapshot : paths . ModuleSnapshot ,
Metadata : paths . Metadata ,
DataPackage : paths . DataPackage ,
Preflight : paths . Preflight ,
RenderedReport : paths . RenderedReport ,
} ) )
if err != nil {
t . Fatalf ( "SaveMetadata() error = %v" , err )
2026-05-29 18:16:10 +00:00
}
2026-06-09 21:33:34 +00:00
return paths
2026-05-29 18:16:10 +00:00
}
2026-05-29 17:40:38 +00:00
func pathsString ( paths ArtifactPaths ) string {
return strings . Join ( [ ] string {
paths . Metadata ,
2026-06-09 20:58:35 +00:00
paths . ModuleSnapshot ,
2026-05-29 17:40:38 +00:00
paths . DataPackage ,
paths . Preflight ,
2026-06-07 20:21:26 -05:00
paths . Notification ,
2026-05-29 17:40:38 +00:00
paths . RenderedReport ,
2026-06-14 04:51:52 +00:00
paths . GeneratedTextRaw ,
paths . GeneratedTextResult ,
paths . GeneratedText ,
paths . RenderContext ,
2026-05-29 17:40:38 +00:00
} , "\n" )
}