Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
239 lines
8.4 KiB
Plaintext
239 lines
8.4 KiB
Plaintext
package auth
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// === PermissionLevel tests ===
|
|
|
|
func TestPermissionLevel_IsValid(t *testing.T) {
|
|
assert.True(t, PermissionFull.IsValid())
|
|
assert.True(t, PermissionRead.IsValid())
|
|
assert.True(t, PermissionNone.IsValid())
|
|
assert.False(t, PermissionLevel("invalid").IsValid())
|
|
assert.False(t, PermissionLevel("").IsValid())
|
|
}
|
|
|
|
func TestPermissionLevel_CanWrite(t *testing.T) {
|
|
assert.True(t, PermissionFull.CanWrite())
|
|
assert.False(t, PermissionRead.CanWrite())
|
|
assert.False(t, PermissionNone.CanWrite())
|
|
}
|
|
|
|
func TestPermissionLevel_CanRead(t *testing.T) {
|
|
assert.True(t, PermissionFull.CanRead())
|
|
assert.True(t, PermissionRead.CanRead())
|
|
assert.False(t, PermissionNone.CanRead())
|
|
}
|
|
|
|
// === PermissionMatrix tests ===
|
|
|
|
func TestAgentDefaultPermissions(t *testing.T) {
|
|
assert.Equal(t, PermissionRead, AgentDefaultPermissions[DimensionConversationManage])
|
|
assert.Equal(t, PermissionNone, AgentDefaultPermissions[DimensionConversationDelete])
|
|
assert.Equal(t, PermissionRead, AgentDefaultPermissions[DimensionContactManage])
|
|
assert.Equal(t, PermissionNone, AgentDefaultPermissions[DimensionReportManage])
|
|
assert.Equal(t, PermissionNone, AgentDefaultPermissions[DimensionKnowledgeBaseManage])
|
|
assert.Equal(t, PermissionNone, AgentDefaultPermissions[DimensionAutomationManage])
|
|
}
|
|
|
|
func TestAdministratorPermissions(t *testing.T) {
|
|
for _, dim := range AllDimensions {
|
|
assert.Equal(t, PermissionFull, AdministratorPermissions[dim], "admin should have full on %s", dim)
|
|
}
|
|
}
|
|
|
|
func TestAllDimensions_ContainsAllSix(t *testing.T) {
|
|
assert.Len(t, AllDimensions, 6)
|
|
expected := []PermissionDimension{
|
|
DimensionConversationManage,
|
|
DimensionConversationDelete,
|
|
DimensionContactManage,
|
|
DimensionReportManage,
|
|
DimensionKnowledgeBaseManage,
|
|
DimensionAutomationManage,
|
|
}
|
|
assert.ElementsMatch(t, expected, AllDimensions)
|
|
}
|
|
|
|
func TestPermissionMatrixMap_ToJSON(t *testing.T) {
|
|
data, err := AgentDefaultPermissions.ToJSON()
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, string(data), "conversation_manage")
|
|
assert.Contains(t, string(data), "read")
|
|
}
|
|
|
|
func TestPermissionMatrixFromJSON_Valid(t *testing.T) {
|
|
data, err := AgentDefaultPermissions.ToJSON()
|
|
assert.NoError(t, err)
|
|
|
|
matrix, err := PermissionMatrixFromJSON(data)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, PermissionRead, matrix[DimensionConversationManage])
|
|
}
|
|
|
|
func TestPermissionMatrixFromJSON_InvalidLevel(t *testing.T) {
|
|
invalidJSON := `{"conversation_manage": "superuser"}`
|
|
matrix, err := PermissionMatrixFromJSON([]byte(invalidJSON))
|
|
assert.Error(t, err)
|
|
assert.Nil(t, matrix)
|
|
}
|
|
|
|
func TestPermissionMatrixFromJSON_Empty(t *testing.T) {
|
|
matrix, err := PermissionMatrixFromJSON([]byte(`{}`))
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, matrix)
|
|
}
|
|
|
|
// === PolicyContext tests ===
|
|
|
|
func TestNewPolicyContext_Administrator(t *testing.T) {
|
|
pc := NewPolicyContext(1, 1, "administrator", 0, nil)
|
|
assert.True(t, pc.IsAdministrator())
|
|
assert.False(t, pc.IsAgent())
|
|
assert.False(t, pc.IsCustomRole())
|
|
assert.Equal(t, AdministratorPermissions, pc.Permissions)
|
|
}
|
|
|
|
func TestNewPolicyContext_Agent(t *testing.T) {
|
|
pc := NewPolicyContext(2, 1, "agent", 0, nil)
|
|
assert.False(t, pc.IsAdministrator())
|
|
assert.True(t, pc.IsAgent())
|
|
assert.False(t, pc.IsCustomRole())
|
|
assert.Equal(t, AgentDefaultPermissions, pc.Permissions)
|
|
}
|
|
|
|
func TestNewPolicyContext_CustomRole(t *testing.T) {
|
|
customPerms := PermissionMatrixMap{
|
|
DimensionConversationManage: PermissionFull,
|
|
DimensionContactManage: PermissionRead,
|
|
}
|
|
pc := NewPolicyContext(3, 1, "custom_role", 10, customPerms)
|
|
assert.True(t, pc.IsCustomRole())
|
|
assert.Equal(t, customPerms, pc.Permissions)
|
|
}
|
|
|
|
func TestNewPolicyContext_CustomRole_FallbackToAgent(t *testing.T) {
|
|
pc := NewPolicyContext(3, 1, "custom_role", 10, nil)
|
|
assert.True(t, pc.IsCustomRole())
|
|
assert.Equal(t, AgentDefaultPermissions, pc.Permissions) // nil perms → agent defaults
|
|
}
|
|
|
|
// === Can() tests ===
|
|
|
|
func TestCan_AdministratorAlwaysTrue(t *testing.T) {
|
|
pc := NewPolicyContext(1, 1, "administrator", 0, nil)
|
|
actions := []string{"manage", "read", "create", "update", "delete", "assign", "resolve"}
|
|
resources := []string{"conversation", "contact", "report", "knowledge_base", "automation"}
|
|
for _, action := range actions {
|
|
for _, resource := range resources {
|
|
assert.True(t, pc.Can(action, resource), "admin Can(%s,%s) should be true", action, resource)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCan_AgentConversationManage(t *testing.T) {
|
|
pc := NewPolicyContext(2, 1, "agent", 0, nil)
|
|
assert.True(t, pc.Can("read", "conversation")) // agent has read on conversation_manage
|
|
assert.False(t, pc.Can("manage", "conversation")) // agent has read, not full
|
|
assert.False(t, pc.Can("create", "conversation"))
|
|
assert.False(t, pc.Can("update", "conversation"))
|
|
}
|
|
|
|
func TestCan_AgentConversationDelete(t *testing.T) {
|
|
pc := NewPolicyContext(2, 1, "agent", 0, nil)
|
|
assert.False(t, pc.Can("delete", "conversation")) // agent has none on conversation_delete
|
|
}
|
|
|
|
func TestCan_AgentContactManage(t *testing.T) {
|
|
pc := NewPolicyContext(2, 1, "agent", 0, nil)
|
|
assert.True(t, pc.Can("read", "contact"))
|
|
assert.False(t, pc.Can("manage", "contact"))
|
|
}
|
|
|
|
func TestCan_AgentNoReportAccess(t *testing.T) {
|
|
pc := NewPolicyContext(2, 1, "agent", 0, nil)
|
|
assert.False(t, pc.Can("read", "report"))
|
|
assert.False(t, pc.Can("manage", "report"))
|
|
}
|
|
|
|
func TestCan_AgentNoKnowledgeBaseAccess(t *testing.T) {
|
|
pc := NewPolicyContext(2, 1, "agent", 0, nil)
|
|
assert.False(t, pc.Can("read", "knowledge_base"))
|
|
}
|
|
|
|
func TestCan_AgentNoAutomationAccess(t *testing.T) {
|
|
pc := NewPolicyContext(2, 1, "agent", 0, nil)
|
|
assert.False(t, pc.Can("read", "automation"))
|
|
}
|
|
|
|
func TestCan_CustomRoleFullConversation(t *testing.T) {
|
|
pc := NewPolicyContext(3, 1, "custom_role", 10, PermissionMatrixMap{
|
|
DimensionConversationManage: PermissionFull,
|
|
DimensionConversationDelete: PermissionFull,
|
|
})
|
|
assert.True(t, pc.Can("manage", "conversation"))
|
|
assert.True(t, pc.Can("create", "conversation"))
|
|
assert.True(t, pc.Can("read", "conversation"))
|
|
assert.True(t, pc.Can("delete", "conversation"))
|
|
}
|
|
|
|
func TestCan_CustomRoleMixedPermissions(t *testing.T) {
|
|
pc := NewPolicyContext(3, 1, "custom_role", 10, PermissionMatrixMap{
|
|
DimensionConversationManage: PermissionRead,
|
|
DimensionContactManage: PermissionFull,
|
|
DimensionReportManage: PermissionNone,
|
|
})
|
|
assert.True(t, pc.Can("read", "conversation"))
|
|
assert.False(t, pc.Can("manage", "conversation"))
|
|
assert.True(t, pc.Can("manage", "contact"))
|
|
assert.False(t, pc.Can("read", "report"))
|
|
}
|
|
|
|
func TestCan_UnknownResource(t *testing.T) {
|
|
pc := NewPolicyContext(2, 1, "agent", 0, nil)
|
|
assert.False(t, pc.Can("read", "unknown_resource"))
|
|
}
|
|
|
|
// === mapActionToDimension tests ===
|
|
|
|
func TestMapActionToDimension_Conversation(t *testing.T) {
|
|
assert.Equal(t, DimensionConversationDelete, mapActionToDimension("delete", "conversation"))
|
|
assert.Equal(t, DimensionConversationManage, mapActionToDimension("read", "conversation"))
|
|
assert.Equal(t, DimensionConversationManage, mapActionToDimension("manage", "conversation"))
|
|
assert.Equal(t, DimensionConversationManage, mapActionToDimension("create", "conversation"))
|
|
}
|
|
|
|
func TestMapActionToDimension_OtherResources(t *testing.T) {
|
|
assert.Equal(t, DimensionContactManage, mapActionToDimension("read", "contact"))
|
|
assert.Equal(t, DimensionReportManage, mapActionToDimension("read", "report"))
|
|
assert.Equal(t, DimensionKnowledgeBaseManage, mapActionToDimension("manage", "knowledge_base"))
|
|
assert.Equal(t, DimensionAutomationManage, mapActionToDimension("create", "automation"))
|
|
}
|
|
|
|
func TestMapActionToDimension_UnknownResource(t *testing.T) {
|
|
assert.Equal(t, PermissionDimension(""), mapActionToDimension("read", "widget"))
|
|
}
|
|
|
|
// === matchesAction tests ===
|
|
|
|
func TestMatchesAction_Read(t *testing.T) {
|
|
assert.True(t, matchesAction(PermissionFull, "read"))
|
|
assert.True(t, matchesAction(PermissionRead, "read"))
|
|
assert.False(t, matchesAction(PermissionNone, "read"))
|
|
}
|
|
|
|
func TestMatchesAction_WriteActions(t *testing.T) {
|
|
writeActions := []string{"manage", "create", "update", "assign", "resolve", "manage_labels", "delete"}
|
|
for _, action := range writeActions {
|
|
assert.True(t, matchesAction(PermissionFull, action), "full should match %s", action)
|
|
assert.False(t, matchesAction(PermissionRead, action), "read should not match %s", action)
|
|
assert.False(t, matchesAction(PermissionNone, action), "none should not match %s", action)
|
|
}
|
|
}
|
|
|
|
func TestMatchesAction_UnknownAction(t *testing.T) {
|
|
assert.False(t, matchesAction(PermissionFull, "teleport"))
|
|
} |