diff options
| author | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2026-03-16 11:46:04 -0700 |
|---|---|---|
| committer | Jeff Halter <868228+jhalter@users.noreply.github.com> | 2026-03-16 11:46:04 -0700 |
| commit | 2f46f87177234070044b5304ca3e0db471699ad8 (patch) | |
| tree | 6dc72ce596e95850a587001a85b3f45e394b0e34 /hotline/account_test.go | |
| parent | 92f4b7af1073b6cdd0786ce2090d6e446e0903ad (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.go | 91 |
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) +} |