1 Commits
Author SHA1 Message Date
sneak 5ac0da65ee Test only small secrets in the size tests (closes #52)
check / check (push) Failing after 2s
The size tests for secret add, secret import and the stdin buffer no
longer store 2 MB to 101 MB secrets; the largest is 1 MiB. The cases
checking that a secret over the 100 MB limit is rejected are deleted
and not replaced. The limit itself is unchanged. With nothing large
left, the helper that skipped a case for want of locked memory is gone.

Model: opus-5-5
2026-10-05 23:06:10 +00:00
2 changed files with 19 additions and 108 deletions
+6
View File
@@ -18,6 +18,12 @@ https://git.eeqj.de/sneak/secret/milestone/12
# Completed Steps
- 2026-10-05: No test stores a secret larger than 1 MiB
(https://git.eeqj.de/sneak/secret/issues/52). The size tests for `secret add`,
`secret import` and the stdin buffer no longer try 2 MB, 10 MB, 99 MB, 100 MB
or 101 MB secrets, and nothing tests that a secret over the 100 MB limit is
rejected; the limit itself is unchanged. With nothing large left, the size
tests no longer skip a case for want of locked memory.
- 2026-10-05: The Go module path is `sneak.berlin/go/secret`, as
`REPO_POLICIES.md` requires, not `git.eeqj.de/sneak/secret`
(https://git.eeqj.de/sneak/secret/issues/43). Every import uses it, as do the
+13 -108
View File
@@ -14,7 +14,6 @@ import (
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
"sneak.berlin/go/secret/internal/vault"
"sneak.berlin/go/secret/pkg/agehd"
)
@@ -22,45 +21,6 @@ import (
// testVaultName is the vault name used by the size tests.
const testVaultName = "test-vault"
// lockedBytesPerSecretByte bounds the locked memory that storing a secret
// holds at once: the buffers it is read into reach up to 1.5 times its
// size, and they are then copied into one more buffer of its size.
const lockedBytesPerSecretByte = 3
// skipIfLockedMemoryTooLow skips the test when this process cannot lock
// the memory a secret of size bytes needs, found by locking a buffer of
// that size and releasing it. memguard panics, ending the whole test run,
// when it cannot lock a buffer, and a plain `docker build .` runs the
// tests under an 8 MiB locked-memory limit (RLIMIT_MEMLOCK). A process
// allowed to lock past that limit runs every case.
func skipIfLockedMemoryTooLow(t *testing.T, size int) {
t.Helper()
need := lockedBytesPerSecretByte * size
buf, err := unix.Mmap(-1, 0, need,
unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_ANON)
require.NoError(t, err)
lockErr := unix.Mlock(buf)
// Unmapping the buffer also unlocks it.
err = unix.Munmap(buf)
require.NoError(t, err)
if lockErr != nil {
var limit unix.Rlimit
err = unix.Getrlimit(unix.RLIMIT_MEMLOCK, &limit)
require.NoError(t, err)
t.Skipf("a %d-byte secret needs up to %d bytes of locked memory, "+
"which could not be locked under the locked-memory limit "+
"(RLIMIT_MEMLOCK) of %d bytes: %v",
size, need, limit.Cur, lockErr)
}
}
// newSizeTestVault creates an in-memory vault unlocked with the test
// mnemonic and returns the filesystem and vault.
//
@@ -93,10 +53,9 @@ func newSizeTestVault(t *testing.T) (afero.Fs, *vault.Vault) {
}
// runAddSecretSizeCase adds a secret of the given size through stdin and
// verifies the outcome: wantErr, or the secret stored when wantErr is nil.
func runAddSecretSizeCase(t *testing.T, size int, wantErr error) {
// verifies that it is stored.
func runAddSecretSizeCase(t *testing.T, size int) {
t.Helper()
skipIfLockedMemoryTooLow(t, size)
fs, vlt := newSizeTestVault(t)
@@ -127,13 +86,6 @@ func runAddSecretSizeCase(t *testing.T, size int, wantErr error) {
// Test adding the secret
secretName := fmt.Sprintf("test-secret-%d", size)
err = cli.AddSecret(secretName, false)
if wantErr != nil {
require.ErrorIs(t, err, wantErr)
return
}
require.NoError(t, err)
// Verify the secret was stored correctly
@@ -147,10 +99,9 @@ func runAddSecretSizeCase(t *testing.T, size int, wantErr error) {
}
// runImportSecretSizeCase imports a secret file of the given size and
// verifies the outcome: wantErr, or the secret stored when wantErr is nil.
func runImportSecretSizeCase(t *testing.T, size int, wantErr error) {
// verifies that it is stored.
func runImportSecretSizeCase(t *testing.T, size int) {
t.Helper()
skipIfLockedMemoryTooLow(t, size)
fs, vlt := newSizeTestVault(t)
@@ -179,13 +130,6 @@ func runImportSecretSizeCase(t *testing.T, size int, wantErr error) {
// Test importing the secret
secretName := fmt.Sprintf("imported-secret-%d", size)
err = cli.ImportSecret(cmd, secretName, testFile, false)
if wantErr != nil {
require.ErrorIs(t, err, wantErr)
return
}
require.NoError(t, err)
// Verify the secret was stored correctly
@@ -200,12 +144,11 @@ func runImportSecretSizeCase(t *testing.T, size int, wantErr error) {
// TestAddSecretVariousSizes tests adding secrets of various sizes through stdin
//
//nolint:paralleltest // together the subtests lock more than the memlock limit
//nolint:paralleltest // in parallel, size tests could exceed the memlock limit
func TestAddSecretVariousSizes(t *testing.T) {
tests := []struct {
name string
size int
wantErr error
name string
size int
}{
{
name: "1KB secret",
@@ -223,40 +166,22 @@ func TestAddSecretVariousSizes(t *testing.T) {
name: "1MB secret",
size: 1024 * 1024,
},
{
name: "10MB secret",
size: 10 * 1024 * 1024,
},
{
name: "99MB secret",
size: 99 * 1024 * 1024,
},
{
name: "100MB secret minus 1 byte",
size: 100*1024*1024 - 1,
},
{
name: "101MB secret - should fail",
size: 101 * 1024 * 1024,
wantErr: errSecretTooLarge,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
runAddSecretSizeCase(t, tt.size, tt.wantErr)
runAddSecretSizeCase(t, tt.size)
})
}
}
// TestImportSecretVariousSizes tests importing secrets of various sizes from files
//
//nolint:paralleltest // together the subtests lock more than the memlock limit
//nolint:paralleltest // in parallel, size tests could exceed the memlock limit
func TestImportSecretVariousSizes(t *testing.T) {
tests := []struct {
name string
size int
wantErr error
name string
size int
}{
{
name: "1KB file",
@@ -274,35 +199,18 @@ func TestImportSecretVariousSizes(t *testing.T) {
name: "1MB file",
size: 1024 * 1024,
},
{
name: "10MB file",
size: 10 * 1024 * 1024,
},
{
name: "99MB file",
size: 99 * 1024 * 1024,
},
{
name: "100MB file",
size: 100 * 1024 * 1024,
},
{
name: "101MB file - should fail",
size: 101 * 1024 * 1024,
wantErr: errSecretTooLarge,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
runImportSecretSizeCase(t, tt.size, tt.wantErr)
runImportSecretSizeCase(t, tt.size)
})
}
}
// TestAddSecretBufferGrowth tests that our buffer growth strategy works correctly
//
//nolint:paralleltest // together the subtests lock more than the memlock limit
//nolint:paralleltest // in parallel, size tests could exceed the memlock limit
func TestAddSecretBufferGrowth(t *testing.T) {
// Test various sizes that should trigger buffer growth
sizes := []int{
@@ -321,13 +229,10 @@ func TestAddSecretBufferGrowth(t *testing.T) {
131072, // 128KB
524288, // 512KB
1048576, // 1MB
2097152, // 2MB
}
for _, size := range sizes {
t.Run(fmt.Sprintf("size_%d", size), func(t *testing.T) {
skipIfLockedMemoryTooLow(t, size)
fs, vlt := newSizeTestVault(t)
// Create test data of exactly the specified size