Add tests for internal/storage: URL parsing, backends, shared conformance suite (closes #66)
internal/storage, the package that parses store URLs and selects the backend, had no tests. Adds table-driven tests for URL parsing (each scheme, query parameters, malformed input, unknown scheme, backend type chosen); one shared conformance suite for the Storer interface, run against the file backend in a temp directory and the s3 backend on the in-process harness internal/s3 already uses, so a new backend inherits it; and rclone construction and argument tests using its in-process local backend. A comment records that rclone data operations need a configured remote and are not unit-tested. No production code changed and no defect surfaced. model: claude-opus-4-8 (implementation, review); claude-fable-5-1 (merge)
This commit was merged in pull request #144.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user