check / check (pull_request) Failing after 1s
internal/storage had no tests. This adds table-driven ParseStorageURL coverage and a shared Storer conformance suite, defined once in conformance_test.go and run against every backend that can run in-process: file:// over a temp dir and s3:// over the same in-memory S3 harness (gofakes3 + s3mem) that internal/s3 and the not-found test use, so a new backend inherits the contract by passing its constructor. The rclone backend gets construction and argument-shaping tests via rclone's in-process ":local" backend, plus the missing-remote error path; a comment records that its data-plane operations need a configured remote and so cannot run in a unit test. Tests only; no production code changed and no defect surfaced. Model: opus-4-8
111 lines
2.8 KiB
Go
111 lines
2.8 KiB
Go
package storage_test
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"sneak.berlin/go/vaultik/internal/storage"
|
|
)
|
|
|
|
// TestParseStorageURLValid checks that each supported scheme parses into
|
|
// the expected fields, since those fields decide which backend is built.
|
|
func TestParseStorageURLValid(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const bucket = "mybucket"
|
|
|
|
cases := []struct {
|
|
name string
|
|
raw string
|
|
want *storage.URL
|
|
}{
|
|
{
|
|
name: "file absolute path",
|
|
raw: "file:///var/backups/vaultik",
|
|
want: &storage.URL{Scheme: "file", Prefix: "/var/backups/vaultik"},
|
|
},
|
|
{
|
|
name: "s3 bucket and prefix, ssl defaults on",
|
|
raw: "s3://mybucket/backups/host",
|
|
want: &storage.URL{
|
|
Scheme: "s3", Bucket: bucket,
|
|
Prefix: "backups/host", UseSSL: true,
|
|
},
|
|
},
|
|
{
|
|
name: "s3 bucket only",
|
|
raw: "s3://mybucket",
|
|
want: &storage.URL{Scheme: "s3", Bucket: bucket, UseSSL: true},
|
|
},
|
|
{
|
|
name: "s3 with endpoint, region, ssl off",
|
|
raw: "s3://mybucket?endpoint=minio.example.com®ion=us-west-2&ssl=false",
|
|
want: &storage.URL{
|
|
Scheme: "s3", Bucket: bucket,
|
|
Endpoint: "minio.example.com", Region: "us-west-2", UseSSL: false,
|
|
},
|
|
},
|
|
{
|
|
name: "rclone remote and path",
|
|
raw: "rclone://gdrive/backups/host",
|
|
want: &storage.URL{
|
|
Scheme: "rclone", RcloneRemote: "gdrive", Prefix: "backups/host",
|
|
},
|
|
},
|
|
{
|
|
name: "rclone remote only",
|
|
raw: "rclone://gdrive",
|
|
want: &storage.URL{Scheme: "rclone", RcloneRemote: "gdrive"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := storage.ParseStorageURL(tc.raw)
|
|
if err != nil {
|
|
t.Fatalf("ParseStorageURL(%q) returned error: %v", tc.raw, err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(got, tc.want) {
|
|
t.Errorf("ParseStorageURL(%q) = %+v, want %+v", tc.raw, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestParseStorageURLErrors checks that empty, missing, and unknown-scheme
|
|
// inputs fail with the documented sentinel errors instead of parsing to a
|
|
// wrong destination.
|
|
func TestParseStorageURLErrors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
name string
|
|
raw string
|
|
wantErr error
|
|
}{
|
|
{"empty url", "", storage.ErrEmptyStorageURL},
|
|
{"file empty path", "file://", storage.ErrEmptyFilePath},
|
|
{"s3 missing bucket", "s3://", storage.ErrMissingBucket},
|
|
{"s3 missing bucket with path", "s3:///justprefix", storage.ErrMissingBucket},
|
|
{"rclone missing remote", "rclone://", storage.ErrMissingRemote},
|
|
{"unknown scheme", "gs://bucket/x", storage.ErrUnsupportedScheme},
|
|
{"no scheme", "/local/path", storage.ErrUnsupportedScheme},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := storage.ParseStorageURL(tc.raw)
|
|
if !errors.Is(err, tc.wantErr) {
|
|
t.Errorf("ParseStorageURL(%q) error = %v, want %v",
|
|
tc.raw, err, tc.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|