Add negative and boundary tests for blobgen and types (closes #170)
Test-only. internal/blobgen and internal/types had no negative or boundary coverage. Adds, in package blobgen_test: Writer-to-Reader round trips at the 64 KiB age-segment edges for random and compressible data, checking plaintext, byte counts and the reader/writer hashes by decrypting; a wrong-identity open; truncation and single-byte corruption of a multi-segment blob at every region; trailing bytes, empty input and garbage; rejected and accepted compression levels; nil, empty and invalid recipients; and a failing destination. In package types_test: Value/Scan round trips, NULL, wrong-type and malformed Scan, Parse and IsZero for FileID and BlobID. The "cut right after the age header and nonce" truncation is excluded: it reads as valid and empty today and belongs to #152. Model: opus-4-8
This commit was merged in pull request #192.
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sneak.berlin/go/vaultik/internal/types"
|
||||
)
|
||||
|
||||
// scannableID is the shared behaviour of the UUID-backed id types. A pointer
|
||||
// to FileID or BlobID satisfies it, so both are tested through one set of
|
||||
// cases.
|
||||
type scannableID interface {
|
||||
driver.Valuer
|
||||
sql.Scanner
|
||||
fmt.Stringer
|
||||
IsZero() bool
|
||||
}
|
||||
|
||||
// idKind adapts one id type to the generic tests below.
|
||||
type idKind struct {
|
||||
name string
|
||||
newZero func() scannableID
|
||||
newRandom func() scannableID
|
||||
parse func(string) (scannableID, error)
|
||||
}
|
||||
|
||||
func idKinds() []idKind {
|
||||
return []idKind{
|
||||
{
|
||||
name: "FileID",
|
||||
newZero: func() scannableID { return &types.FileID{} },
|
||||
newRandom: func() scannableID {
|
||||
id := types.NewFileID()
|
||||
|
||||
return &id
|
||||
},
|
||||
parse: func(s string) (scannableID, error) {
|
||||
id, err := types.ParseFileID(s)
|
||||
|
||||
return &id, err
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "BlobID",
|
||||
newZero: func() scannableID { return &types.BlobID{} },
|
||||
newRandom: func() scannableID {
|
||||
id := types.NewBlobID()
|
||||
|
||||
return &id
|
||||
},
|
||||
parse: func(s string) (scannableID, error) {
|
||||
id, err := types.ParseBlobID(s)
|
||||
|
||||
return &id, err
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TestIDValueScan checks that Value then Scan round trips from both a string
|
||||
// and a []byte, that a NULL scans to the zero id, and that a non-string type
|
||||
// and malformed text are rejected.
|
||||
func TestIDValueScan(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, k := range idKinds() {
|
||||
t.Run(k.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
orig := k.newRandom()
|
||||
v, err := orig.Value()
|
||||
require.NoError(t, err)
|
||||
|
||||
s, ok := v.(string)
|
||||
require.True(t, ok, "Value must yield a string")
|
||||
|
||||
fromString := k.newZero()
|
||||
require.NoError(t, fromString.Scan(s))
|
||||
assert.Equal(t, orig.String(), fromString.String())
|
||||
assert.False(t, fromString.IsZero())
|
||||
|
||||
fromBytes := k.newZero()
|
||||
require.NoError(t, fromBytes.Scan([]byte(s)))
|
||||
assert.Equal(t, orig.String(), fromBytes.String())
|
||||
|
||||
nulled := k.newRandom()
|
||||
require.NoError(t, nulled.Scan(nil))
|
||||
assert.True(t, nulled.IsZero(), "NULL scans to the zero id")
|
||||
|
||||
require.Error(t, k.newZero().Scan(42),
|
||||
"a non-string type must be rejected")
|
||||
assert.Error(t, k.newZero().Scan("not-a-uuid"),
|
||||
"malformed text must be rejected")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestIDParse checks that the Parse function accepts a canonical id and
|
||||
// rejects malformed text.
|
||||
func TestIDParse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, k := range idKinds() {
|
||||
t.Run(k.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
canonical := k.newRandom().String()
|
||||
|
||||
parsed, err := k.parse(canonical)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, canonical, parsed.String())
|
||||
|
||||
_, err = k.parse("not-a-uuid")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestIDIsZero checks IsZero on the zero and on a freshly generated id.
|
||||
func TestIDIsZero(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, k := range idKinds() {
|
||||
t.Run(k.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.True(t, k.newZero().IsZero())
|
||||
assert.False(t, k.newRandom().IsZero())
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user