check / check (pull_request) Failing after 1s
internal/storage had no tests. This adds table-driven coverage for ParseStorageURL (each scheme, query parameters, missing components, an unknown scheme, and the fields that decide which backend is built) and a backend-agnostic Storer conformance suite run against the file:// backend over a temp directory: put, get, stat, list with prefix filtering, overwrite, delete, delete-of-missing, and not-found on Get and Stat. Tests only; no production code changed and no defect surfaced. New files (url_parse_test.go, file_backend_test.go) avoid colliding with the s3_test.go and file-backend work in flight on other branches. The conformance helper takes a constructor, so s3:// and rclone:// can adopt it once their harness is wired. 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)
|
|
}
|
|
})
|
|
}
|
|
}
|