storage

Tesztelés

A storagetest konformancia-suite, és amit egy saját Drivernek implementálnia kell.

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ásd Disk.ProvidesTemporaryURLs).
  • Az opcionális Mover interfészt csak akkor implementáld, ha a backendnek van natív, nem-másoló mozgatása; egyébként hagyd implementálatlanul, és a Disk.Move a drivered helyett Copy + 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, egy List lap, ami nem lapoz helyesen), amit egy kézzel írt smoke test nem kapna el.

Kapcsolódó oldalak

  • Áttekintés: a teljes Driver szerződés és a Mover interfé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.
Copyright © 2026