Files
OwnCord/Server/auth/password_test.go
T
jevb 56f012cd3f test: boost server coverage — auth 60→95%, db 69→81%, config 75→85%
Add comprehensive tests across all Go packages:
- auth: username validation, concurrent rate limiting, TOTP stores, timing
- config: env overrides, default credential detection, voice defaults
- db: search, message queries, special char handling
- api: handler edge cases, error paths, DM/invite/TOTP coverage
- ws: voice handler paths, integration scenarios
- updater: version comparison, timeout handling

6 of 8 packages now at 80%+ coverage.
2026-04-01 11:38:11 +02:00

168 lines
5.0 KiB
Go

package auth_test
import (
"strings"
"testing"
"github.com/owncord/server/auth"
)
func TestHashPassword_DiffersFromPlaintext(t *testing.T) {
hash, err := auth.HashPassword("mypassword")
if err != nil {
t.Fatalf("HashPassword() error = %v", err)
}
if hash == "mypassword" {
t.Error("HashPassword() hash equals plaintext")
}
}
func TestHashPassword_BcryptPrefix(t *testing.T) {
hash, err := auth.HashPassword("mypassword")
if err != nil {
t.Fatalf("HashPassword() error = %v", err)
}
if !strings.HasPrefix(hash, "$2") {
t.Errorf("HashPassword() = %q, want bcrypt prefix $2*", hash)
}
}
func TestCheckPassword_CorrectPassword(t *testing.T) {
hash, err := auth.HashPassword("correctpassword")
if err != nil {
t.Fatalf("HashPassword() error = %v", err)
}
if !auth.CheckPassword(hash, "correctpassword") {
t.Error("CheckPassword() returned false for correct password")
}
}
func TestCheckPassword_WrongPassword(t *testing.T) {
hash, err := auth.HashPassword("correctpassword")
if err != nil {
t.Fatalf("HashPassword() error = %v", err)
}
if auth.CheckPassword(hash, "wrongpassword") {
t.Error("CheckPassword() returned true for wrong password")
}
}
func TestCheckPassword_EmptyPassword(t *testing.T) {
hash, err := auth.HashPassword("somepassword")
if err != nil {
t.Fatalf("HashPassword() error = %v", err)
}
if auth.CheckPassword(hash, "") {
t.Error("CheckPassword() returned true for empty password")
}
}
func TestCheckPassword_EmptyHash(t *testing.T) {
if auth.CheckPassword("", "somepassword") {
t.Error("CheckPassword() returned true with empty hash")
}
}
func TestValidatePasswordStrength_Valid(t *testing.T) {
cases := []string{
"12345678", // exactly 8 chars
"abcdefghij", // 10 chars
strings.Repeat("a", 72), // exactly 72 chars (bcrypt max)
}
for _, pw := range cases {
if err := auth.ValidatePasswordStrength(pw); err != nil {
t.Errorf("ValidatePasswordStrength(%q) error = %v, want nil", pw, err)
}
}
}
func TestValidatePasswordStrength_TooShort(t *testing.T) {
cases := []string{
"", // empty
"1234567", // 7 chars
"abc", // 3 chars
}
for _, pw := range cases {
if err := auth.ValidatePasswordStrength(pw); err == nil {
t.Errorf("ValidatePasswordStrength(%q) error = nil, want error", pw)
}
}
}
func TestValidatePasswordStrength_TooLong(t *testing.T) {
pw := strings.Repeat("a", 73) // 73 chars — over bcrypt 72 byte limit
if err := auth.ValidatePasswordStrength(pw); err == nil {
t.Errorf("ValidatePasswordStrength(%q) error = nil, want error for >72 chars", pw)
}
}
func TestHashPassword_TwoCallsDifferentHashes(t *testing.T) {
// bcrypt includes a random salt
h1, _ := auth.HashPassword("password")
h2, _ := auth.HashPassword("password")
if h1 == h2 {
t.Error("HashPassword() produced identical hashes for the same password (salt missing?)")
}
}
func TestCheckPassword_EmptyHashTimingResistance(t *testing.T) {
// Calling CheckPassword with an empty hash should not be significantly
// faster than with a real hash (dummy comparison is performed).
// We just verify it returns false and doesn't panic.
result := auth.CheckPassword("", "anypassword")
if result {
t.Error("CheckPassword(\"\", ...) = true, want false")
}
}
func TestCheckPassword_MalformedHash(t *testing.T) {
// A malformed hash string (not bcrypt) should return false without panic.
result := auth.CheckPassword("not-a-bcrypt-hash", "password")
if result {
t.Error("CheckPassword(malformed, ...) = true, want false")
}
}
func TestHashPassword_UnicodePassword(t *testing.T) {
// Unicode passwords should hash and verify correctly.
pw := "Pässwörd™日本語"
hash, err := auth.HashPassword(pw)
if err != nil {
t.Fatalf("HashPassword(unicode) error: %v", err)
}
if !auth.CheckPassword(hash, pw) {
t.Error("CheckPassword() = false for correct unicode password")
}
if auth.CheckPassword(hash, "Pässwörd™日本") {
t.Error("CheckPassword() = true for slightly different unicode password")
}
}
func TestValidatePasswordStrength_UnicodeMultibyte(t *testing.T) {
// A password of 8 multi-byte runes may exceed 8 bytes but len() counts bytes.
// "日本語日本語日本" is 8 runes but 24 bytes — should pass the min check.
pw := "日本語日本語日本"
if err := auth.ValidatePasswordStrength(pw); err != nil {
t.Errorf("ValidatePasswordStrength(8-rune unicode) = %v, want nil", err)
}
}
func TestValidatePasswordStrength_ExactBoundaries(t *testing.T) {
// Exactly 8 bytes — valid.
if err := auth.ValidatePasswordStrength("12345678"); err != nil {
t.Errorf("exactly 8 chars: %v", err)
}
// Exactly 72 bytes — valid.
if err := auth.ValidatePasswordStrength(strings.Repeat("x", 72)); err != nil {
t.Errorf("exactly 72 chars: %v", err)
}
// 7 bytes — too short.
if err := auth.ValidatePasswordStrength("1234567"); err == nil {
t.Error("7 chars should be too short")
}
// 73 bytes — too long.
if err := auth.ValidatePasswordStrength(strings.Repeat("x", 73)); err == nil {
t.Error("73 chars should be too long")
}
}