package integration import ( "Coves/internal/core/communities" "Coves/internal/db/postgres" "context" "fmt" "os" "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestCommunityIdentifierResolution tests all formats accepted by ResolveCommunityIdentifier func TestCommunityIdentifierResolution(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode") } db := setupTestDB(t) defer func() { if err := db.Close(); err != nil { t.Logf("Failed to close database: %v", err) } }() repo := postgres.NewCommunityRepository(db) ctx := context.Background() // Get configuration from environment pdsURL := os.Getenv("PDS_URL") if pdsURL == "" { pdsURL = "http://localhost:3001" // Default to dev PDS port (see .env.dev) } instanceDomain := os.Getenv("INSTANCE_DOMAIN") if instanceDomain == "" { instanceDomain = "coves.social" } // Create provisioner (signature: instanceDomain, pdsURL) provisioner := communities.NewPDSAccountProvisioner(instanceDomain, pdsURL) // Create service instanceDID := os.Getenv("INSTANCE_DID") if instanceDID == "" { instanceDID = "did:web:" + instanceDomain } service := communities.NewCommunityService( repo, pdsURL, instanceDID, instanceDomain, provisioner, ) // Create a test community to resolve uniqueName := fmt.Sprintf("test%d", time.Now().UnixNano()%1000000) req := communities.CreateCommunityRequest{ Name: uniqueName, DisplayName: "Test Community", Description: "A test community for identifier resolution", Visibility: "public", CreatedByDID: "did:plc:testowner123", HostedByDID: instanceDID, AllowExternalDiscovery: true, } community, err := service.CreateCommunity(ctx, req) require.NoError(t, err, "Failed to create test community") require.NotNil(t, community) t.Run("DID format", func(t *testing.T) { t.Run("resolves valid DID", func(t *testing.T) { did, err := service.ResolveCommunityIdentifier(ctx, community.DID) require.NoError(t, err) assert.Equal(t, community.DID, did) }) t.Run("rejects non-existent DID", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, "did:plc:nonexistent123") require.Error(t, err) assert.Contains(t, err.Error(), "community not found") }) t.Run("rejects malformed DID", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, "did:invalid") require.Error(t, err) }) }) t.Run("Canonical handle format", func(t *testing.T) { t.Run("resolves lowercase canonical handle", func(t *testing.T) { did, err := service.ResolveCommunityIdentifier(ctx, community.Handle) require.NoError(t, err) assert.Equal(t, community.DID, did) }) t.Run("resolves uppercase canonical handle (case-insensitive)", func(t *testing.T) { // Use actual community handle in uppercase upperHandle := fmt.Sprintf("%s.COMMUNITY.%s", uniqueName, strings.ToUpper(instanceDomain)) did, err := service.ResolveCommunityIdentifier(ctx, upperHandle) require.NoError(t, err) assert.Equal(t, community.DID, did) }) t.Run("rejects non-existent canonical handle", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, fmt.Sprintf("nonexistent.community.%s", instanceDomain)) require.Error(t, err) assert.Contains(t, err.Error(), "community not found") }) }) t.Run("At-identifier format", func(t *testing.T) { t.Run("resolves @-prefixed handle", func(t *testing.T) { atHandle := "@" + community.Handle did, err := service.ResolveCommunityIdentifier(ctx, atHandle) require.NoError(t, err) assert.Equal(t, community.DID, did) }) t.Run("resolves @-prefixed handle with uppercase (case-insensitive)", func(t *testing.T) { atHandle := "@" + fmt.Sprintf("%s.COMMUNITY.%s", uniqueName, strings.ToUpper(instanceDomain)) did, err := service.ResolveCommunityIdentifier(ctx, atHandle) require.NoError(t, err) assert.Equal(t, community.DID, did) }) }) t.Run("Scoped format (!name@instance)", func(t *testing.T) { t.Run("resolves valid scoped identifier", func(t *testing.T) { scopedID := fmt.Sprintf("!%s@%s", uniqueName, instanceDomain) did, err := service.ResolveCommunityIdentifier(ctx, scopedID) require.NoError(t, err) assert.Equal(t, community.DID, did) }) t.Run("resolves uppercase scoped identifier (case-insensitive domain)", func(t *testing.T) { scopedID := fmt.Sprintf("!%s@%s", uniqueName, strings.ToUpper(instanceDomain)) did, err := service.ResolveCommunityIdentifier(ctx, scopedID) require.NoError(t, err, "Should normalize uppercase domain to lowercase") assert.Equal(t, community.DID, did) }) t.Run("resolves mixed-case scoped identifier", func(t *testing.T) { // Mix case of domain mixedDomain := "" for i, c := range instanceDomain { if i%2 == 0 { mixedDomain += strings.ToUpper(string(c)) } else { mixedDomain += strings.ToLower(string(c)) } } scopedID := fmt.Sprintf("!%s@%s", uniqueName, mixedDomain) did, err := service.ResolveCommunityIdentifier(ctx, scopedID) require.NoError(t, err, "Should normalize all parts to lowercase") assert.Equal(t, community.DID, did) }) t.Run("rejects scoped identifier without @ symbol", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, "!testcommunity") require.Error(t, err) assert.Contains(t, err.Error(), "must include @ symbol") }) t.Run("rejects scoped identifier with empty name", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, fmt.Sprintf("!@%s", instanceDomain)) require.Error(t, err) assert.Contains(t, err.Error(), "community name cannot be empty") }) t.Run("rejects scoped identifier with wrong instance", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, "!testcommunity@wrong.social") require.Error(t, err) assert.Contains(t, err.Error(), "not hosted on this instance") }) t.Run("rejects non-existent community in scoped format", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, fmt.Sprintf("!nonexistent@%s", instanceDomain)) require.Error(t, err) assert.Contains(t, err.Error(), "community not found") }) }) t.Run("Edge cases", func(t *testing.T) { t.Run("rejects empty identifier", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, "") require.Error(t, err) }) t.Run("rejects whitespace-only identifier", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, " ") require.Error(t, err) }) t.Run("handles leading/trailing whitespace in valid identifier", func(t *testing.T) { did, err := service.ResolveCommunityIdentifier(ctx, " "+community.Handle+" ") require.NoError(t, err) assert.Equal(t, community.DID, did) }) t.Run("rejects identifier without dots (not a valid handle)", func(t *testing.T) { _, err := service.ResolveCommunityIdentifier(ctx, "nodots") require.Error(t, err) assert.Contains(t, err.Error(), "must be a DID, handle, or scoped identifier") }) }) } // TestResolveScopedIdentifier_InputValidation tests input sanitization func TestResolveScopedIdentifier_InputValidation(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode") } db := setupTestDB(t) defer func() { if err := db.Close(); err != nil { t.Logf("Failed to close database: %v", err) } }() repo := postgres.NewCommunityRepository(db) ctx := context.Background() pdsURL := os.Getenv("PDS_URL") if pdsURL == "" { pdsURL = "http://localhost:3000" } instanceDomain := os.Getenv("INSTANCE_DOMAIN") if instanceDomain == "" { instanceDomain = "coves.social" } instanceDID := os.Getenv("INSTANCE_DID") if instanceDID == "" { instanceDID = "did:web:" + instanceDomain } provisioner := communities.NewPDSAccountProvisioner(instanceDomain, pdsURL) service := communities.NewCommunityService( repo, pdsURL, instanceDID, instanceDomain, provisioner, ) tests := []struct { name string identifier string expectError string }{ { name: "rejects special characters in name", identifier: fmt.Sprintf("!