aboutsummaryrefslogtreecommitdiff
path: root/hotline/account_test.go
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2026-03-16 11:46:04 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2026-03-16 11:46:04 -0700
commit2f46f87177234070044b5304ca3e0db471699ad8 (patch)
tree6dc72ce596e95850a587001a85b3f45e394b0e34 /hotline/account_test.go
parent92f4b7af1073b6cdd0786ce2090d6e446e0903ad (diff)
Improve test coverage for hotline and internal/mobius packages
Add comprehensive test cases across both packages to increase coverage: - hotline: 52.4% → ~55% (Disconnect, handleTransaction, SendAll, sendBanMessage, MemClientMgr, and other tests) - internal/mobius: 75.9% → ~80% (HandleUpdateUser, HandleDeleteUser, HandleSetUser, HandleNewUser, HandleUserBroadcast success paths)
Diffstat (limited to 'hotline/account_test.go')
-rw-r--r--hotline/account_test.go91
1 files changed, 91 insertions, 0 deletions
diff --git a/hotline/account_test.go b/hotline/account_test.go
new file mode 100644
index 0000000..43fe28f
--- /dev/null
+++ b/hotline/account_test.go
@@ -0,0 +1,91 @@
+package hotline
+
+import (
+ "encoding/binary"
+ "io"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "golang.org/x/crypto/bcrypt"
+)
+
+func TestNewAccount(t *testing.T) {
+ access := AccessBitmap{0xff, 0, 0, 0, 0, 0, 0, 0}
+ acct := NewAccount("jdoe", "John Doe", "secret123", access)
+
+ assert.Equal(t, "jdoe", acct.Login)
+ assert.Equal(t, "John Doe", acct.Name)
+ assert.Equal(t, access, acct.Access)
+
+ // Password should be a bcrypt hash, not the plaintext.
+ assert.NotEqual(t, "secret123", acct.Password)
+ err := bcrypt.CompareHashAndPassword([]byte(acct.Password), []byte("secret123"))
+ require.NoError(t, err, "password hash should validate against original plaintext")
+}
+
+func TestHashAndSalt(t *testing.T) {
+ t.Run("produces valid bcrypt hash", func(t *testing.T) {
+ hash := HashAndSalt([]byte("password"))
+ err := bcrypt.CompareHashAndPassword([]byte(hash), []byte("password"))
+ require.NoError(t, err)
+ })
+
+ t.Run("different calls produce different hashes", func(t *testing.T) {
+ h1 := HashAndSalt([]byte("same"))
+ h2 := HashAndSalt([]byte("same"))
+ assert.NotEqual(t, h1, h2, "bcrypt salts should differ between calls")
+
+ // Both should still verify against the original input.
+ require.NoError(t, bcrypt.CompareHashAndPassword([]byte(h1), []byte("same")))
+ require.NoError(t, bcrypt.CompareHashAndPassword([]byte(h2), []byte("same")))
+ })
+}
+
+func TestAccount_Read(t *testing.T) {
+ t.Run("with password set", func(t *testing.T) {
+ acct := NewAccount("admin", "Admin User", "pass", AccessBitmap{})
+
+ data, err := io.ReadAll(acct)
+ require.NoError(t, err)
+
+ // First two bytes are the field count (big-endian uint16).
+ require.GreaterOrEqual(t, len(data), 2)
+ fieldCount := binary.BigEndian.Uint16(data[:2])
+ assert.Equal(t, uint16(4), fieldCount, "should have 4 fields when password is set")
+
+ // Verify the password marker "x" appears in the serialized data.
+ assert.Contains(t, string(data), "x")
+
+ // Verify the user name appears in the serialized data.
+ assert.Contains(t, string(data), "Admin User")
+ })
+
+ t.Run("with empty password", func(t *testing.T) {
+ acct := &Account{
+ Login: "guest",
+ Name: "Guest",
+ Password: HashAndSalt([]byte("")),
+ Access: AccessBitmap{},
+ }
+
+ data, err := io.ReadAll(acct)
+ require.NoError(t, err)
+
+ require.GreaterOrEqual(t, len(data), 2)
+ fieldCount := binary.BigEndian.Uint16(data[:2])
+ assert.Equal(t, uint16(3), fieldCount, "should have 3 fields when password is empty")
+ })
+
+ t.Run("full read returns all serialized bytes", func(t *testing.T) {
+ acct := NewAccount("test", "Test", "pw", AccessBitmap{0x01})
+
+ data, err := io.ReadAll(acct)
+ require.NoError(t, err)
+ assert.Greater(t, len(data), 2, "serialized output should contain field data beyond the count header")
+ })
+}
+
+func TestNewAccount_GuestConstant(t *testing.T) {
+ assert.Equal(t, "guest", GuestAccount)
+}