Replaces #559, which conflicted. Model: opus-5-5
This commit is contained in:
@@ -10,12 +10,15 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/fx/fxtest"
|
||||
"sneak.berlin/go/webhooker/internal/archive"
|
||||
"sneak.berlin/go/webhooker/internal/config"
|
||||
"sneak.berlin/go/webhooker/internal/database"
|
||||
"sneak.berlin/go/webhooker/internal/datadir"
|
||||
@@ -24,6 +27,7 @@ import (
|
||||
"sneak.berlin/go/webhooker/internal/handlers"
|
||||
"sneak.berlin/go/webhooker/internal/healthcheck"
|
||||
"sneak.berlin/go/webhooker/internal/logger"
|
||||
"sneak.berlin/go/webhooker/internal/metrics"
|
||||
"sneak.berlin/go/webhooker/internal/middleware"
|
||||
"sneak.berlin/go/webhooker/internal/resetpw"
|
||||
"sneak.berlin/go/webhooker/internal/session"
|
||||
@@ -127,20 +131,36 @@ func run(
|
||||
return code, stdout.String(), stderr.String()
|
||||
}
|
||||
|
||||
type noopNotifier struct{}
|
||||
type noopArchives struct {
|
||||
renameMu sync.Mutex
|
||||
}
|
||||
|
||||
func (n *noopNotifier) Notify([]delivery.Task) {}
|
||||
func (n *noopArchives) RenameLock() *sync.Mutex {
|
||||
return &n.renameMu
|
||||
}
|
||||
|
||||
type noopEvictor struct{}
|
||||
func (n *noopArchives) EvictWebhook(string) {}
|
||||
|
||||
func (n *noopEvictor) EvictWebhook(string) {}
|
||||
func (n *noopArchives) EvictTarget(string) {}
|
||||
|
||||
func (n *noopArchives) Rename(_, _, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type noopCircuitBreakers struct{}
|
||||
|
||||
func (n *noopCircuitBreakers) StateAndCooldown(
|
||||
string,
|
||||
) (delivery.CircuitState, time.Duration) {
|
||||
return delivery.CircuitClosed, 0
|
||||
}
|
||||
|
||||
// newServerApp starts the real login path against dir: the handlers,
|
||||
// the middleware that bounds password verification, the session store
|
||||
// and the database, exactly as internal/handlers builds them.
|
||||
//
|
||||
// One application per test function, not per case: every start that
|
||||
// finds no account seeds one at 64 MB of Argon2id, and this package's
|
||||
// finds no account seeds one with an Argon2id hash, and this package's
|
||||
// budget is not the place to spend that repeatedly.
|
||||
func newServerApp(
|
||||
t *testing.T, dir string,
|
||||
@@ -151,9 +171,14 @@ func newServerApp(
|
||||
|
||||
app := fxtest.New(
|
||||
t,
|
||||
// fx's own log is discarded, not sent to t.Logf: a hook still
|
||||
// running after a start or stop timeout would write there after
|
||||
// the test has returned.
|
||||
fx.NopLogger,
|
||||
fx.Provide(
|
||||
globals.New,
|
||||
logger.New,
|
||||
func(l *logger.Logger) *slog.Logger { return l.Get() },
|
||||
func() *config.Config {
|
||||
return &config.Config{DataDir: dir}
|
||||
},
|
||||
@@ -161,8 +186,18 @@ func newServerApp(
|
||||
database.NewWebhookDBManager,
|
||||
healthcheck.New,
|
||||
session.New,
|
||||
func() delivery.Notifier { return &noopNotifier{} },
|
||||
func() delivery.WebhookEvictor { return &noopEvictor{} },
|
||||
archive.NewWriters,
|
||||
// The engine is built on a lifecycle that is never started,
|
||||
// so no worker runs.
|
||||
func(p delivery.EngineParams) *delivery.Engine {
|
||||
return delivery.New(fxtest.NewLifecycle(t), p)
|
||||
},
|
||||
func() archive.Archives { return &noopArchives{} },
|
||||
func() delivery.CircuitBreakers {
|
||||
return &noopCircuitBreakers{}
|
||||
},
|
||||
metrics.NewRegistry,
|
||||
metrics.New,
|
||||
middleware.New,
|
||||
delivery.NewGuard,
|
||||
handlers.New,
|
||||
@@ -364,6 +399,33 @@ func TestMissingDatabaseCreatesNothing(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
// TestZeroLengthDatabaseCreatesNothing covers a webhooker.db left at
|
||||
// zero length, as a truncated copy leaves it. SQLite would open it as
|
||||
// an empty database, so it is refused like a missing one and left as
|
||||
// it is.
|
||||
func TestZeroLengthDatabaseCreatesNothing(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
t.Setenv("DATA_DIR", dir)
|
||||
|
||||
dbPath := filepath.Join(dir, database.MainDBFileName)
|
||||
require.NoError(
|
||||
t, os.WriteFile(dbPath, nil, database.SQLiteFilePerm),
|
||||
)
|
||||
|
||||
code, _, stderr := run(t, newPassword+"\n", operatorUser)
|
||||
|
||||
require.Equal(t, exitFailure, code)
|
||||
assert.Contains(t, stderr, dbPath)
|
||||
|
||||
entries, err := os.ReadDir(dir)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, entries, 1, "nothing may be created beside it")
|
||||
|
||||
info, err := os.Stat(dbPath)
|
||||
require.NoError(t, err)
|
||||
assert.Zero(t, info.Size(), "nothing may be written into it")
|
||||
}
|
||||
|
||||
// TestUnknownUserFails states the decision: resetpw changes an
|
||||
// existing account's password and never creates an account. A typo in
|
||||
// the username must say so rather than quietly adding a second user.
|
||||
|
||||
Reference in New Issue
Block a user