Tesztelés
A storage/storagetest egy közös konformancia-teszt-suite: ugyanaz a viselkedési tesztkészlet fut le minden Driver-implementáción, beépítettn vagy sajáton egyaránt, így az, hogy "tényleg úgy viselkedik-e ez a driver, mint a többi", egyetlen függvény futtatásával eldönthető kérdés, nem driverenként külön, kézzel megírt tesztfájlé.
Futtatás egy Driver ellen
import (
"testing"
"github.com/gp-system/storage/storagetest"
)
func TestMyDriver(t *testing.T) {
storagetest.Run(t, func(t *testing.T) storage.Driver {
dir := t.TempDir()
driver, err := local.New(local.Config{Root: dir, Secret: "test-secret"})
if err != nil {
t.Fatal(err)
}
return driver
}, storagetest.Options{})
}
A Run(t, factory, opts) egy friss-drivert-adó factoryt vár, amit subtestenként egyszer hív, hogy a tesztek sose osszanak meg állapotot, plusz egy storagetest.Options-t. Ezután végigmegy a teljes Driver felületen: put/read kör, range-olvasás, stat, listázás és lapozás, copy, delete-idempotencia, a ValidPath traversal-elutasítása, és, ahol a driver támogatja, aláírt URL-ek. Egy driver, ami ErrUnsupported-ot ad egy opcionális képességre, érvényes, csak kevésbé képes implementációnak számít, nem hibának: a storagetest azt ellenőrzi, hogy a megfelelő hibát adja-e, nem azt, hogy minden driver mindent támogat.
Opciók olyan driverekhez, amik nem őriznek meg mindent
type Options struct {
PersistsContentType bool
PersistsMetadata bool
}
Nem minden backend viszi át körben minden metaadatot; egy minimális driver (mondjuk egy csupasz key/value store fölött, attribútum-támogatás nélkül) lehet, hogy nem őrzi meg a PutOptions.ContentType-ot vagy más metaadatot egy Put/Stat kör alatt. Ahelyett, hogy minden drivert egy nem meglévő támogatás hamisítására kényszerítenénk, a storagetest.Options lehetővé teszi, hogy ezt előre jelezd: hagyd a PersistsContentType/PersistsMetadata-t a zéróértéken (false), és a suite kihagyja azokat az assertionöket, amik egyébként elbuknának egy olyan driveren, ami őszinte a saját korlátairól.
Saját Driver írása
A Driver interfész (lásd Áttekintés) a teljes felület, amit egy új backendnek le kell fednie. Egy minimális váz:
type myDriver struct {
name string
// backend-specifikus mezők
}
func (d *myDriver) Name() string { return d.name }
func (d *myDriver) Put(ctx context.Context, path string, r io.Reader, opts storage.PutOptions) error {
if !storage.ValidPath(path) {
return storage.ErrInvalidPath
}
// r írása a backendbe a path-ra
return nil
}
func (d *myDriver) Reader(ctx context.Context, path string) (io.ReadCloser, error) {
// a path-on lévő objektum, vagy storage.ErrNotExist, ha hiányzik
}
func (d *myDriver) RangeReader(ctx context.Context, path string, offset, length int64) (io.ReadCloser, error) {
// egy bájttartomány-olvasás; length < 0 azt jelenti, "a végéig"
}
func (d *myDriver) Stat(ctx context.Context, path string) (storage.FileInfo, error) { /* ... */ }
func (d *myDriver) List(ctx context.Context, opts storage.ListOptions) (storage.ListPage, error) { /* ... */ }
func (d *myDriver) Copy(ctx context.Context, src, dst string) error { /* ... */ }
func (d *myDriver) Delete(ctx context.Context, path string) error { /* idempotens */ }
func (d *myDriver) URL(ctx context.Context, path string) (string, error) { /* ... */ }
func (d *myDriver) TemporaryURL(ctx context.Context, path string, opts storage.TemporaryURLOptions) (string, error) {
return "", storage.ErrUnsupported // ha a backend nem tud URL-t aláírni
}
func (d *myDriver) TemporaryUploadURL(ctx context.Context, path string, opts storage.TemporaryUploadURLOptions) (storage.UploadURL, error) {
return storage.UploadURL{}, storage.ErrUnsupported
}
Néhány dolog, amire érdemes figyelni írás közben:
- Hívd meg a
storage.ValidPath-t minden olyan metódus elején, ami path-ot kap és hozzáér a backendhez; ez az egyetlen dolog, amit minden drivertől elvárunk, hogy osszon. - A
Delete-nek idempotensnek kell lennie: egy már törölt path törlése nem hiba. - Ha egy képesség tényleg nem alkalmazható a backendre, adj vissza
storage.ErrUnsupported-ot egy félig működő közelítés helyett; a hívóktól elvárt, hogy ezt kezeljék (lásdDisk.ProvidesTemporaryURLs). - Az opcionális
Moverinterfészt csak akkor implementáld, ha a backendnek van natív, nem-másoló mozgatása; egyébként hagyd implementálatlanul, és aDisk.Movea drivered helyettCopy+Delete-re esik vissza. - Futtasd le a
storagetest.Run-on, mielőtt bármi valósiba bekötnéd. Ez a leggyorsabb módja megtalálni egy sarkot (egy off-by-one egy range-olvasásban, egyListlap, ami nem lapoz helyesen), amit egy kézzel írt smoke test nem kapna el.
Kapcsolódó oldalak
- Áttekintés: a teljes
Driverszerződés és aMoverinterfész. - Disk API: a metódusok, amik végül a te driveredbe hívnak.
- Memória és helyi lemez: olvasd el e két driver forrását egy kidolgozott példáért a szerződésről.
- S3: a harmadik beépített driver, és az, amelyikben a legtöbb backend-specifikus logika van.