A community based topic aggregation platform built on atproto

test(communities): update existing tests for V2.0 architecture

Update all existing community tests to align with V2.0 changes:
- Replace password hash fields with encrypted password fields
- Remove DID generator mocks and dependencies
- Update test data structures for PDS-managed keys
- Fix assertions for new community provisioning flow

Modified Test Files:
- community_consumer_test.go: Update Jetstream consumer tests
- community_credentials_test.go: Update credential validation tests
- community_e2e_test.go: Update end-to-end workflow tests
- community_repo_test.go: Update repository layer tests
- user_test.go: Add helper functions for test DIDs
- community_service_test.go: Update service layer unit tests

Breaking Changes:
- Communities no longer have Coves-generated DIDs
- Password field is now encrypted, not hashed
- Service constructors simplified (no DID generator)

All tests pass with new V2.0 architecture.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

+4 -19
tests/integration/community_consumer_test.go
···
package integration
import (
-
"Coves/internal/atproto/did"
"Coves/internal/atproto/jetstream"
"Coves/internal/core/communities"
"Coves/internal/db/postgres"
···
repo := postgres.NewCommunityRepository(db)
consumer := jetstream.NewCommunityEventConsumer(repo)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("creates community from firehose event", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
// Simulate a Jetstream commit event
event := &jetstream.JetstreamEvent{
···
})
t.Run("updates existing community", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
handle := fmt.Sprintf("!update-test-%s@coves.local", uniqueSuffix)
// Create initial community
···
})
t.Run("deletes community", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
// Create community to delete
community := &communities.Community{
···
repo := postgres.NewCommunityRepository(db)
consumer := jetstream.NewCommunityEventConsumer(repo)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("creates subscription from event", func(t *testing.T) {
// Create a community first
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
+9 -28
tests/integration/community_credentials_test.go
···
package integration
import (
-
"Coves/internal/atproto/did"
"Coves/internal/core/communities"
"Coves/internal/db/postgres"
"context"
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("persists PDS credentials on create", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
···
Visibility: "public",
// V2: PDS credentials
PDSEmail: "community-test@communities.coves.local",
-
PDSPasswordHash: "$2a$10$abcdefghijklmnopqrstuv", // Mock bcrypt hash
+
PDSPassword: "cleartext-password-encrypted-by-repo", // V2: Cleartext (encrypted by repository)
PDSAccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test.token",
PDSRefreshToken: "refresh_token_xyz123",
PDSURL: "http://localhost:2583",
···
if retrieved.PDSEmail != community.PDSEmail {
t.Errorf("Expected PDSEmail %s, got %s", community.PDSEmail, retrieved.PDSEmail)
}
-
if retrieved.PDSPasswordHash != community.PDSPasswordHash {
-
t.Errorf("Expected PDSPasswordHash to be persisted")
+
if retrieved.PDSPassword != community.PDSPassword {
+
t.Errorf("Expected PDSPassword to be persisted and encrypted/decrypted")
}
if retrieved.PDSAccessToken != community.PDSAccessToken {
t.Errorf("Expected PDSAccessToken to be persisted and decrypted correctly")
···
})
t.Run("handles empty credentials gracefully", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
// Community without PDS credentials (e.g., from Jetstream consumer)
community := &communities.Community{
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("credentials are encrypted in database", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
accessToken := "sensitive_access_token_xyz123"
refreshToken := "sensitive_refresh_token_abc456"
···
HostedByDID: "did:web:coves.local",
Visibility: "public",
PDSEmail: "encrypted@communities.coves.local",
-
PDSPasswordHash: "$2a$10$encrypted",
+
PDSPassword: "cleartext-password-for-encryption", // V2: Cleartext (encrypted by repository)
PDSAccessToken: accessToken,
PDSRefreshToken: refreshToken,
PDSURL: "http://localhost:2583",
···
})
t.Run("encryption handles special characters", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
// Token with special characters
specialToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2NvdmVzLnNvY2lhbCIsInN1YiI6ImRpZDpwbGM6YWJjMTIzIiwiaWF0IjoxNzA5MjQwMDAwfQ.special/chars+here=="
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("V2 communities are self-owned", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
+16 -10
tests/integration/community_e2e_test.go
···
import (
"Coves/internal/api/routes"
-
"Coves/internal/atproto/did"
"Coves/internal/atproto/identity"
"Coves/internal/atproto/jetstream"
"Coves/internal/core/communities"
···
// Setup dependencies
communityRepo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
// Get instance credentials
instanceHandle := os.Getenv("PDS_INSTANCE_HANDLE")
···
t.Logf("✅ Authenticated - Instance DID: %s", instanceDID)
-
// V2: Extract instance domain for community provisioning
+
// V2.0: Extract instance domain for community provisioning
var instanceDomain string
if strings.HasPrefix(instanceDID, "did:web:") {
instanceDomain = strings.TrimPrefix(instanceDID, "did:web:")
···
instanceDomain = "coves.social"
}
-
// V2: Create user service for PDS account provisioning
+
// V2.0: Create user service with REAL identity resolution using local PLC
+
plcURL := os.Getenv("PLC_DIRECTORY_URL")
+
if plcURL == "" {
+
plcURL = "http://localhost:3002" // Local PLC directory
+
}
userRepo := postgres.NewUserRepository(db)
-
identityResolver := &communityTestIdentityResolver{} // Simple mock for test
-
userService := users.NewUserService(userRepo, identityResolver, pdsURL)
+
identityConfig := identity.DefaultConfig()
+
identityConfig.PLCURL = plcURL // Use local PLC for identity resolution
+
identityResolver := identity.NewResolver(db, identityConfig)
+
_ = users.NewUserService(userRepo, identityResolver, pdsURL) // Keep for potential future use
+
t.Logf("✅ Identity resolver configured with local PLC: %s", plcURL)
-
// V2: Initialize PDS account provisioner
-
provisioner := communities.NewPDSAccountProvisioner(userService, instanceDomain, pdsURL)
+
// V2.0: Initialize PDS account provisioner (simplified - no DID generator needed!)
+
// PDS handles all DID generation and registration automatically
+
provisioner := communities.NewPDSAccountProvisioner(instanceDomain, pdsURL)
-
// Create service and consumer
-
communityService := communities.NewCommunityService(communityRepo, didGen, pdsURL, instanceDID, instanceDomain, provisioner)
+
// Create service (no longer needs didGen directly - provisioner owns it)
+
communityService := communities.NewCommunityService(communityRepo, pdsURL, instanceDID, instanceDomain, provisioner)
if svc, ok := communityService.(interface{ SetPDSAccessToken(string) }); ok {
svc.SetPDSAccessToken(accessToken)
}
+17 -55
tests/integration/community_repo_test.go
···
package integration
import (
-
"Coves/internal/atproto/did"
"Coves/internal/core/communities"
"Coves/internal/db/postgres"
"context"
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("creates community successfully", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
-
// Generate unique handle using timestamp to avoid collisions
+
// Generate unique handle and DID using timestamp to avoid collisions
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
Handle: fmt.Sprintf("!test-gaming-%s@coves.local", uniqueSuffix),
···
})
t.Run("returns error for duplicate DID", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
Handle: fmt.Sprintf("!duplicate-test-%s@coves.local", uniqueSuffix),
···
}
// Try to create again with same DID
-
if _, err = repo.Create(ctx, community); err != communities.ErrCommunityAlreadyExists {
+
if _, err := repo.Create(ctx, community); err != communities.ErrCommunityAlreadyExists {
t.Errorf("Expected ErrCommunityAlreadyExists, got: %v", err)
}
})
···
handle := fmt.Sprintf("!unique-handle-%s@coves.local", uniqueSuffix)
// First community
-
did1, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate first community DID: %v", err)
-
}
+
did1 := generateTestDID(uniqueSuffix + "1")
community1 := &communities.Community{
DID: did1,
Handle: handle,
···
}
// Second community with different DID but same handle
-
did2, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate second community DID: %v", err)
-
}
+
did2 := generateTestDID(uniqueSuffix + "2")
community2 := &communities.Community{
DID: did2,
Handle: handle, // Same handle!
···
UpdatedAt: time.Now(),
}
-
if _, err = repo.Create(ctx, community2); err != communities.ErrHandleTaken {
+
if _, err := repo.Create(ctx, community2); err != communities.ErrHandleTaken {
t.Errorf("Expected ErrHandleTaken, got: %v", err)
}
})
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("retrieves existing community", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
Handle: fmt.Sprintf("!getbyid-test-%s@coves.local", uniqueSuffix),
···
})
t.Run("returns error for non-existent community", func(t *testing.T) {
-
fakeDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate fake DID: %v", err)
-
}
+
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
fakeDID := generateTestDID(uniqueSuffix)
if _, err := repo.GetByDID(ctx, fakeDID); err != communities.ErrCommunityNotFound {
t.Errorf("Expected ErrCommunityNotFound, got: %v", err)
}
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("retrieves community by handle", func(t *testing.T) {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
handle := fmt.Sprintf("!handle-lookup-%s@coves.local", uniqueSuffix)
community := &communities.Community{
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
// Create a community for subscription tests
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
Handle: fmt.Sprintf("!subscription-test-%s@coves.local", uniqueSuffix),
···
}
// Try to subscribe again
-
_, err = repo.Subscribe(ctx, sub)
+
_, err := repo.Subscribe(ctx, sub)
if err != communities.ErrSubscriptionAlreadyExists {
t.Errorf("Expected ErrSubscriptionAlreadyExists, got: %v", err)
}
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("lists communities with pagination", func(t *testing.T) {
// Create multiple communities
baseSuffix := time.Now().UnixNano()
for i := 0; i < 5; i++ {
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
+
uniqueSuffix := fmt.Sprintf("%d%d", baseSuffix, i)
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
Handle: fmt.Sprintf("!list-test-%d-%d@coves.local", baseSuffix, i),
···
t.Run("filters by visibility", func(t *testing.T) {
// Create an unlisted community
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
Handle: fmt.Sprintf("!unlisted-test-%s@coves.local", uniqueSuffix),
···
}()
repo := postgres.NewCommunityRepository(db)
-
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("searches communities by name", func(t *testing.T) {
// Create a community with searchable name
-
communityDID, err := didGen.GenerateCommunityDID()
-
if err != nil {
-
t.Fatalf("Failed to generate community DID: %v", err)
-
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
+
communityDID := generateTestDID(uniqueSuffix)
community := &communities.Community{
DID: communityDID,
Handle: fmt.Sprintf("!golang-search-%s@coves.local", uniqueSuffix),
+8
tests/integration/user_test.go
···
return db
}
+
// generateTestDID generates a unique test DID for integration tests
+
// V2.0: No longer uses DID generator - just creates valid did:plc strings
+
func generateTestDID(suffix string) string {
+
// Use a deterministic base + suffix for reproducible test DIDs
+
// Format matches did:plc but doesn't need PLC registration for unit/repo tests
+
return fmt.Sprintf("did:plc:test%s", suffix)
+
}
+
func TestUserCreationAndRetrieval(t *testing.T) {
db := setupTestDB(t)
defer func() {
+2 -3
tests/unit/community_service_test.go
···
package unit
import (
-
"Coves/internal/atproto/did"
"Coves/internal/core/communities"
"context"
"fmt"
···
defer slowPDS.Close()
_ = newMockCommunityRepo()
-
_ = did.NewGenerator(true, "https://plc.directory")
+
// V2.0: DID generator no longer needed - PDS generates DIDs
// Note: We can't easily test the actual service without mocking more dependencies
// This test verifies the concept - in practice, a 15s operation should NOT timeout
···
CreatedByDID: "did:plc:creator",
HostedByDID: "did:web:coves.social",
PDSEmail: "community-test@communities.coves.social",
-
PDSPasswordHash: "$2a$10$hash",
+
PDSPassword: "cleartext-password-will-be-encrypted", // V2: Cleartext (encrypted by repository)
PDSAccessToken: "test_access_token",
PDSRefreshToken: "test_refresh_token",
PDSURL: "http://localhost:2583",