Files
query-orchestration/test/integration/admin_integration_test.go
T
Jay Brown c668485e6f Merged in feature/cognito-shared-environments (pull request #211)
cognito and permit shared prod environment support

* code complete

* user creattion tool

test harness
2026-02-26 12:33:35 +00:00

859 lines
33 KiB
Go

//go:build integration
// admin_integration_test.go provides integration tests for admin user management operations.
// These tests require real AWS Cognito and Permit.io credentials and are not run in CI/CD.
// Run with: go test -tags=integration ./test/integration/...
package integration
import (
"context"
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
"github.com/google/uuid"
"github.com/joho/godotenv"
"github.com/stretchr/testify/suite"
"queryorchestration/internal/serviceconfig/aws"
"queryorchestration/internal/usermanagement"
)
// AdminIntegrationTestSuite provides integration testing for admin user management operations
// using real AWS Cognito and Permit.io services.
type AdminIntegrationTestSuite struct {
suite.Suite
cognitoClient *cognitoidentityprovider.Client
cognitoConfig *usermanagement.CognitoConfig
httpClient *http.Client
permitConfig *usermanagement.PermitConfig
testUsers []string // Track users for cleanup (emails)
permitOnlyUsers map[string]string // Track Permit.io-only users: email -> SubjectID
}
// SetupSuite runs once before all tests to initialize clients and validate configuration.
func (s *AdminIntegrationTestSuite) SetupSuite() {
// Load .env file if it exists (supports running tests from project root or test/integration)
// Using Overload() to override shell environment variables (e.g., from AWS SSO)
envPaths := []string{
".env", // Running from test/integration directory
"test/integration/.env", // Running from project root
filepath.Join("..", "..", ".env"), // Alternative from test/integration
}
envLoaded := false
for _, envPath := range envPaths {
if err := godotenv.Overload(envPath); err == nil {
s.T().Logf("Loaded environment variables from: %s (overriding shell variables)", envPath)
envLoaded = true
break
}
}
if !envLoaded {
s.T().Log("No .env file found - using environment variables from shell")
}
// Validate required environment variables
requiredEnvVars := map[string]string{
"AWS_REGION": "AWS region where Cognito User Pool is located (e.g., us-east-1)",
"COGNITO_USER_POOL_ID": "AWS Cognito User Pool ID (e.g., us-east-1_XXXXXXXXX)",
"PERMIT_IO_API_KEY": "Permit.io API key (starts with 'permit_key_')",
"PERMIT_IO_PROJECT_ID": "Permit.io project identifier",
"PERMIT_IO_ENV_ID": "Permit.io environment identifier (e.g., dev, staging, production)",
}
missingVars := make([]string, 0)
for envVar, description := range requiredEnvVars {
if os.Getenv(envVar) == "" {
missingVars = append(missingVars, fmt.Sprintf(" - %s: %s", envVar, description))
}
}
if len(missingVars) > 0 {
errorMsg := "\n\n❌ Missing required environment variables for integration tests\n\n" +
"The following environment variables must be set:\n"
for _, line := range missingVars {
errorMsg += line + "\n"
}
errorMsg += "\nSetup instructions:\n" +
" 1. Copy test/integration/.env.example to test/integration/.env\n" +
" 2. Fill in your AWS and Permit.io credentials\n" +
" 3. Run tests: go test -tags=integration -v ./test/integration/...\n\n" +
"Note: The .env file will be automatically loaded from test/integration/.env\n" +
"See test/integration/README.md for detailed setup instructions.\n"
s.T().Fatal(errorMsg)
}
// Initialize AWS Cognito client
awsConfig, err := aws.GetAWSConfig(context.Background())
s.Require().NoError(err, "Failed to get AWS config")
s.cognitoClient = cognitoidentityprovider.NewFromConfig(awsConfig)
s.cognitoConfig = &usermanagement.CognitoConfig{
UserPoolID: os.Getenv("COGNITO_USER_POOL_ID"),
Region: os.Getenv("AWS_REGION"),
}
// Initialize Permit.io client
s.httpClient = &http.Client{
Timeout: 30 * time.Second,
}
defaultTenant := os.Getenv("PERMIT_IO_TENANT")
if defaultTenant == "" {
defaultTenant = "default"
}
baseURL := os.Getenv("PERMIT_IO_BASE_URL")
if baseURL == "" {
baseURL = "https://api.permit.io"
}
s.permitConfig = &usermanagement.PermitConfig{
APIKey: os.Getenv("PERMIT_IO_API_KEY"),
BaseURL: baseURL,
DefaultTenant: defaultTenant,
}
s.testUsers = make([]string, 0)
s.permitOnlyUsers = make(map[string]string)
s.T().Logf("Integration test suite initialized")
s.T().Logf("AWS Region: %s", s.cognitoConfig.Region)
s.T().Logf("Cognito User Pool: %s", s.cognitoConfig.UserPoolID)
s.T().Logf("Permit.io Base URL: %s", s.permitConfig.BaseURL)
}
// TearDownSuite runs once after all tests to cleanup test resources.
func (s *AdminIntegrationTestSuite) TearDownSuite() {
s.T().Log("\n=== Verifying and Cleaning Up Test Users ===")
ctx := context.Background()
// Check if Permit.io cleanup should be skipped (useful for manual inspection)
skipPermitCleanup := os.Getenv("SKIP_PERMITIO_CLEANUP") == "true"
if skipPermitCleanup {
s.T().Log("⚠️ SKIP_PERMITIO_CLEANUP=true - Permit.io users will NOT be deleted")
}
for _, email := range s.testUsers {
s.T().Logf("\n--- User: %s ---", email)
// Check if this is a Permit.io-only user
if subjectID, isPermitOnly := s.permitOnlyUsers[email]; isPermitOnly {
s.T().Logf("🔵 Permit.io-only user (SubjectID: %s)", subjectID)
// Verify user exists in Permit.io
roles, err := usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
if err != nil {
s.T().Logf("❌ User NOT found in Permit.io: %v", err)
} else {
s.T().Logf("✅ User found in Permit.io:")
s.T().Logf(" - Roles: %v", roles)
}
// Delete from Permit.io (unless skipped)
if skipPermitCleanup {
s.T().Logf("⏭️ Skipping Permit.io cleanup - user left for manual inspection")
} else {
err = usermanagement.DeletePermitUser(s.httpClient, s.permitConfig, subjectID)
if err != nil {
s.T().Logf("⚠️ Warning: Failed to delete from Permit.io: %v", err)
} else {
s.T().Logf("🗑️ Deleted from Permit.io")
}
}
continue
}
// Full integration user - cleanup both Cognito and Permit.io
cognitoUser, err := usermanagement.GetCognitoUser(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
if err != nil {
s.T().Logf("❌ User NOT found in Cognito: %v", err)
} else {
s.T().Logf("✅ User found in Cognito:")
s.T().Logf(" - SubjectID: %s", cognitoUser.SubjectID)
s.T().Logf(" - Email: %s", cognitoUser.Email)
s.T().Logf(" - Name: %s %s", cognitoUser.FirstName, cognitoUser.LastName)
s.T().Logf(" - Enabled: %v", cognitoUser.Enabled)
// Verify user exists in Permit.io
roles, err := usermanagement.GetUserRoles(s.httpClient, s.permitConfig, cognitoUser.SubjectID)
if err != nil {
s.T().Logf("❌ User NOT found in Permit.io: %v", err)
} else {
s.T().Logf("✅ User found in Permit.io:")
s.T().Logf(" - Roles: %v", roles)
}
// Delete from Permit.io first (unless skipped)
if skipPermitCleanup {
s.T().Logf("⏭️ Skipping Permit.io cleanup - user left for manual inspection")
} else {
err = usermanagement.DeletePermitUser(s.httpClient, s.permitConfig, cognitoUser.SubjectID)
if err != nil {
s.T().Logf("⚠️ Warning: Failed to delete from Permit.io: %v", err)
} else {
s.T().Logf("🗑️ Deleted from Permit.io")
}
}
}
// Delete from Cognito
err = usermanagement.DeleteCognitoUser(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
if err != nil {
s.T().Logf("⚠️ Warning: Failed to delete from Cognito: %v", err)
} else {
s.T().Logf("🗑️ Deleted from Cognito")
}
}
s.T().Logf("\n=== Cleanup Complete ===")
}
// generateTestEmail generates a unique email for testing.
func (s *AdminIntegrationTestSuite) generateTestEmail(testName string) string {
prefix := os.Getenv("TEST_USER_EMAIL_PREFIX")
if prefix == "" {
prefix = "integration-test-"
}
domain := os.Getenv("TEST_USER_DOMAIN")
if domain == "" {
domain = "example.com"
}
timestamp := time.Now().Unix()
return fmt.Sprintf("%s%s-%d@%s", prefix, testName, timestamp, domain)
}
// generateTestSubjectID generates a UUID for testing Permit.io without Cognito.
// Returns email and subjectID. The email is tracked for cleanup, subjectID is used for Permit.io operations.
func (s *AdminIntegrationTestSuite) generateTestSubjectID(testName string) (email string, subjectID string) {
email = s.generateTestEmail(testName)
subjectID = uuid.New().String()
s.testUsers = append(s.testUsers, email)
s.permitOnlyUsers[email] = subjectID
return email, subjectID
}
// TestCreateUser tests creating a new user in both Cognito and Permit.io.
func (s *AdminIntegrationTestSuite) TestCreateUser() {
ctx := context.Background()
email := s.generateTestEmail("create")
s.testUsers = append(s.testUsers, email)
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "Test",
LastName: "User",
}
// Create user
user, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create Cognito user")
s.Require().NotEmpty(user.SubjectID, "Subject ID should not be empty")
s.Equal(email, user.Email)
s.Equal("Test", user.FirstName)
s.Equal("User", user.LastName)
// Create in Permit.io
err = usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, user)
s.Require().NoError(err, "Failed to create Permit.io user")
// Verify user exists in Cognito
retrieved, err := usermanagement.GetCognitoUser(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.Require().NoError(err, "Failed to get Cognito user")
s.Equal(user.SubjectID, retrieved.SubjectID)
}
// TestCreateUserIdempotent tests that creating an existing user is idempotent.
func (s *AdminIntegrationTestSuite) TestCreateUserIdempotent() {
ctx := context.Background()
email := s.generateTestEmail("idempotent")
s.testUsers = append(s.testUsers, email)
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "Idempotent",
LastName: "Test",
}
// Create user first time
user1, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create Cognito user first time")
// Create in Permit.io
err = usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, user1)
s.Require().NoError(err, "Failed to create Permit.io user")
// Attempt to create same user again should succeed (idempotent behavior tested in handler)
exists := usermanagement.UserExistsInCognito(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.True(exists, "User should exist")
}
// TestGetUser tests retrieving an existing user.
func (s *AdminIntegrationTestSuite) TestGetUser() {
ctx := context.Background()
email := s.generateTestEmail("get")
s.testUsers = append(s.testUsers, email)
// Create user first
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "Get",
LastName: "Test",
}
user, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create user")
// Get user
retrieved, err := usermanagement.GetCognitoUser(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.Require().NoError(err, "Failed to get user")
s.Equal(user.SubjectID, retrieved.SubjectID)
s.Equal(email, retrieved.Email)
s.Equal("Get", retrieved.FirstName)
s.Equal("Test", retrieved.LastName)
}
// TestUpdateUserAttributes tests updating user attributes in Cognito.
func (s *AdminIntegrationTestSuite) TestUpdateUserAttributes() {
ctx := context.Background()
email := s.generateTestEmail("update")
s.testUsers = append(s.testUsers, email)
// Create user
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "Original",
LastName: "Name",
}
_, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create user")
// Update attributes
updateAttrs := usermanagement.UserAttributes{
FirstName: "Updated",
LastName: "Name",
}
err = usermanagement.UpdateCognitoUserAttributes(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email, updateAttrs)
s.Require().NoError(err, "Failed to update user attributes")
// Verify updates
retrieved, err := usermanagement.GetCognitoUser(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.Require().NoError(err, "Failed to get updated user")
s.Equal("Updated", retrieved.FirstName)
s.Equal("Name", retrieved.LastName)
}
// TestDisableEnableUser tests disabling and enabling a user.
func (s *AdminIntegrationTestSuite) TestDisableEnableUser() {
ctx := context.Background()
email := s.generateTestEmail("disable")
s.testUsers = append(s.testUsers, email)
// Create user
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "Disable",
LastName: "Test",
}
user, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create user")
// User should be enabled by default
enabled, err := usermanagement.IsCognitoUserEnabled(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.Require().NoError(err, "Failed to check if user is enabled")
s.True(enabled, "User should be enabled initially")
// Disable user
err = usermanagement.DisableCognitoUser(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.Require().NoError(err, "Failed to disable user")
// Verify disabled
enabled, err = usermanagement.IsCognitoUserEnabled(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.Require().NoError(err, "Failed to check if user is disabled")
s.False(enabled, "User should be disabled")
// Re-enable user
err = usermanagement.EnableCognitoUser(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, user.Username)
s.Require().NoError(err, "Failed to enable user")
// Verify enabled
enabled, err = usermanagement.IsCognitoUserEnabled(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.Require().NoError(err, "Failed to check if user is re-enabled")
s.True(enabled, "User should be re-enabled")
}
// TestDeleteUser tests deleting a user from both systems.
func (s *AdminIntegrationTestSuite) TestDeleteUser() {
ctx := context.Background()
email := s.generateTestEmail("delete")
// Create user
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "Delete",
LastName: "Test",
}
user, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create user")
// Create in Permit.io
err = usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, user)
s.Require().NoError(err, "Failed to create Permit.io user")
// Delete user
err = usermanagement.DeleteCognitoUser(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.Require().NoError(err, "Failed to delete Cognito user")
// Delete from Permit.io
err = usermanagement.DeletePermitUser(s.httpClient, s.permitConfig, user.SubjectID)
s.Require().NoError(err, "Failed to delete Permit.io user")
// Verify user doesn't exist
exists := usermanagement.UserExistsInCognito(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, email)
s.False(exists, "User should not exist after deletion")
// Don't add to cleanup list since already deleted
}
// TestListUsers tests listing users with pagination.
func (s *AdminIntegrationTestSuite) TestListUsers() {
ctx := context.Background()
// Create multiple test users
numUsers := 3
for i := 0; i < numUsers; i++ {
email := s.generateTestEmail(fmt.Sprintf("list-%d", i))
s.testUsers = append(s.testUsers, email)
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: fmt.Sprintf("User%d", i),
LastName: "Test",
}
_, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create test user %d", i)
}
// List users (may include other users in the pool)
result, err := usermanagement.ListCognitoUsers(ctx, s.cognitoClient, s.cognitoConfig.UserPoolID, 60, nil)
s.Require().NoError(err, "Failed to list users")
s.NotNil(result, "Result should not be nil")
s.NotEmpty(result.Users, "Should have at least some users")
// Verify our test users are in the list
foundCount := 0
for _, user := range result.Users {
for _, testEmail := range s.testUsers {
if user.Email == testEmail {
foundCount++
}
}
}
s.GreaterOrEqual(foundCount, 1, "Should find at least one of our test users")
}
// TestRoleAssignment tests assigning and unassigning roles in Permit.io.
func (s *AdminIntegrationTestSuite) TestRoleAssignment() {
ctx := context.Background()
email := s.generateTestEmail("role")
s.testUsers = append(s.testUsers, email)
// Create user
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "Role",
LastName: "Test",
}
user, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create user")
// Create in Permit.io
err = usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, user)
s.Require().NoError(err, "Failed to create Permit.io user")
// Try roles defined in permit_policies.yaml - use the first one that exists
testRoles := []string{"super_admin", "user_admin", "auditor", "client_user"}
var testRole string
var roleFound bool
for _, role := range testRoles {
err = usermanagement.AssignRole(s.httpClient, s.permitConfig, user.SubjectID, role)
if err == nil {
testRole = role
roleFound = true
s.T().Logf("✅ Using role '%s' for testing", testRole)
break
}
// Check if it's a 404 (role doesn't exist) vs other errors
if err != nil && !strings.Contains(err.Error(), "404") && !strings.Contains(err.Error(), "NOT_FOUND") {
// Some other error occurred, fail the test
s.Require().NoError(err, "Failed to assign role %s (unexpected error)", role)
}
}
if !roleFound {
s.T().Skip("\n❌ SKIPPED: No suitable roles found in Permit.io environment.\n\n" +
"To run this test, ensure the Permit.io environment has roles defined.\n" +
"Expected roles (from permit_policies.yaml):\n" +
" - super_admin\n - user_admin\n - auditor\n - client_user\n\n" +
"Run: task permit:setup (or use cmd/auth_related/permit.setup/setup_permit)\n" +
"Visit: https://app.permit.io")
return
}
// Get roles - verify assignment worked
roles, err := usermanagement.GetUserRoles(s.httpClient, s.permitConfig, user.SubjectID)
s.Require().NoError(err, "Failed to get user roles")
s.Contains(roles, testRole, "User should have the assigned role")
s.T().Logf("✅ User has role '%s': %v", testRole, roles)
// Unassign role
err = usermanagement.UnassignRole(s.httpClient, s.permitConfig, user.SubjectID, testRole)
s.Require().NoError(err, "Failed to unassign role")
// Verify role removed
roles, err = usermanagement.GetUserRoles(s.httpClient, s.permitConfig, user.SubjectID)
s.Require().NoError(err, "Failed to get user roles after unassignment")
s.NotContains(roles, testRole, "User should not have the role after unassignment")
s.T().Logf("✅ Role '%s' successfully unassigned. Current roles: %v", testRole, roles)
}
// ============================================================================
// Permit.io-Specific Integration Tests
// ============================================================================
// These tests focus specifically on Permit.io operations independent of Cognito.
// They verify that all Permit.io user management functions work correctly.
// TestPermitIO_CreateAndGetUser tests creating a user in Permit.io and verifying it exists.
func (s *AdminIntegrationTestSuite) TestPermitIO_CreateAndGetUser() {
email, subjectID := s.generateTestSubjectID("permitio-create")
// Create user in Permit.io with generated UUID
permitUser := &usermanagement.CognitoUserResponse{
SubjectID: subjectID,
Email: email,
FirstName: "PermitIO",
LastName: "CreateTest",
}
err := usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, permitUser)
s.Require().NoError(err, "Failed to create Permit.io user")
s.T().Logf("✅ Created user in Permit.io: %s (SubjectID: %s)", email, subjectID)
// Verify user exists by getting roles (should return empty list, not error)
roles, err := usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to get user roles from Permit.io")
s.NotNil(roles, "Roles should not be nil")
s.Empty(roles, "New user should have no roles initially")
s.T().Logf("✅ Verified user exists in Permit.io with no roles")
}
// TestPermitIO_AssignMultipleRoles tests assigning multiple roles to a user.
func (s *AdminIntegrationTestSuite) TestPermitIO_AssignMultipleRoles() {
email, subjectID := s.generateTestSubjectID("permitio-multirole")
// Create user in Permit.io
permitUser := &usermanagement.CognitoUserResponse{
SubjectID: subjectID,
Email: email,
FirstName: "PermitIO",
LastName: "MultiRoleTest",
}
err := usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, permitUser)
s.Require().NoError(err, "Failed to create Permit.io user")
// Try to assign multiple roles from permit_policies.yaml
rolesToAssign := []string{"auditor", "client_user"}
assignedRoles := make([]string, 0)
for _, role := range rolesToAssign {
err = usermanagement.AssignRole(s.httpClient, s.permitConfig, subjectID, role)
if err == nil {
assignedRoles = append(assignedRoles, role)
s.T().Logf("✅ Assigned role '%s'", role)
} else if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "NOT_FOUND") {
s.T().Logf("⚠️ Role '%s' not found in environment, skipping", role)
} else {
s.Require().NoError(err, "Unexpected error assigning role %s", role)
}
}
// Verify at least one role was assigned
s.NotEmpty(assignedRoles, "Should have assigned at least one role")
// Get roles and verify
roles, err := usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to get user roles")
for _, assignedRole := range assignedRoles {
s.Contains(roles, assignedRole, "User should have role %s", assignedRole)
}
s.T().Logf("✅ Verified user has all assigned roles: %v", roles)
// Unassign all roles
for _, role := range assignedRoles {
err = usermanagement.UnassignRole(s.httpClient, s.permitConfig, subjectID, role)
s.Require().NoError(err, "Failed to unassign role %s", role)
s.T().Logf("✅ Unassigned role '%s'", role)
}
// Verify all roles removed
roles, err = usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to get user roles after unassignment")
s.Empty(roles, "User should have no roles after unassignment")
s.T().Logf("✅ Verified all roles unassigned: %v", roles)
}
// TestPermitIO_DeleteUser tests deleting a user from Permit.io and verifying it's gone.
func (s *AdminIntegrationTestSuite) TestPermitIO_DeleteUser() {
email, subjectID := s.generateTestSubjectID("permitio-delete")
// Create user in Permit.io
permitUser := &usermanagement.CognitoUserResponse{
SubjectID: subjectID,
Email: email,
FirstName: "PermitIO",
LastName: "DeleteTest",
}
err := usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, permitUser)
s.Require().NoError(err, "Failed to create Permit.io user")
s.T().Logf("✅ Created user in Permit.io")
// Verify user exists
roles, err := usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "User should exist before deletion")
s.T().Logf("✅ Verified user exists (roles: %v)", roles)
// Delete user from Permit.io
err = usermanagement.DeletePermitUser(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to delete Permit.io user")
s.T().Logf("✅ Deleted user from Permit.io")
// Verify user no longer exists by attempting to delete again
// Note: GetUserRoles returns empty array for non-existent users (normal API behavior)
// so we verify deletion by trying to delete again and expecting 404
err = usermanagement.DeletePermitUser(s.httpClient, s.permitConfig, subjectID)
s.Error(err, "Deleting non-existent user should return 404 error")
s.Contains(err.Error(), "404", "Should get 404 error for deleted user")
s.T().Logf("✅ Verified user deleted (second delete returned 404)")
}
// TestPermitIO_UserLifecycle tests the complete user lifecycle in Permit.io.
func (s *AdminIntegrationTestSuite) TestPermitIO_UserLifecycle() {
email, subjectID := s.generateTestSubjectID("permitio-lifecycle")
s.T().Log("=== Starting Permit.io User Lifecycle Test ===")
// Step 1: Generate test SubjectID
s.T().Logf("Step 1: ✅ Generated test SubjectID: %s", subjectID)
// Step 2: Create user in Permit.io
permitUser := &usermanagement.CognitoUserResponse{
SubjectID: subjectID,
Email: email,
FirstName: "PermitIO",
LastName: "LifecycleTest",
}
err := usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, permitUser)
s.Require().NoError(err, "Failed to create Permit.io user")
s.T().Log("Step 2: ✅ Created user in Permit.io")
// Step 3: Verify user has no roles initially
roles, err := usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to get initial roles")
s.Empty(roles, "New user should have no roles")
s.T().Logf("Step 3: ✅ Verified no initial roles: %v", roles)
// Step 4: Assign a role
testRole := "auditor" // Use a role from permit_policies.yaml
err = usermanagement.AssignRole(s.httpClient, s.permitConfig, subjectID, testRole)
if err != nil && (strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "NOT_FOUND")) {
// Try alternative role if auditor doesn't exist
testRole = "client_user"
err = usermanagement.AssignRole(s.httpClient, s.permitConfig, subjectID, testRole)
}
s.Require().NoError(err, "Failed to assign role")
s.T().Logf("Step 4: ✅ Assigned role '%s'", testRole)
// Step 5: Verify role was assigned
roles, err = usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to get roles after assignment")
s.Contains(roles, testRole, "User should have the assigned role")
s.T().Logf("Step 5: ✅ Verified role assigned: %v", roles)
// Step 6: Unassign the role
err = usermanagement.UnassignRole(s.httpClient, s.permitConfig, subjectID, testRole)
s.Require().NoError(err, "Failed to unassign role")
s.T().Logf("Step 6: ✅ Unassigned role '%s'", testRole)
// Step 7: Verify role was unassigned
roles, err = usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to get roles after unassignment")
s.NotContains(roles, testRole, "User should not have the role after unassignment")
s.T().Logf("Step 7: ✅ Verified role unassigned: %v", roles)
// Step 8: Delete user from Permit.io
err = usermanagement.DeletePermitUser(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to delete user")
s.T().Log("Step 8: ✅ Deleted user from Permit.io")
// Step 9: Verify user is deleted by attempting to delete again (should get 404)
err = usermanagement.DeletePermitUser(s.httpClient, s.permitConfig, subjectID)
s.Error(err, "Deleting non-existent user should return 404 error")
s.Contains(err.Error(), "404", "Should get 404 error for deleted user")
s.T().Log("Step 9: ✅ Verified user deleted (second delete returned 404)")
s.T().Log("=== Permit.io User Lifecycle Test Complete ===")
}
// TestPermitIO_IdempotentOperations tests that Permit.io operations handle duplicates gracefully.
func (s *AdminIntegrationTestSuite) TestPermitIO_IdempotentOperations() {
email, subjectID := s.generateTestSubjectID("permitio-idempotent")
// Create user in Permit.io
permitUser := &usermanagement.CognitoUserResponse{
SubjectID: subjectID,
Email: email,
FirstName: "PermitIO",
LastName: "IdempotentTest",
}
err := usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, permitUser)
s.Require().NoError(err, "Failed to create Permit.io user first time")
s.T().Log("✅ Created user in Permit.io (first time)")
// Try to create the same user again - should handle gracefully
err = usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, permitUser)
// Note: Depending on implementation, this might succeed (idempotent) or return conflict
// Just log the result, don't fail the test
if err != nil {
s.T().Logf("Creating duplicate user returned error (expected): %v", err)
} else {
s.T().Log("✅ Creating duplicate user succeeded (idempotent)")
}
// Assign a role
testRole := "auditor"
err = usermanagement.AssignRole(s.httpClient, s.permitConfig, subjectID, testRole)
if err != nil && (strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "NOT_FOUND")) {
testRole = "client_user"
err = usermanagement.AssignRole(s.httpClient, s.permitConfig, subjectID, testRole)
}
s.Require().NoError(err, "Failed to assign role")
s.T().Logf("✅ Assigned role '%s'", testRole)
// Assign the same role again - should handle gracefully
err = usermanagement.AssignRole(s.httpClient, s.permitConfig, subjectID, testRole)
if err != nil {
s.T().Logf("Assigning duplicate role returned error (may be expected): %v", err)
} else {
s.T().Logf("✅ Assigning duplicate role succeeded (idempotent)")
}
// Verify user still has the role (only once)
roles, err := usermanagement.GetUserRoles(s.httpClient, s.permitConfig, subjectID)
s.Require().NoError(err, "Failed to get user roles")
s.Contains(roles, testRole, "User should have the role")
// Count occurrences of the role (should be exactly 1)
count := 0
for _, r := range roles {
if r == testRole {
count++
}
}
s.Equal(1, count, "Role should appear exactly once, not duplicated")
s.T().Logf("✅ Verified role appears exactly once: %v", roles)
}
// TestPermitIO_CreateUserWithEnvironmentID verifies that when a Cognito user is created
// with an EnvironmentID, the corresponding Permit.io user has "environment_id" stored in
// its attributes map. This closes the gap where the Cognito side was tested but the
// Permit.io attribute propagation was not.
func (s *AdminIntegrationTestSuite) TestPermitIO_CreateUserWithEnvironmentID() {
ctx := context.Background()
email := s.generateTestEmail("permitio-envid")
s.testUsers = append(s.testUsers, email)
const testEnvID = "integrationtest"
// Step 1: Create user in Cognito with EnvironmentID
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "PermitEnv",
LastName: "Test",
EnvironmentID: testEnvID,
}
cognitoUser, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create Cognito user with EnvironmentID")
s.Equal(testEnvID, cognitoUser.EnvironmentID,
"Cognito user should have EnvironmentID set")
s.T().Logf("Created Cognito user %s (sub=%s, env=%s)", email, cognitoUser.SubjectID, cognitoUser.EnvironmentID)
// Step 2: Create user in Permit.io (should propagate environment_id to attributes)
err = usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, cognitoUser)
s.Require().NoError(err, "Failed to create Permit.io user")
s.T().Log("Created user in Permit.io")
// Step 3: Fetch the Permit.io user and verify environment_id is in attributes
permitUser, err := usermanagement.GetPermitUser(s.httpClient, s.permitConfig, cognitoUser.SubjectID)
s.Require().NoError(err, "Failed to get Permit.io user")
s.Require().NotNil(permitUser, "Permit.io user should not be nil")
s.Require().NotNil(permitUser.Attributes, "Permit.io user attributes should not be nil")
envIDAttr, ok := permitUser.Attributes["environment_id"]
s.Require().True(ok, "Permit.io user attributes should contain 'environment_id'")
s.Equal(testEnvID, envIDAttr,
"Permit.io environment_id attribute should match the Cognito environment ID")
s.T().Logf("Verified Permit.io user has environment_id=%v", envIDAttr)
}
// TestPermitIO_CreateUserWithoutEnvironmentID verifies that when a Cognito user is created
// without an EnvironmentID (backward-compatible case), the Permit.io user does NOT have
// environment_id in its attributes.
func (s *AdminIntegrationTestSuite) TestPermitIO_CreateUserWithoutEnvironmentID() {
ctx := context.Background()
email := s.generateTestEmail("permitio-noenvid")
s.testUsers = append(s.testUsers, email)
// Create user in Cognito without EnvironmentID
attrs := usermanagement.UserAttributes{
Email: email,
FirstName: "NoEnv",
LastName: "Test",
}
cognitoUser, err := usermanagement.CreateCognitoUser(ctx, s.cognitoClient, s.cognitoConfig, attrs)
s.Require().NoError(err, "Failed to create Cognito user")
s.Empty(cognitoUser.EnvironmentID, "Cognito user should have empty EnvironmentID")
// Create in Permit.io
err = usermanagement.CreatePermitUser(s.httpClient, s.permitConfig, cognitoUser)
s.Require().NoError(err, "Failed to create Permit.io user")
// Fetch and verify no environment_id attribute
permitUser, err := usermanagement.GetPermitUser(s.httpClient, s.permitConfig, cognitoUser.SubjectID)
s.Require().NoError(err, "Failed to get Permit.io user")
s.Require().NotNil(permitUser)
if permitUser.Attributes != nil {
_, hasEnvID := permitUser.Attributes["environment_id"]
s.False(hasEnvID, "Permit.io user should NOT have environment_id when Cognito user has no env ID")
}
s.T().Log("Verified Permit.io user has no environment_id attribute (backward compat)")
}
// TestSuite runs the integration test suite.
func TestSuite(t *testing.T) {
suite.Run(t, new(AdminIntegrationTestSuite))
}