Add source bundle validation and inspection
This commit is contained in:
@@ -3,12 +3,63 @@ package app
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/adapters/local"
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/bundle"
|
||||
)
|
||||
|
||||
type InspectOptions struct {
|
||||
Path string
|
||||
Path string
|
||||
Stdout io.Writer
|
||||
}
|
||||
|
||||
func Inspect(context.Context, InspectOptions) error {
|
||||
return fmt.Errorf("inspect command: %w", ErrNotImplemented)
|
||||
func Inspect(ctx context.Context, options InspectOptions) error {
|
||||
if options.Path == "" {
|
||||
return fmt.Errorf("inspect command requires a path")
|
||||
}
|
||||
backend, err := local.New(options.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bundles, err := bundle.Discover(ctx, backend, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeInspection(options.Stdout, bundles)
|
||||
}
|
||||
|
||||
func writeInspection(w io.Writer, bundles []bundle.Bundle) error {
|
||||
if w == nil {
|
||||
return nil
|
||||
}
|
||||
if _, err := fmt.Fprintf(w, "Bundles: %d\n", len(bundles)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, sourceBundle := range bundles {
|
||||
if _, err := fmt.Fprintf(
|
||||
w,
|
||||
"- path=%s id=%s created=%s digest=%s files=%d\n",
|
||||
displayBundlePath(sourceBundle.RootRelativePath),
|
||||
sourceBundle.Manifest.ID,
|
||||
sourceBundle.Manifest.Created.Format("2006-01-02T15:04:05Z07:00"),
|
||||
sourceBundle.Manifest.Digest,
|
||||
len(sourceBundle.Manifest.Files),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range sourceBundle.Manifest.Files {
|
||||
if _, err := fmt.Fprintf(w, " - %s size=%d sha256=%s\n", file.Path, file.Size, file.SHA256); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func displayBundlePath(path string) string {
|
||||
if path == "" {
|
||||
return "."
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
40
internal/app/inspect_test.go
Normal file
40
internal/app/inspect_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,31 @@ package app
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/adapters/local"
|
||||
"gitea.maximumdirect.net/eric/distributor/internal/bundle"
|
||||
)
|
||||
|
||||
type ValidateOptions struct {
|
||||
Path string
|
||||
Path string
|
||||
Stdout io.Writer
|
||||
}
|
||||
|
||||
func Validate(context.Context, ValidateOptions) error {
|
||||
return fmt.Errorf("validate command: %w", ErrNotImplemented)
|
||||
func Validate(ctx context.Context, options ValidateOptions) error {
|
||||
if options.Path == "" {
|
||||
return fmt.Errorf("validate command requires a path")
|
||||
}
|
||||
backend, err := local.New(options.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bundles, err := bundle.Discover(ctx, backend, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if options.Stdout != nil {
|
||||
_, err = fmt.Fprintf(options.Stdout, "Validated %d bundle(s)\n", len(bundles))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
39
internal/app/validate_test.go
Normal file
39
internal/app/validate_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateLocalBundle(t *testing.T) {
|
||||
var stdout bytes.Buffer
|
||||
err := Validate(context.Background(), ValidateOptions{
|
||||
Path: filepath.Join("..", "bundle", "testdata", "valid_bundle"),
|
||||
Stdout: &stdout,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Validate() error = %v", err)
|
||||
}
|
||||
if got, want := stdout.String(), "Validated 1 bundle(s)\n"; got != want {
|
||||
t.Fatalf("stdout = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateExampleSourceBundle(t *testing.T) {
|
||||
err := Validate(context.Background(), ValidateOptions{
|
||||
Path: filepath.Join("..", "..", "examples", "source-bundle"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Validate() example error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRequiresPath(t *testing.T) {
|
||||
err := Validate(context.Background(), ValidateOptions{})
|
||||
if err == nil || !strings.Contains(err.Error(), "requires a path") {
|
||||
t.Fatalf("Validate() error = %v, want required path", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user