Your IP : 216.73.216.224


Current Path : /home/hotlineuser/mobius/hotline/
Upload File :
Current File : //home/hotlineuser/mobius/hotline/account_test.go

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)
}