A community based topic aggregation platform built on atproto

test: update E2E tests for OAuth authentication

- Update test helpers for new OAuth flow
- Adapt aggregator, community, post tests
- Update user journey tests

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

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

+201 -104
+20 -19
tests/integration/aggregator_e2e_test.go
··· 3 3 import ( 4 4 "Coves/internal/api/handlers/aggregator" 5 5 "Coves/internal/api/handlers/post" 6 - "Coves/internal/api/middleware" 7 6 "Coves/internal/atproto/identity" 8 7 "Coves/internal/atproto/jetstream" 9 8 "Coves/internal/core/aggregators" ··· 82 81 getAuthorizationsHandler := aggregator.NewGetAuthorizationsHandler(aggregatorService) 83 82 listForCommunityHandler := aggregator.NewListForCommunityHandler(aggregatorService) 84 83 createPostHandler := post.NewCreateHandler(postService) 85 - authMiddleware := middleware.NewAtProtoAuthMiddleware(nil, true) // Skip JWT verification for testing 86 - defer authMiddleware.Stop() // Clean up DPoP replay cache goroutine 84 + e2eAuth := NewE2EOAuthMiddleware() 87 85 88 86 ctx := context.Background() 89 87 ··· 99 97 // Part 1: Service Declaration via Real PDS 100 98 // ==================================================================================== 101 99 // Store DIDs, tokens, and URIs for use across all test parts 102 - var aggregatorDID, aggregatorToken, aggregatorHandle, communityDID, communityToken, authorizationRkey string 100 + var aggregatorDID, aggregatorToken, aggregatorAPIToken, aggregatorHandle, communityDID, communityToken, authorizationRkey string 103 101 104 102 t.Run("1. Service Declaration - PDS Account → Write Record → Jetstream → AppView DB", func(t *testing.T) { 105 103 t.Log("\n📝 Part 1: Create aggregator account and publish service declaration to PDS...") ··· 118 116 require.NotEmpty(t, aggregatorDID, "Should receive DID") 119 117 120 118 t.Logf("✓ Created aggregator account: %s (%s)", aggregatorHandle, aggregatorDID) 119 + 120 + // Register aggregator user with OAuth middleware for API requests 121 + aggregatorAPIToken = e2eAuth.AddUser(aggregatorDID) 121 122 122 123 // STEP 2: Write service declaration to aggregator's repository on PDS 123 124 configSchema := map[string]interface{}{ ··· 335 336 336 337 req := httptest.NewRequest("POST", "/xrpc/social.coves.community.post.create", bytes.NewReader(reqJSON)) 337 338 req.Header.Set("Content-Type", "application/json") 338 - 339 - // Create JWT for aggregator (not a user) 340 - aggregatorJWT := createSimpleTestJWT(aggregatorDID) 341 - req.Header.Set("Authorization", "DPoP "+aggregatorJWT) 339 + req.Header.Set("Authorization", "Bearer "+aggregatorAPIToken) 342 340 343 341 // Execute request through auth middleware + handler 344 342 rr := httptest.NewRecorder() 345 - handler := authMiddleware.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 343 + handler := e2eAuth.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 346 344 handler.ServeHTTP(rr, req) 347 345 348 346 // STEP 2: Verify post creation succeeded ··· 425 423 426 424 req := httptest.NewRequest("POST", "/xrpc/social.coves.community.post.create", bytes.NewReader(reqJSON)) 427 425 req.Header.Set("Content-Type", "application/json") 428 - req.Header.Set("Authorization", "DPoP "+createSimpleTestJWT(aggregatorDID)) 426 + req.Header.Set("Authorization", "Bearer "+aggregatorAPIToken) 429 427 430 428 rr := httptest.NewRecorder() 431 - handler := authMiddleware.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 429 + handler := e2eAuth.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 432 430 handler.ServeHTTP(rr, req) 433 431 434 432 require.Equal(t, http.StatusOK, rr.Code, "Post %d should succeed", i) ··· 447 445 448 446 req := httptest.NewRequest("POST", "/xrpc/social.coves.community.post.create", bytes.NewReader(reqJSON)) 449 447 req.Header.Set("Content-Type", "application/json") 450 - req.Header.Set("Authorization", "DPoP "+createSimpleTestJWT(aggregatorDID)) 448 + req.Header.Set("Authorization", "Bearer "+aggregatorAPIToken) 451 449 452 450 rr := httptest.NewRecorder() 453 - handler := authMiddleware.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 451 + handler := e2eAuth.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 454 452 handler.ServeHTTP(rr, req) 455 453 456 454 require.Equal(t, http.StatusOK, rr.Code, "10th post should succeed (at limit)") ··· 468 466 469 467 req = httptest.NewRequest("POST", "/xrpc/social.coves.community.post.create", bytes.NewReader(reqJSON)) 470 468 req.Header.Set("Content-Type", "application/json") 471 - req.Header.Set("Authorization", "DPoP "+createSimpleTestJWT(aggregatorDID)) 469 + req.Header.Set("Authorization", "Bearer "+aggregatorAPIToken) 472 470 473 471 rr = httptest.NewRecorder() 474 - handler = authMiddleware.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 472 + handler = e2eAuth.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 475 473 handler.ServeHTTP(rr, req) 476 474 477 475 // Should be rate limited ··· 649 647 err := aggregatorConsumer.HandleEvent(ctx, &unAuthAggEvent) 650 648 require.NoError(t, err) 651 649 650 + // Register unauthorized aggregator with OAuth middleware 651 + unauthorizedAPIToken := e2eAuth.AddUser(unauthorizedAggDID) 652 + 652 653 // Try to create post without authorization 653 654 reqBody := map[string]interface{}{ 654 655 "community": communityDID, ··· 660 661 661 662 req := httptest.NewRequest("POST", "/xrpc/social.coves.community.post.create", bytes.NewReader(reqJSON)) 662 663 req.Header.Set("Content-Type", "application/json") 663 - req.Header.Set("Authorization", "DPoP "+createSimpleTestJWT(unauthorizedAggDID)) 664 + req.Header.Set("Authorization", "Bearer "+unauthorizedAPIToken) 664 665 665 666 rr := httptest.NewRecorder() 666 - handler := authMiddleware.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 667 + handler := e2eAuth.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 667 668 handler.ServeHTTP(rr, req) 668 669 669 670 // Should be forbidden ··· 784 785 785 786 req := httptest.NewRequest("POST", "/xrpc/social.coves.community.post.create", bytes.NewReader(reqJSON)) 786 787 req.Header.Set("Content-Type", "application/json") 787 - req.Header.Set("Authorization", "DPoP "+createSimpleTestJWT(aggregatorDID)) 788 + req.Header.Set("Authorization", "Bearer "+aggregatorAPIToken) 788 789 789 790 rr := httptest.NewRecorder() 790 - handler := authMiddleware.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 791 + handler := e2eAuth.RequireAuth(http.HandlerFunc(createPostHandler.HandleCreate)) 791 792 handler.ServeHTTP(rr, req) 792 793 793 794 assert.Equal(t, http.StatusForbidden, rr.Code, "Should reject post from disabled aggregator")
+16 -20
tests/integration/community_e2e_test.go
··· 1 1 package integration 2 2 3 3 import ( 4 - "Coves/internal/api/middleware" 5 4 "Coves/internal/api/routes" 6 5 "Coves/internal/atproto/identity" 7 6 "Coves/internal/atproto/jetstream" ··· 108 107 109 108 t.Logf("✅ Authenticated - Instance DID: %s", instanceDID) 110 109 111 - // Initialize auth middleware with skipVerify=true 112 - // IMPORTANT: PDS password authentication returns Bearer tokens (not DPoP-bound tokens). 113 - // E2E tests use these Bearer tokens with the DPoP scheme header, which only works 114 - // because skipVerify=true bypasses signature and DPoP binding verification. 115 - // In production, skipVerify=false requires proper DPoP-bound tokens from OAuth flow. 116 - authMiddleware := middleware.NewAtProtoAuthMiddleware(nil, true) 117 - defer authMiddleware.Stop() // Clean up DPoP replay cache goroutine 110 + // Initialize OAuth auth middleware for E2E testing 111 + e2eAuth := NewE2EOAuthMiddleware() 112 + // Register the instance user for OAuth authentication 113 + token := e2eAuth.AddUser(instanceDID) 118 114 119 115 // V2.0: Extract instance domain for community provisioning 120 116 var instanceDomain string ··· 152 148 153 149 // Setup HTTP server with XRPC routes 154 150 r := chi.NewRouter() 155 - routes.RegisterCommunityRoutes(r, communityService, authMiddleware, nil) // nil = allow all community creators 151 + routes.RegisterCommunityRoutes(r, communityService, e2eAuth.OAuthAuthMiddleware, nil) // nil = allow all community creators 156 152 httpServer := httptest.NewServer(r) 157 153 defer httpServer.Close() 158 154 ··· 387 383 t.Fatalf("Failed to create request: %v", err) 388 384 } 389 385 req.Header.Set("Content-Type", "application/json") 390 - // Use real PDS access token for E2E authentication 391 - req.Header.Set("Authorization", "DPoP "+accessToken) 386 + // Use OAuth token for Coves API authentication 387 + req.Header.Set("Authorization", "Bearer "+token) 392 388 393 389 resp, err := http.DefaultClient.Do(req) 394 390 if err != nil { ··· 773 769 t.Fatalf("Failed to create request: %v", err) 774 770 } 775 771 req.Header.Set("Content-Type", "application/json") 776 - // Use real PDS access token for E2E authentication 777 - req.Header.Set("Authorization", "DPoP "+accessToken) 772 + // Use OAuth token for Coves API authentication 773 + req.Header.Set("Authorization", "Bearer "+token) 778 774 779 775 resp, err := http.DefaultClient.Do(req) 780 776 if err != nil { ··· 1008 1004 t.Fatalf("Failed to create request: %v", err) 1009 1005 } 1010 1006 req.Header.Set("Content-Type", "application/json") 1011 - // Use real PDS access token for E2E authentication 1012 - req.Header.Set("Authorization", "DPoP "+accessToken) 1007 + // Use OAuth token for Coves API authentication 1008 + req.Header.Set("Authorization", "Bearer "+token) 1013 1009 1014 1010 resp, err := http.DefaultClient.Do(req) 1015 1011 if err != nil { ··· 1141 1137 t.Fatalf("Failed to create block request: %v", err) 1142 1138 } 1143 1139 req.Header.Set("Content-Type", "application/json") 1144 - req.Header.Set("Authorization", "DPoP "+accessToken) 1140 + req.Header.Set("Authorization", "Bearer "+token) 1145 1141 1146 1142 resp, err := http.DefaultClient.Do(req) 1147 1143 if err != nil { ··· 1261 1257 t.Fatalf("Failed to create block request: %v", err) 1262 1258 } 1263 1259 blockHttpReq.Header.Set("Content-Type", "application/json") 1264 - blockHttpReq.Header.Set("Authorization", "DPoP "+accessToken) 1260 + blockHttpReq.Header.Set("Authorization", "Bearer "+token) 1265 1261 1266 1262 blockResp, err := http.DefaultClient.Do(blockHttpReq) 1267 1263 if err != nil { ··· 1321 1317 t.Fatalf("Failed to create unblock request: %v", err) 1322 1318 } 1323 1319 req.Header.Set("Content-Type", "application/json") 1324 - req.Header.Set("Authorization", "DPoP "+accessToken) 1320 + req.Header.Set("Authorization", "Bearer "+token) 1325 1321 1326 1322 resp, err := http.DefaultClient.Do(req) 1327 1323 if err != nil { ··· 1477 1473 t.Fatalf("Failed to create request: %v", err) 1478 1474 } 1479 1475 req.Header.Set("Content-Type", "application/json") 1480 - // Use real PDS access token for E2E authentication 1481 - req.Header.Set("Authorization", "DPoP "+accessToken) 1476 + // Use OAuth token for Coves API authentication 1477 + req.Header.Set("Authorization", "Bearer "+token) 1482 1478 1483 1479 resp, err := http.DefaultClient.Do(req) 1484 1480 if err != nil {
+136 -37
tests/integration/helpers.go
··· 1 1 package integration 2 2 3 3 import ( 4 - "Coves/internal/atproto/auth" 4 + "Coves/internal/api/middleware" 5 + "Coves/internal/atproto/oauth" 5 6 "Coves/internal/core/users" 6 7 "bytes" 7 8 "context" 8 9 "database/sql" 9 - "encoding/base64" 10 10 "encoding/json" 11 11 "fmt" 12 12 "io" ··· 16 16 "testing" 17 17 "time" 18 18 19 - "github.com/golang-jwt/jwt/v5" 19 + oauthlib "github.com/bluesky-social/indigo/atproto/auth/oauth" 20 + "github.com/bluesky-social/indigo/atproto/syntax" 20 21 ) 21 22 22 23 // getTestPDSURL returns the PDS URL for testing from env var or default ··· 113 114 } 114 115 115 116 return sessionResp.AccessJwt, sessionResp.DID, nil 116 - } 117 - 118 - // createSimpleTestJWT creates a minimal JWT for testing (Phase 1 - no signature) 119 - // In production, this would be a real OAuth token from PDS with proper signatures 120 - func createSimpleTestJWT(userDID string) string { 121 - // Create minimal JWT claims using RegisteredClaims 122 - // Use userDID as issuer since we don't have a proper PDS DID for testing 123 - claims := auth.Claims{ 124 - RegisteredClaims: jwt.RegisteredClaims{ 125 - Subject: userDID, 126 - Issuer: userDID, // Use DID as issuer for testing (valid per atProto) 127 - Audience: jwt.ClaimStrings{getTestInstanceDID()}, 128 - IssuedAt: jwt.NewNumericDate(time.Now()), 129 - ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)), 130 - }, 131 - Scope: "com.atproto.access", 132 - } 133 - 134 - // For Phase 1 testing, we create an unsigned JWT 135 - // The middleware is configured with skipVerify=true for testing 136 - header := map[string]interface{}{ 137 - "alg": "none", 138 - "typ": "JWT", 139 - } 140 - 141 - headerJSON, _ := json.Marshal(header) 142 - claimsJSON, _ := json.Marshal(claims) 143 - 144 - // Base64url encode (without padding) 145 - headerB64 := base64.RawURLEncoding.EncodeToString(headerJSON) 146 - claimsB64 := base64.RawURLEncoding.EncodeToString(claimsJSON) 147 - 148 - // For "alg: none", signature is empty 149 - return headerB64 + "." + claimsB64 + "." 150 117 } 151 118 152 119 // generateTID generates a simple timestamp-based identifier for testing ··· 310 277 311 278 return uri 312 279 } 280 + 281 + // MockSessionUnsealer is a mock implementation of SessionUnsealer for testing 282 + // It returns predefined sessions based on token value 283 + type MockSessionUnsealer struct { 284 + sessions map[string]*oauth.SealedSession 285 + } 286 + 287 + // NewMockSessionUnsealer creates a new mock unsealer 288 + func NewMockSessionUnsealer() *MockSessionUnsealer { 289 + return &MockSessionUnsealer{ 290 + sessions: make(map[string]*oauth.SealedSession), 291 + } 292 + } 293 + 294 + // AddSession adds a token -> session mapping 295 + func (m *MockSessionUnsealer) AddSession(token, did, sessionID string) { 296 + m.sessions[token] = &oauth.SealedSession{ 297 + DID: did, 298 + SessionID: sessionID, 299 + ExpiresAt: time.Now().Add(1 * time.Hour).Unix(), 300 + } 301 + } 302 + 303 + // UnsealSession returns the predefined session for a token 304 + func (m *MockSessionUnsealer) UnsealSession(token string) (*oauth.SealedSession, error) { 305 + if sess, ok := m.sessions[token]; ok { 306 + return sess, nil 307 + } 308 + return nil, fmt.Errorf("unknown token") 309 + } 310 + 311 + // MockOAuthStore is a mock implementation of ClientAuthStore for testing 312 + type MockOAuthStore struct { 313 + sessions map[string]*oauthlib.ClientSessionData 314 + } 315 + 316 + // NewMockOAuthStore creates a new mock OAuth store 317 + func NewMockOAuthStore() *MockOAuthStore { 318 + return &MockOAuthStore{ 319 + sessions: make(map[string]*oauthlib.ClientSessionData), 320 + } 321 + } 322 + 323 + // AddSession adds a session to the store 324 + func (m *MockOAuthStore) AddSession(did, sessionID, accessToken string) { 325 + key := did + ":" + sessionID 326 + parsedDID, _ := syntax.ParseDID(did) 327 + m.sessions[key] = &oauthlib.ClientSessionData{ 328 + AccountDID: parsedDID, 329 + SessionID: sessionID, 330 + AccessToken: accessToken, 331 + } 332 + } 333 + 334 + // GetSession implements ClientAuthStore 335 + func (m *MockOAuthStore) GetSession(ctx context.Context, did syntax.DID, sessionID string) (*oauthlib.ClientSessionData, error) { 336 + key := did.String() + ":" + sessionID 337 + if sess, ok := m.sessions[key]; ok { 338 + return sess, nil 339 + } 340 + return nil, fmt.Errorf("session not found") 341 + } 342 + 343 + // SaveSession implements ClientAuthStore 344 + func (m *MockOAuthStore) SaveSession(ctx context.Context, sess oauthlib.ClientSessionData) error { 345 + key := sess.AccountDID.String() + ":" + sess.SessionID 346 + m.sessions[key] = &sess 347 + return nil 348 + } 349 + 350 + // DeleteSession implements ClientAuthStore 351 + func (m *MockOAuthStore) DeleteSession(ctx context.Context, did syntax.DID, sessionID string) error { 352 + key := did.String() + ":" + sessionID 353 + delete(m.sessions, key) 354 + return nil 355 + } 356 + 357 + // GetAuthRequestInfo implements ClientAuthStore 358 + func (m *MockOAuthStore) GetAuthRequestInfo(ctx context.Context, state string) (*oauthlib.AuthRequestData, error) { 359 + return nil, fmt.Errorf("not implemented in mock") 360 + } 361 + 362 + // SaveAuthRequestInfo implements ClientAuthStore 363 + func (m *MockOAuthStore) SaveAuthRequestInfo(ctx context.Context, info oauthlib.AuthRequestData) error { 364 + return nil 365 + } 366 + 367 + // DeleteAuthRequestInfo implements ClientAuthStore 368 + func (m *MockOAuthStore) DeleteAuthRequestInfo(ctx context.Context, state string) error { 369 + return nil 370 + } 371 + 372 + // CreateTestOAuthMiddleware creates an OAuth middleware with mock implementations for testing 373 + // The returned middleware accepts a test token that maps to the specified userDID 374 + func CreateTestOAuthMiddleware(userDID string) (*middleware.OAuthAuthMiddleware, string) { 375 + unsealer := NewMockSessionUnsealer() 376 + store := NewMockOAuthStore() 377 + 378 + testToken := "test-token-" + userDID 379 + sessionID := "test-session-123" 380 + 381 + // Add the test session 382 + unsealer.AddSession(testToken, userDID, sessionID) 383 + store.AddSession(userDID, sessionID, "test-access-token") 384 + 385 + authMiddleware := middleware.NewOAuthAuthMiddleware(unsealer, store) 386 + return authMiddleware, testToken 387 + } 388 + 389 + // E2EOAuthMiddleware wraps OAuth middleware for E2E testing with multiple users 390 + type E2EOAuthMiddleware struct { 391 + *middleware.OAuthAuthMiddleware 392 + unsealer *MockSessionUnsealer 393 + store *MockOAuthStore 394 + } 395 + 396 + // NewE2EOAuthMiddleware creates an OAuth middleware for E2E testing 397 + func NewE2EOAuthMiddleware() *E2EOAuthMiddleware { 398 + unsealer := NewMockSessionUnsealer() 399 + store := NewMockOAuthStore() 400 + m := middleware.NewOAuthAuthMiddleware(unsealer, store) 401 + return &E2EOAuthMiddleware{m, unsealer, store} 402 + } 403 + 404 + // AddUser registers a user DID and returns the token to use in Authorization header 405 + func (e *E2EOAuthMiddleware) AddUser(did string) string { 406 + token := "test-token-" + did 407 + sessionID := "session-" + did 408 + e.unsealer.AddSession(token, did, sessionID) 409 + e.store.AddSession(did, sessionID, "access-token-"+did) 410 + return token 411 + }
+7 -9
tests/integration/post_e2e_test.go
··· 2 2 3 3 import ( 4 4 "Coves/internal/api/handlers/post" 5 - "Coves/internal/api/middleware" 6 5 "Coves/internal/atproto/identity" 7 6 "Coves/internal/atproto/jetstream" 8 7 "Coves/internal/core/communities" ··· 405 404 406 405 postService := posts.NewPostService(postRepo, communityService, nil, nil, nil, pdsURL) // nil aggregatorService, blobService, unfurlService for user-only tests 407 406 408 - // Setup auth middleware (skip JWT verification for testing) 409 - authMiddleware := middleware.NewAtProtoAuthMiddleware(nil, true) 410 - defer authMiddleware.Stop() // Clean up DPoP replay cache goroutine 407 + // Setup OAuth auth middleware for E2E testing 408 + e2eAuth := NewE2EOAuthMiddleware() 411 409 412 410 // Setup HTTP handler 413 411 createHandler := post.NewCreateHandler(postService) ··· 476 474 req := httptest.NewRequest("POST", "/xrpc/social.coves.community.post.create", bytes.NewReader(reqJSON)) 477 475 req.Header.Set("Content-Type", "application/json") 478 476 479 - // Create a simple JWT for testing (Phase 1: no signature verification) 480 - // In production, this would be a real OAuth token from PDS 481 - testJWT := createSimpleTestJWT(author.DID) 482 - req.Header.Set("Authorization", "DPoP "+testJWT) 477 + // Register the author user with OAuth middleware and get test token 478 + // For Coves API handlers, use Bearer scheme with OAuth middleware 479 + token := e2eAuth.AddUser(author.DID) 480 + req.Header.Set("Authorization", "Bearer "+token) 483 481 484 482 // Execute request through auth middleware + handler 485 483 rr := httptest.NewRecorder() 486 - handler := authMiddleware.RequireAuth(http.HandlerFunc(createHandler.HandleCreate)) 484 + handler := e2eAuth.RequireAuth(http.HandlerFunc(createHandler.HandleCreate)) 487 485 handler.ServeHTTP(rr, req) 488 486 489 487 // Check response
+22 -19
tests/integration/user_journey_e2e_test.go
··· 1 1 package integration 2 2 3 3 import ( 4 - "Coves/internal/api/middleware" 5 4 "Coves/internal/api/routes" 6 5 "Coves/internal/atproto/identity" 7 6 "Coves/internal/atproto/jetstream" ··· 22 21 "testing" 23 22 "time" 24 23 25 - timelineCore "Coves/internal/core/timeline" 26 - 27 24 "github.com/go-chi/chi/v5" 28 25 "github.com/gorilla/websocket" 29 26 _ "github.com/lib/pq" 30 27 "github.com/pressly/goose/v3" 31 28 "github.com/stretchr/testify/assert" 32 29 "github.com/stretchr/testify/require" 30 + 31 + timelineCore "Coves/internal/core/timeline" 33 32 ) 34 33 35 34 // TestFullUserJourney_E2E tests the complete user experience from signup to interaction: ··· 139 138 commentConsumer := jetstream.NewCommentEventConsumer(commentRepo, db) 140 139 voteConsumer := jetstream.NewVoteEventConsumer(voteRepo, userService, db) 141 140 142 - // Setup HTTP server with all routes 143 - // IMPORTANT: skipVerify=true because PDS password auth returns Bearer tokens (not DPoP-bound). 144 - // E2E tests use Bearer tokens with DPoP scheme header, which only works with skipVerify=true. 145 - // In production, skipVerify=false requires proper DPoP-bound tokens from OAuth flow. 146 - authMiddleware := middleware.NewAtProtoAuthMiddleware(nil, true) 147 - defer authMiddleware.Stop() // Clean up DPoP replay cache goroutine 141 + // Setup HTTP server with all routes using OAuth middleware 142 + e2eAuth := NewE2EOAuthMiddleware() 148 143 r := chi.NewRouter() 149 - routes.RegisterCommunityRoutes(r, communityService, authMiddleware, nil) // nil = allow all community creators 150 - routes.RegisterPostRoutes(r, postService, authMiddleware) 151 - routes.RegisterTimelineRoutes(r, timelineService, authMiddleware) 144 + routes.RegisterCommunityRoutes(r, communityService, e2eAuth.OAuthAuthMiddleware, nil) // nil = allow all community creators 145 + routes.RegisterPostRoutes(r, postService, e2eAuth.OAuthAuthMiddleware) 146 + routes.RegisterTimelineRoutes(r, timelineService, e2eAuth.OAuthAuthMiddleware) 152 147 httpServer := httptest.NewServer(r) 153 148 defer httpServer.Close() 154 149 ··· 181 176 var ( 182 177 userAHandle string 183 178 userADID string 184 - userAToken string 179 + userAToken string // PDS access token for direct PDS requests 180 + userAAPIToken string // Coves API token for Coves API requests 185 181 userBHandle string 186 182 userBDID string 187 - userBToken string 183 + userBToken string // PDS access token for direct PDS requests 184 + userBAPIToken string // Coves API token for Coves API requests 188 185 communityDID string 189 186 communityHandle string 190 187 postURI string ··· 217 214 userA := createTestUser(t, db, userAHandle, userADID) 218 215 require.NotNil(t, userA) 219 216 217 + // Register user with OAuth middleware for Coves API requests 218 + userAAPIToken = e2eAuth.AddUser(userADID) 219 + 220 220 t.Logf("✅ User A indexed in AppView") 221 221 }) 222 222 ··· 244 244 httpServer.URL+"/xrpc/social.coves.community.create", 245 245 bytes.NewBuffer(reqBody)) 246 246 req.Header.Set("Content-Type", "application/json") 247 - req.Header.Set("Authorization", "DPoP "+userAToken) 247 + req.Header.Set("Authorization", "Bearer "+userAAPIToken) 248 248 249 249 resp, err := http.DefaultClient.Do(req) 250 250 require.NoError(t, err) ··· 328 328 httpServer.URL+"/xrpc/social.coves.community.post.create", 329 329 bytes.NewBuffer(reqBody)) 330 330 req.Header.Set("Content-Type", "application/json") 331 - req.Header.Set("Authorization", "DPoP "+userAToken) 331 + req.Header.Set("Authorization", "Bearer "+userAAPIToken) 332 332 333 333 resp, err := http.DefaultClient.Do(req) 334 334 require.NoError(t, err) ··· 413 413 userB := createTestUser(t, db, userBHandle, userBDID) 414 414 require.NotNil(t, userB) 415 415 416 + // Register user with OAuth middleware for Coves API requests 417 + userBAPIToken = e2eAuth.AddUser(userBDID) 418 + 416 419 t.Logf("✅ User B indexed in AppView") 417 420 }) 418 421 ··· 437 440 httpServer.URL+"/xrpc/social.coves.community.subscribe", 438 441 bytes.NewBuffer(reqBody)) 439 442 req.Header.Set("Content-Type", "application/json") 440 - req.Header.Set("Authorization", "DPoP "+userBToken) 443 + req.Header.Set("Authorization", "Bearer "+userBAPIToken) 441 444 442 445 resp, err := http.DefaultClient.Do(req) 443 446 require.NoError(t, err) ··· 669 672 t.Run("9. User B - Verify Timeline Feed Shows Subscribed Community Posts", func(t *testing.T) { 670 673 t.Log("\n📰 Part 9: User B checks timeline feed...") 671 674 672 - // Use HTTP client to properly go through auth middleware with DPoP token 675 + // Use HTTP client to properly go through auth middleware with Bearer token 673 676 req, _ := http.NewRequest(http.MethodGet, 674 677 httpServer.URL+"/xrpc/social.coves.feed.getTimeline?sort=new&limit=10", nil) 675 - req.Header.Set("Authorization", "DPoP "+userBToken) 678 + req.Header.Set("Authorization", "Bearer "+userBAPIToken) 676 679 677 680 resp, err := http.DefaultClient.Do(req) 678 681 require.NoError(t, err)