Test only small secrets in the size tests (closes #52)
check / check (push) Failing after 2s
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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user