Files
osdb-1-claude-opusplan/backend/internal/auth/auth_test.go
juliaandClaude Sonnet 4.6 1b381d9385 feat: protect write endpoints with JWT auth (story #08)
Add bcrypt user file auth, JWT middleware on all write routes, Pinia
authStore with localStorage persistence, login view with redirect
support, and v-if guards on all CRUD controls and admin nav link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:18:42 +02:00

126 lines
3.0 KiB
Go

package auth_test
import (
"os"
"path/filepath"
"testing"
"golang.org/x/crypto/bcrypt"
"osdb/internal/auth"
)
// hashPepper mirrors the pepper convention: bcrypt("username:password")
func hashPepper(username, password string) string {
h, err := bcrypt.GenerateFromPassword([]byte(username+":"+password), bcrypt.MinCost)
if err != nil {
panic(err)
}
return string(h)
}
func writeTempUsersFile(t *testing.T, content string) string {
t.Helper()
f, err := os.CreateTemp(t.TempDir(), "users*.env")
if err != nil {
t.Fatal(err)
}
if _, err := f.WriteString(content); err != nil {
t.Fatal(err)
}
f.Close()
return f.Name()
}
func TestLoadUsers_ParsesValidFile(t *testing.T) {
hash := hashPepper("admin", "secret")
path := writeTempUsersFile(t, "admin:"+hash+"\n")
users, err := auth.LoadUsers(path)
if err != nil {
t.Fatalf("LoadUsers: %v", err)
}
if len(users) != 1 {
t.Fatalf("expected 1 user, got %d", len(users))
}
if _, ok := users["admin"]; !ok {
t.Error("expected 'admin' in users map")
}
}
func TestLoadUsers_SkipsCommentLines(t *testing.T) {
hash := hashPepper("admin", "secret")
content := "# comment\nadmin:" + hash + "\n"
path := writeTempUsersFile(t, content)
users, err := auth.LoadUsers(path)
if err != nil {
t.Fatalf("LoadUsers: %v", err)
}
if len(users) != 1 {
t.Fatalf("expected 1 user, got %d", len(users))
}
}
func TestLoadUsers_MissingFile(t *testing.T) {
_, err := auth.LoadUsers(filepath.Join(t.TempDir(), "nonexistent.env"))
if err == nil {
t.Error("expected error for missing file")
}
}
func TestVerifyPassword_CorrectPassword(t *testing.T) {
hash := hashPepper("admin", "secret")
users := map[string]string{"admin": hash}
if !auth.VerifyPassword(users, "admin", "secret") {
t.Error("expected password to match")
}
}
func TestVerifyPassword_WrongPassword(t *testing.T) {
hash := hashPepper("admin", "secret")
users := map[string]string{"admin": hash}
if auth.VerifyPassword(users, "admin", "wrong") {
t.Error("expected password to not match")
}
}
func TestVerifyPassword_UnknownUser(t *testing.T) {
if auth.VerifyPassword(map[string]string{}, "nobody", "secret") {
t.Error("expected false for unknown user")
}
}
func TestGenerateAndValidateJWT(t *testing.T) {
token, err := auth.GenerateJWT("admin", "testsecret")
if err != nil {
t.Fatalf("GenerateJWT: %v", err)
}
if token == "" {
t.Error("expected non-empty token")
}
claims, err := auth.ValidateJWT(token, "testsecret")
if err != nil {
t.Fatalf("ValidateJWT: %v", err)
}
if claims.Username != "admin" {
t.Errorf("expected username 'admin', got %q", claims.Username)
}
}
func TestValidateJWT_WrongSecret(t *testing.T) {
token, _ := auth.GenerateJWT("admin", "correct")
if _, err := auth.ValidateJWT(token, "wrong"); err == nil {
t.Error("expected error for wrong secret")
}
}
func TestValidateJWT_MalformedToken(t *testing.T) {
if _, err := auth.ValidateJWT("not.a.token", "secret"); err == nil {
t.Error("expected error for malformed token")
}
}