All checks were successful
check / check (push) Successful in 5s
Clears the final 80 golangci-lint findings under the canonical .golangci.yml (sha256 021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb), taking the repo from red to green: script/cibuild exits 0. - wsl_v5 (60): blank line above defer/go statements sharing no variable with the line above; blank-line-only diff. - sqlclosecheck (10): the package-local CloseRows helper hid the close from the analyzer. Helper removed; all 18 call sites now defer an inline rows.Close(), preserving the fatal-on-close-error path. No resource leak existed - the rows were always being closed. - prealloc (3): append targets given a starting capacity. - revive (3): package-name findings suppressed with per-site directives pending the naming decision tracked in #76. No gosec suppressions are needed under the pinned linter. .golangci.yml, Dockerfile, Makefile, .gitea/ and script/ are byte-identical to main. Verified with script/cibuild (digest-pinned golangci-lint v2.12.2), not make check - the latter resolves the linter from PATH and is not a trustworthy gate here; see #78. Closes #59.
130 lines
2.8 KiB
Go
130 lines
2.8 KiB
Go
package pidlock_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/vaultik/internal/pidlock"
|
|
)
|
|
|
|
func TestAcquireAndRelease(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
// Acquire lock
|
|
lock, err := pidlock.Acquire(tmpDir)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, lock)
|
|
|
|
// Verify PID file exists with our PID
|
|
pidPath := filepath.Join(tmpDir, "vaultik.pid")
|
|
data, err := os.ReadFile(pidPath) //nolint:gosec // G304: test's own temp file
|
|
require.NoError(t, err)
|
|
pid, err := strconv.Atoi(string(data))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, os.Getpid(), pid)
|
|
|
|
// Release lock
|
|
err = lock.Release()
|
|
require.NoError(t, err)
|
|
|
|
// Verify PID file is gone
|
|
_, err = os.Stat(pidPath)
|
|
assert.True(t, os.IsNotExist(err))
|
|
}
|
|
|
|
func TestAcquireBlocksSecondInstance(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
// Acquire first lock
|
|
lock1, err := pidlock.Acquire(tmpDir)
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, lock1)
|
|
|
|
defer func() { _ = lock1.Release() }()
|
|
|
|
// Try to acquire second lock - should fail
|
|
lock2, err := pidlock.Acquire(tmpDir)
|
|
require.ErrorIs(t, err, pidlock.ErrAlreadyRunning)
|
|
assert.Nil(t, lock2)
|
|
}
|
|
|
|
func TestAcquireWithStaleLock(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
// Write a stale PID file (PID that doesn't exist)
|
|
stalePID := 999999999 // Unlikely to be a real process
|
|
pidPath := filepath.Join(tmpDir, "vaultik.pid")
|
|
err := os.WriteFile(pidPath, []byte(strconv.Itoa(stalePID)), 0600)
|
|
require.NoError(t, err)
|
|
|
|
// Should be able to acquire lock (stale lock is cleaned up)
|
|
lock, err := pidlock.Acquire(tmpDir)
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, lock)
|
|
|
|
defer func() { _ = lock.Release() }()
|
|
|
|
// Verify our PID is now in the file
|
|
data, err := os.ReadFile(pidPath) //nolint:gosec // G304: test's own temp file
|
|
require.NoError(t, err)
|
|
pid, err := strconv.Atoi(string(data))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, os.Getpid(), pid)
|
|
}
|
|
|
|
func TestReleaseIsIdempotent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
lock, err := pidlock.Acquire(tmpDir)
|
|
require.NoError(t, err)
|
|
|
|
// Release multiple times - should not error
|
|
err = lock.Release()
|
|
require.NoError(t, err)
|
|
|
|
err = lock.Release()
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestReleaseNilLock(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var lock *pidlock.Lock
|
|
|
|
err := lock.Release()
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestAcquireCreatesDirectory(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
nestedDir := filepath.Join(tmpDir, "nested", "dir")
|
|
|
|
lock, err := pidlock.Acquire(nestedDir)
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, lock)
|
|
|
|
defer func() { _ = lock.Release() }()
|
|
|
|
// Verify directory was created
|
|
info, err := os.Stat(nestedDir)
|
|
require.NoError(t, err)
|
|
assert.True(t, info.IsDir())
|
|
}
|