ae1831e61f
- Rename user_id → users_id across all models, handlers, services, and tests
- Add custom RoleIDs type supporting string/int/array unmarshaling (e.g., "1", 1, [1])
- Implement flexible JSON unmarshaling for JWT Claims to handle field name variants
- Support both user_id/users_id and email/email_address field names
- Enable role_id as string ("1"), int (1), or array ([1,2])
- Update AuthorizationContext to handle role_id type flexibility
- Add comprehensive logging to repository, service, and handler layers
- Entry/exit logs with full context
- Success (✓) and failure (✗) indicators
- Step-by-step authorization flow tracking
- Add containsRole helper for multi-role membership checks
- Fix database queries: user_id → users_id, id → permissions_id
- Update all tests to use models.RoleIDs{} syntax
- Change GetRole middleware return type: string → []int
- Maintain backward compatibility with legacy JWT tokens
This change improves integration with external services (MIS) that may send
role_id in different formats and standardizes field naming conventions
throughout the authorization microservice.
481 lines
13 KiB
Go
481 lines
13 KiB
Go
package repository
|
|
|
|
import (
|
|
"authorization/db"
|
|
"database/sql"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
)
|
|
|
|
func setupMockDB(t *testing.T) (sqlmock.Sqlmock, func()) {
|
|
mockDB, mock, err := sqlmock.New()
|
|
if err != nil {
|
|
t.Fatalf("Failed to create mock database: %v", err)
|
|
}
|
|
|
|
// Store original DB and replace with mock
|
|
originalDB := db.DB
|
|
db.DB = mockDB
|
|
|
|
cleanup := func() {
|
|
db.DB = originalDB
|
|
mockDB.Close()
|
|
}
|
|
|
|
return mock, cleanup
|
|
}
|
|
|
|
func TestGetPolicyAttributesByPermissionSuccess(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "attribute_name", "attribute_type", "comparison", "attribute_value", "permission_id"}).
|
|
AddRow(1, "department", "user", "=", "engineering", 1).
|
|
AddRow(2, "level", "user", ">=", "5", 1)
|
|
|
|
mock.ExpectQuery("SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id FROM policy_attributes WHERE permission_id = \\?").
|
|
WithArgs(1).
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetPolicyAttributesByPermission(1)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(attrs) != 2 {
|
|
t.Errorf("Expected 2 attributes, got %d", len(attrs))
|
|
}
|
|
if attrs[0].AttributeName != "department" {
|
|
t.Errorf("Expected attribute name 'department', got '%s'", attrs[0].AttributeName)
|
|
}
|
|
}
|
|
|
|
func TestGetPolicyAttributesByPermissionEmpty(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "attribute_name", "attribute_type", "comparison", "attribute_value", "permission_id"})
|
|
|
|
mock.ExpectQuery("SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id FROM policy_attributes WHERE permission_id = \\?").
|
|
WithArgs(999).
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetPolicyAttributesByPermission(999)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(attrs) != 0 {
|
|
t.Errorf("Expected 0 attributes, got %d", len(attrs))
|
|
}
|
|
}
|
|
|
|
func TestGetUserAttributesSuccess(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"attribute_name", "attribute_value"}).
|
|
AddRow("department", "engineering").
|
|
AddRow("level", "5")
|
|
|
|
mock.ExpectQuery("SELECT attribute_name, attribute_value FROM user_attributes WHERE users_id = \\?").
|
|
WithArgs("user123").
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetUserAttributes("user123")
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(attrs) != 2 {
|
|
t.Errorf("Expected 2 attributes, got %d", len(attrs))
|
|
}
|
|
if attrs["department"] != "engineering" {
|
|
t.Errorf("Expected department 'engineering', got '%s'", attrs["department"])
|
|
}
|
|
if attrs["level"] != "5" {
|
|
t.Errorf("Expected level '5', got '%s'", attrs["level"])
|
|
}
|
|
}
|
|
|
|
func TestGetUserByIDSuccess(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
testTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
rows := sqlmock.NewRows([]string{
|
|
"users_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
|
|
"home_address", "contact_number",
|
|
"role_id", "is_deleted", "created_at", "updated_at",
|
|
}).AddRow(
|
|
"user123", "John", "M", "Doe", "Jr", "john@example.com",
|
|
"EMP001", "Y", "Y", "123 Main St", "1234567890", "device001",
|
|
1, "N", "secret", "Y", testTime, testTime,
|
|
)
|
|
|
|
mock.ExpectQuery("SELECT users_id, first_name").
|
|
WithArgs("user123").
|
|
WillReturnRows(rows)
|
|
|
|
user, err := GetUserByID("user123")
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if user == nil {
|
|
t.Fatal("Expected user, got nil")
|
|
}
|
|
if user.UsersID != "user123" {
|
|
t.Errorf("Expected UsersID 'user123', got '%s'", user.UsersID)
|
|
}
|
|
if user.FirstName != "John" {
|
|
t.Errorf("Expected FirstName 'John', got '%s'", user.FirstName)
|
|
}
|
|
}
|
|
|
|
func TestGetUserByIDNotFound(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
mock.ExpectQuery("SELECT users_id, first_name").
|
|
WithArgs("nonexistent").
|
|
WillReturnError(sql.ErrNoRows)
|
|
|
|
user, err := GetUserByID("nonexistent")
|
|
|
|
if err == nil {
|
|
t.Error("Expected error for non-existent user")
|
|
}
|
|
if user != nil {
|
|
t.Error("Expected nil user")
|
|
}
|
|
}
|
|
|
|
func TestGetAllPermissionsSuccess(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "permission_name", "description", "resource", "action"}).
|
|
AddRow(1, "read_document", "Read document", "document", "read").
|
|
AddRow(2, "write_document", "Write document", "document", "write")
|
|
|
|
mock.ExpectQuery("SELECT id, permission_name, description, resource, action FROM permissions ORDER BY id").
|
|
WillReturnRows(rows)
|
|
|
|
perms, err := GetAllPermissions()
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(perms) != 2 {
|
|
t.Errorf("Expected 2 permissions, got %d", len(perms))
|
|
}
|
|
}
|
|
|
|
func TestGetAllPolicyAttributesSuccess(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "attribute_name", "attribute_type", "comparison", "attribute_value", "permission_id"}).
|
|
AddRow(1, "department", "user", "=", "engineering", 1).
|
|
AddRow(2, "level", "user", ">=", "5", 1).
|
|
AddRow(3, "role", "user", "=", "admin", 2)
|
|
|
|
mock.ExpectQuery("SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id FROM policy_attributes ORDER BY permission_id, id").
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetAllPolicyAttributes()
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(attrs) != 2 {
|
|
t.Errorf("Expected 2 permission groups, got %d", len(attrs))
|
|
}
|
|
if len(attrs[1]) != 2 {
|
|
t.Errorf("Expected 2 attributes for permission 1, got %d", len(attrs[1]))
|
|
}
|
|
if len(attrs[2]) != 1 {
|
|
t.Errorf("Expected 1 attribute for permission 2, got %d", len(attrs[2]))
|
|
}
|
|
}
|
|
|
|
func TestGetAllPolicyAttributesEmpty(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "attribute_name", "attribute_type", "comparison", "attribute_value", "permission_id"})
|
|
|
|
mock.ExpectQuery("SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id FROM policy_attributes ORDER BY permission_id, id").
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetAllPolicyAttributes()
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(attrs) != 0 {
|
|
t.Errorf("Expected 0 permission groups, got %d", len(attrs))
|
|
}
|
|
}
|
|
|
|
func TestGetPolicyAttributesByPermissionInvalidID(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "attribute_name", "attribute_type", "comparison", "attribute_value", "permission_id"})
|
|
|
|
// Match the actual query with proper whitespace handling
|
|
mock.ExpectQuery(`SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id\s+FROM policy_attributes\s+WHERE permission_id = \?`).
|
|
WithArgs(-1).
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetPolicyAttributesByPermission(-1)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(attrs) != 0 {
|
|
t.Errorf("Expected 0 attributes for invalid ID, got %d", len(attrs))
|
|
}
|
|
}
|
|
|
|
func TestGetPolicyAttributesByPermissionDatabaseError(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
mock.ExpectQuery(`SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id\s+FROM policy_attributes\s+WHERE permission_id = \?`).
|
|
WithArgs(1).
|
|
WillReturnError(errors.New("database error"))
|
|
|
|
attrs, err := GetPolicyAttributesByPermission(1)
|
|
|
|
if err == nil {
|
|
t.Error("Expected error, got nil")
|
|
}
|
|
if attrs != nil {
|
|
t.Error("Expected nil attributes on error")
|
|
}
|
|
}
|
|
|
|
func TestGetUserAttributesEmptyUserID(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"attribute_name", "attribute_value"})
|
|
|
|
// Match the actual query format
|
|
mock.ExpectQuery(`SELECT attribute_name, attribute_value\s+FROM user_attributes\s+WHERE users_id = \?`).
|
|
WithArgs("").
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetUserAttributes("")
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(attrs) != 0 {
|
|
t.Errorf("Expected 0 attributes for empty user ID, got %d", len(attrs))
|
|
}
|
|
}
|
|
|
|
func TestGetUserAttributesMultipleAttributes(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"attribute_name", "attribute_value"}).
|
|
AddRow("department", "IT").
|
|
AddRow("level", "5").
|
|
AddRow("location", "US").
|
|
AddRow("clearance", "high")
|
|
|
|
// Match the actual query
|
|
mock.ExpectQuery(`SELECT attribute_name, attribute_value\s+FROM user_attributes\s+WHERE users_id = \?`).
|
|
WithArgs("user123").
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetUserAttributes("user123")
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
|
|
if len(attrs) != 4 {
|
|
t.Errorf("Expected 4 attributes, got %d", len(attrs))
|
|
}
|
|
}
|
|
|
|
func TestGetUserByIDEmptyID(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{
|
|
"users_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
|
|
"home_address", "contact_number",
|
|
"role_id", "is_deleted", "created_at", "updated_at",
|
|
})
|
|
|
|
// Match the actual query format with all the fields
|
|
mock.ExpectQuery(`SELECT users_id, first_name, middle_initial, last_name, suffix, email_address`).
|
|
WithArgs("").
|
|
WillReturnRows(rows)
|
|
|
|
user, err := GetUserByID("")
|
|
|
|
// Should get an error (empty ID returns error from function logic)
|
|
if err == nil {
|
|
t.Error("Expected error for empty ID, got nil")
|
|
}
|
|
if user != nil {
|
|
t.Error("Expected nil user for empty ID")
|
|
}
|
|
}
|
|
|
|
func TestGetUserByIDDatabaseError(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
mock.ExpectQuery("SELECT id, username, role, email, created_at, updated_at FROM users WHERE id = \\?").
|
|
WithArgs("user123").
|
|
WillReturnError(errors.New("database connection failed"))
|
|
|
|
user, err := GetUserByID("user123")
|
|
|
|
if err == nil {
|
|
t.Error("Expected error, got nil")
|
|
}
|
|
if user != nil {
|
|
t.Error("Expected nil user on error")
|
|
}
|
|
}
|
|
|
|
func TestGetAllPermissionsDatabaseError(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
mock.ExpectQuery("SELECT id, permission_name, description, resource, action FROM permissions ORDER BY id").
|
|
WillReturnError(errors.New("database error"))
|
|
|
|
perms, err := GetAllPermissions()
|
|
|
|
if err == nil {
|
|
t.Error("Expected error, got nil")
|
|
}
|
|
if perms != nil {
|
|
t.Error("Expected nil permissions on error")
|
|
}
|
|
}
|
|
|
|
func TestGetAllPermissionsEmpty(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "permission_name", "description", "resource", "action"})
|
|
|
|
mock.ExpectQuery("SELECT id, permission_name, description, resource, action FROM permissions ORDER BY id").
|
|
WillReturnRows(rows)
|
|
|
|
perms, err := GetAllPermissions()
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(perms) != 0 {
|
|
t.Errorf("Expected 0 permissions, got %d", len(perms))
|
|
}
|
|
}
|
|
|
|
func TestGetAllPermissionsLargeDataset(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "permission_name", "description", "resource", "action"})
|
|
for i := 1; i <= 1000; i++ {
|
|
rows.AddRow(i, "perm"+string(rune(i)), "description", "resource", "action")
|
|
}
|
|
|
|
mock.ExpectQuery("SELECT id, permission_name, description, resource, action FROM permissions ORDER BY id").
|
|
WillReturnRows(rows)
|
|
|
|
perms, err := GetAllPermissions()
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(perms) != 1000 {
|
|
t.Errorf("Expected 1000 permissions, got %d", len(perms))
|
|
}
|
|
}
|
|
|
|
func TestGetAllPolicyAttributesDatabaseError(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
mock.ExpectQuery("SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id FROM policy_attributes ORDER BY permission_id, id").
|
|
WillReturnError(errors.New("connection lost"))
|
|
|
|
attrs, err := GetAllPolicyAttributes()
|
|
|
|
if err == nil {
|
|
t.Error("Expected error, got nil")
|
|
}
|
|
if attrs != nil {
|
|
t.Error("Expected nil attributes on error")
|
|
}
|
|
}
|
|
|
|
func TestGetAllPolicyAttributesManyPermissions(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
rows := sqlmock.NewRows([]string{"id", "attribute_name", "attribute_type", "comparison", "attribute_value", "permission_id"})
|
|
|
|
// Add attributes for multiple permissions
|
|
for permID := 1; permID <= 50; permID++ {
|
|
for attrID := 1; attrID <= 3; attrID++ {
|
|
rows.AddRow(attrID, "attr", "string", "equals", "value", permID)
|
|
}
|
|
}
|
|
|
|
mock.ExpectQuery("SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id FROM policy_attributes ORDER BY permission_id, id").
|
|
WillReturnRows(rows)
|
|
|
|
attrs, err := GetAllPolicyAttributes()
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
if len(attrs) != 50 {
|
|
t.Errorf("Expected 50 permission groups, got %d", len(attrs))
|
|
}
|
|
|
|
// Check that each permission has 3 attributes
|
|
for permID := 1; permID <= 50; permID++ {
|
|
if len(attrs[permID]) != 3 {
|
|
t.Errorf("Expected 3 attributes for permission %d, got %d", permID, len(attrs[permID]))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetUserAttributesDatabaseError(t *testing.T) {
|
|
mock, cleanup := setupMockDB(t)
|
|
defer cleanup()
|
|
|
|
mock.ExpectQuery("SELECT attribute_name, attribute_value, attribute_type FROM user_attributes WHERE users_id = \\?").
|
|
WithArgs("user123").
|
|
WillReturnError(errors.New("timeout"))
|
|
|
|
attrs, err := GetUserAttributes("user123")
|
|
|
|
if err == nil {
|
|
t.Error("Expected error, got nil")
|
|
}
|
|
if attrs != nil {
|
|
t.Error("Expected nil attributes on error")
|
|
}
|
|
}
|