189 lines
5.5 KiB
Go
189 lines
5.5 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/distributor/internal/testutil"
|
|
)
|
|
|
|
func TestInspectPrintsBundleSummary(t *testing.T) {
|
|
var stdout bytes.Buffer
|
|
err := Inspect(context.Background(), InspectOptions{
|
|
Path: filepath.Join("..", "bundle", "testdata", "valid_bundle"),
|
|
Stdout: &stdout,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Inspect() error = %v", err)
|
|
}
|
|
output := stdout.String()
|
|
for _, want := range []string{
|
|
"Bundles: 1",
|
|
"path=.",
|
|
"id=weather.daily.brentwood.2026-05-30",
|
|
"created=2026-05-30T11:10:00Z",
|
|
"report.md size=16",
|
|
"summary.txt size=8",
|
|
} {
|
|
if !strings.Contains(output, want) {
|
|
t.Fatalf("Inspect() output = %q, want substring %q", output, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInspectConfiguredLocalSource(t *testing.T) {
|
|
sourceRoot := t.TempDir()
|
|
destinationRoot := t.TempDir()
|
|
testutil.WriteSourceBundle(t, sourceRoot, "daily", testutil.BundleOptions{ID: "reports.daily"})
|
|
var stdout bytes.Buffer
|
|
|
|
err := Inspect(context.Background(), InspectOptions{
|
|
ConfigPath: testutil.WriteMinimalLocalConfig(t, sourceRoot, destinationRoot),
|
|
PipelineID: "reports",
|
|
Stdout: &stdout,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("Inspect() configured source error = %v", err)
|
|
}
|
|
output := stdout.String()
|
|
for _, want := range []string{
|
|
"Pipeline: reports",
|
|
"Source: local",
|
|
"Bundles: 1",
|
|
"path=daily",
|
|
"id=reports.daily",
|
|
} {
|
|
if !strings.Contains(output, want) {
|
|
t.Fatalf("Inspect() output = %q, want substring %q", output, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInspectConfiguredSourceJSON(t *testing.T) {
|
|
sourceRoot := t.TempDir()
|
|
destinationRoot := t.TempDir()
|
|
testutil.WriteSourceBundle(t, sourceRoot, "", testutil.BundleOptions{ID: "reports.json"})
|
|
var stdout bytes.Buffer
|
|
|
|
err := Inspect(context.Background(), InspectOptions{
|
|
ConfigPath: testutil.WriteMinimalLocalConfig(t, sourceRoot, destinationRoot),
|
|
PipelineID: "reports",
|
|
Stdout: &stdout,
|
|
OutputFormat: OutputFormatJSON,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("Inspect() configured JSON error = %v", err)
|
|
}
|
|
result := decodeAppResult(t, stdout.String())
|
|
if result["pipeline_id"] != "reports" || result["source_backend"] != "local" || result["bundle_count"] != float64(1) {
|
|
t.Fatalf("result = %#v, want configured inspect metadata", result)
|
|
}
|
|
}
|
|
|
|
func TestInspectJSONPreservesCreatedOffsetAndFileMetadata(t *testing.T) {
|
|
sourceRoot := t.TempDir()
|
|
created := time.Date(2026, 6, 1, 6, 30, 0, 0, time.FixedZone("CDT", -5*60*60))
|
|
testutil.WriteSourceBundle(t, sourceRoot, "daily", testutil.BundleOptions{
|
|
ID: "reports.offset",
|
|
Created: created,
|
|
Files: []testutil.SourceFile{
|
|
{Path: "report.md", Data: "# Report\n"},
|
|
},
|
|
})
|
|
var stdout bytes.Buffer
|
|
|
|
err := Inspect(context.Background(), InspectOptions{
|
|
Path: sourceRoot,
|
|
Stdout: &stdout,
|
|
OutputFormat: OutputFormatJSON,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("Inspect() error = %v", err)
|
|
}
|
|
result := decodeAppResult(t, stdout.String())
|
|
bundles, ok := result["bundles"].([]any)
|
|
if !ok || len(bundles) != 1 {
|
|
t.Fatalf("bundles = %#v, want one bundle", result["bundles"])
|
|
}
|
|
bundle, ok := bundles[0].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("bundle = %#v, want object", bundles[0])
|
|
}
|
|
if bundle["created"] != "2026-06-01T06:30:00-05:00" || bundle["file_count"] != float64(1) {
|
|
t.Fatalf("bundle = %#v, want offset timestamp and file count", bundle)
|
|
}
|
|
files, ok := bundle["files"].([]any)
|
|
if !ok || len(files) != 1 {
|
|
t.Fatalf("files = %#v, want one file", bundle["files"])
|
|
}
|
|
file, ok := files[0].(map[string]any)
|
|
if !ok || file["path"] != "report.md" || file["sha256"] == "" || file["size"] != float64(9) {
|
|
t.Fatalf("file = %#v, want projected file metadata", file)
|
|
}
|
|
}
|
|
|
|
func TestInspectConfiguredSourceJSONIncludesSecretConflictWarningWithoutValues(t *testing.T) {
|
|
name := "DISTRIBUTOR_TEST_INSPECT_SECRET"
|
|
t.Setenv(name, "process-value")
|
|
sourceRoot := t.TempDir()
|
|
destinationRoot := t.TempDir()
|
|
secretsRoot := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(secretsRoot, name), []byte("secret-value\n"), 0o600); err != nil {
|
|
t.Fatalf("write secret: %v", err)
|
|
}
|
|
testutil.WriteSourceBundle(t, sourceRoot, "", testutil.BundleOptions{ID: "reports.json"})
|
|
configPath := writeConfigFile(t, `
|
|
secrets:
|
|
directory: `+secretsRoot+`
|
|
pipelines:
|
|
- id: reports
|
|
source:
|
|
backend: local
|
|
path: `+sourceRoot+`
|
|
destinations:
|
|
- id: archive
|
|
backend: local
|
|
path: `+destinationRoot+`
|
|
`)
|
|
|
|
var stdout bytes.Buffer
|
|
err := Inspect(context.Background(), InspectOptions{
|
|
ConfigPath: configPath,
|
|
PipelineID: "reports",
|
|
Stdout: &stdout,
|
|
OutputFormat: OutputFormatJSON,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Inspect() error = %v", err)
|
|
}
|
|
var envelope struct {
|
|
Warnings []OutputWarning `json:"warnings"`
|
|
}
|
|
if err := json.Unmarshal(stdout.Bytes(), &envelope); err != nil {
|
|
t.Fatalf("decode output: %v; output = %q", err, stdout.String())
|
|
}
|
|
if len(envelope.Warnings) != 1 || !strings.Contains(envelope.Warnings[0].Message, "secret "+name+" ignored") {
|
|
t.Fatalf("warnings = %#v, want secret conflict warning", envelope.Warnings)
|
|
}
|
|
output := stdout.String()
|
|
if strings.Contains(output, "process-value") || strings.Contains(output, "secret-value") {
|
|
t.Fatalf("stdout exposed secret values: %q", output)
|
|
}
|
|
}
|
|
|
|
func TestInspectRequiresPath(t *testing.T) {
|
|
err := Inspect(context.Background(), InspectOptions{})
|
|
if err == nil || !strings.Contains(err.Error(), "requires a path") {
|
|
t.Fatalf("Inspect() error = %v, want required path", err)
|
|
}
|
|
}
|