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
This commit is contained in:
2026-10-05 23:06:10 +00:00
parent af9ac6d979
commit 5ac0da65ee
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 # 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 - 2026-10-05: The Go module path is `sneak.berlin/go/secret`, as
`REPO_POLICIES.md` requires, not `git.eeqj.de/sneak/secret` `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 (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/spf13/cobra"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
"sneak.berlin/go/secret/internal/vault" "sneak.berlin/go/secret/internal/vault"
"sneak.berlin/go/secret/pkg/agehd" "sneak.berlin/go/secret/pkg/agehd"
) )
@@ -22,45 +21,6 @@ import (
// testVaultName is the vault name used by the size tests. // testVaultName is the vault name used by the size tests.
const testVaultName = "test-vault" 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 // newSizeTestVault creates an in-memory vault unlocked with the test
// mnemonic and returns the filesystem and vault. // 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 // runAddSecretSizeCase adds a secret of the given size through stdin and
// verifies the outcome: wantErr, or the secret stored when wantErr is nil. // verifies that it is stored.
func runAddSecretSizeCase(t *testing.T, size int, wantErr error) { func runAddSecretSizeCase(t *testing.T, size int) {
t.Helper() t.Helper()
skipIfLockedMemoryTooLow(t, size)
fs, vlt := newSizeTestVault(t) fs, vlt := newSizeTestVault(t)
@@ -127,13 +86,6 @@ func runAddSecretSizeCase(t *testing.T, size int, wantErr error) {
// Test adding the secret // Test adding the secret
secretName := fmt.Sprintf("test-secret-%d", size) secretName := fmt.Sprintf("test-secret-%d", size)
err = cli.AddSecret(secretName, false) err = cli.AddSecret(secretName, false)
if wantErr != nil {
require.ErrorIs(t, err, wantErr)
return
}
require.NoError(t, err) require.NoError(t, err)
// Verify the secret was stored correctly // 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 // runImportSecretSizeCase imports a secret file of the given size and
// verifies the outcome: wantErr, or the secret stored when wantErr is nil. // verifies that it is stored.
func runImportSecretSizeCase(t *testing.T, size int, wantErr error) { func runImportSecretSizeCase(t *testing.T, size int) {
t.Helper() t.Helper()
skipIfLockedMemoryTooLow(t, size)
fs, vlt := newSizeTestVault(t) fs, vlt := newSizeTestVault(t)
@@ -179,13 +130,6 @@ func runImportSecretSizeCase(t *testing.T, size int, wantErr error) {
// Test importing the secret // Test importing the secret
secretName := fmt.Sprintf("imported-secret-%d", size) secretName := fmt.Sprintf("imported-secret-%d", size)
err = cli.ImportSecret(cmd, secretName, testFile, false) err = cli.ImportSecret(cmd, secretName, testFile, false)
if wantErr != nil {
require.ErrorIs(t, err, wantErr)
return
}
require.NoError(t, err) require.NoError(t, err)
// Verify the secret was stored correctly // 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 // 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) { func TestAddSecretVariousSizes(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
size int size int
wantErr error
}{ }{
{ {
name: "1KB secret", name: "1KB secret",
@@ -223,40 +166,22 @@ func TestAddSecretVariousSizes(t *testing.T) {
name: "1MB secret", name: "1MB secret",
size: 1024 * 1024, 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 { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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 // 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) { func TestImportSecretVariousSizes(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
size int size int
wantErr error
}{ }{
{ {
name: "1KB file", name: "1KB file",
@@ -274,35 +199,18 @@ func TestImportSecretVariousSizes(t *testing.T) {
name: "1MB file", name: "1MB file",
size: 1024 * 1024, 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 { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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 // 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) { func TestAddSecretBufferGrowth(t *testing.T) {
// Test various sizes that should trigger buffer growth // Test various sizes that should trigger buffer growth
sizes := []int{ sizes := []int{
@@ -321,13 +229,10 @@ func TestAddSecretBufferGrowth(t *testing.T) {
131072, // 128KB 131072, // 128KB
524288, // 512KB 524288, // 512KB
1048576, // 1MB 1048576, // 1MB
2097152, // 2MB
} }
for _, size := range sizes { for _, size := range sizes {
t.Run(fmt.Sprintf("size_%d", size), func(t *testing.T) { t.Run(fmt.Sprintf("size_%d", size), func(t *testing.T) {
skipIfLockedMemoryTooLow(t, size)
fs, vlt := newSizeTestVault(t) fs, vlt := newSizeTestVault(t)
// Create test data of exactly the specified size // Create test data of exactly the specified size