Files
authentik/internal/outpost/proxyv2/filesystemstore/filesystemstore_test.go
Tom Neuber 7ed3fed5c3 outpost/proxyv2: add session cleanup for filesystem session store (#15798)
* proxyv2/filesystemstore: add persistent filesystem store to implement a session cleanup job

* proxyv2: add session cleanup for filesystem session store
2025-08-09 20:13:39 +01:00

147 lines
3.6 KiB
Go

package filesystemstore
import (
"context"
"os"
"path"
"path/filepath"
"syscall"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func createTempSessionFile(t *testing.T, dir string, modTime time.Time) string {
t.Helper()
path := filepath.Join(dir, "session_test")
err := os.WriteFile(path, []byte("session data"), 0600)
require.NoError(t, err)
err = os.Chtimes(path, modTime, modTime)
require.NoError(t, err)
return path
}
func TestNewStore_PathNotExist(t *testing.T) {
_, err := NewStore("/invalid_path")
assert.ErrorIs(t, err, ErrSessionStorePathNotExist)
}
func TestNewStore_PathNotWritable(t *testing.T) {
storePath := path.Join(os.TempDir(), "test")
err := os.Mkdir(storePath, 0400)
require.NoError(t, err)
_, err = NewStore(storePath)
assert.ErrorIs(t, err, ErrSessionStoreNoPermission)
_ = os.RemoveAll(storePath)
}
func TestNewStore(t *testing.T) {
tmpDir := t.TempDir()
store, err := NewStore(tmpDir)
assert.NoError(t, err)
assert.NotEmpty(t, store)
}
func TestSessionCleanup_RemovesExpired(t *testing.T) {
tmpDir := t.TempDir()
store, err := NewStore(tmpDir)
require.NoError(t, err)
store.Options.MaxAge = 1 // 1 second
// Create an expired session file
oldTime := time.Now().Add(-10 * time.Second)
createTempSessionFile(t, tmpDir, oldTime)
ctx := context.Background()
err = store.SessionCleanup(ctx)
assert.NoError(t, err)
// File should be deleted
files, _ := os.ReadDir(tmpDir)
assert.Empty(t, files)
}
func TestSessionCleanup_PreservesValid(t *testing.T) {
tmpDir := t.TempDir()
store, err := NewStore(tmpDir)
require.NoError(t, err)
store.Options.MaxAge = 3600 // 1 hour
// Create a valid (non-expired) session file
modTime := time.Now().Add(-10 * time.Second)
createTempSessionFile(t, tmpDir, modTime)
ctx := context.Background()
err = store.SessionCleanup(ctx)
assert.NoError(t, err)
// File should still exist
files, _ := os.ReadDir(tmpDir)
assert.Len(t, files, 1)
}
func TestSessionCleanup_ContextCancel(t *testing.T) {
tmpDir := t.TempDir()
store, err := NewStore(tmpDir)
require.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background())
cancel() // cancel immediately
err = store.SessionCleanup(ctx)
assert.ErrorIs(t, err, context.Canceled)
}
func TestSessionCleanup_AlreadyRunning(t *testing.T) {
tmpDir := t.TempDir()
store, err := NewStore(tmpDir)
require.NoError(t, err)
// Manually acquire the lock before calling SessionCleanup
lockPath := path.Join(tmpDir, SessionCleanupLockFileName)
lockFile, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0600)
require.NoError(t, err, "failed to create lock file")
err = syscall.Flock(int(lockFile.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
require.NoError(t, err, "failed to acquire lock for test")
// Run SessionCleanup while lock is held
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err = store.SessionCleanup(ctx)
assert.ErrorIs(t, err, ErrSessionCleanupAlreadyRunning)
// Unlock and clean up
_ = syscall.Flock(int(lockFile.Fd()), syscall.LOCK_UN)
_ = lockFile.Close()
_ = os.Remove(lockPath)
}
func TestPersistentStore_ReusesStore(t *testing.T) {
tmpDir := t.TempDir()
store1, err := GetPersistentStore(tmpDir)
require.NoError(t, err)
assert.NotNil(t, store1)
store2, err := GetPersistentStore(tmpDir)
require.NoError(t, err)
assert.Equal(t, store1, store2)
StopPersistentStore()
}
func TestStopPersistentStore(t *testing.T) {
tmpDir := t.TempDir()
_, err := GetPersistentStore(tmpDir)
require.NoError(t, err)
StopPersistentStore()
// call again should not panic
StopPersistentStore()
}