Move the package into src/

Thirty-seven entries in the root, most of them .go files. The assets had to
come along: //go:embed cannot reach outside its own directory, so
templates/, static/ and migrations/ live beside the code that embeds them,
and testdata/ beside the test that reads it. storage/ stays put — runtime
data, not source.

go build now needs -o. Without it the output would be named after the
package directory and collide with src/ itself.
This commit is contained in:
Esa Kataja
2026-09-05 13:53:04 +03:00
parent 5db19b26ae
commit b01d08b1e1
48 changed files with 9 additions and 3 deletions
+43
View File
@@ -0,0 +1,43 @@
package main
import (
"testing"
"time"
)
func TestLoginRateLimit(t *testing.T) {
var l limiter
for i := range loginMaxFailures - 1 {
l.fail("[email protected]")
if l.locked("[email protected]") {
t.Fatalf("locked after %d failures, limit is %d", i+1, loginMaxFailures)
}
}
l.fail("[email protected]")
if !l.locked("[email protected]") {
t.Fatalf("not locked after %d failures", loginMaxFailures)
}
// The limit is per email: locking one address must not lock anyone else out.
if l.locked("[email protected]") {
t.Fatal("a different address was locked too")
}
// A correct password clears it, so a member who mistypes nine times and then gets it right
// starts from zero.
l.succeed("[email protected]")
if l.locked("[email protected]") {
t.Fatal("still locked after a successful login")
}
// Failures older than the window don't accumulate.
for range loginMaxFailures - 1 {
l.fail("[email protected]")
}
l.by["[email protected]"].first = time.Now().Add(-loginWindow - time.Minute)
l.fail("[email protected]")
if l.locked("[email protected]") {
t.Fatal("failures outside the window were counted")
}
}