A community based topic aggregation platform built on atproto

test: fix all linter errors in test files

Comprehensive error handling improvements across all test files:

Error handling patterns applied:
- defer db.Close() → defer func() { if err := db.Close(); err != nil { t.Logf(...) } }()
- defer resp.Body.Close() → defer func() { _ = resp.Body.Close() }()
- defer conn.Close() → defer func() { _ = conn.Close() }()
- body, _ := io.ReadAll(...) → proper error checking with t.Fatalf()
- json.Marshal/Unmarshal → proper error checking with descriptive variable names
- os.Setenv/Unsetenv → proper error checking in tests
- didGen.GenerateCommunityDID() → proper error checking with t.Fatalf()

Test data fixes:
- Fix community profile test: add required fields (handle, createdBy, hostedBy, visibility)
- Ensure all lexicon validation tests pass with proper schema data

Files updated (12 test files):
- tests/lexicon_validation_test.go
- tests/unit/community_service_test.go
- tests/e2e/user_signup_test.go
- tests/integration/community_consumer_test.go
- tests/integration/community_credentials_test.go
- tests/integration/community_e2e_test.go
- tests/integration/community_repo_test.go
- tests/integration/community_v2_validation_test.go
- tests/integration/identity_resolution_test.go
- tests/integration/jetstream_consumer_test.go
- tests/integration/oauth_test.go
- tests/integration/user_test.go

All test files now pass golangci-lint with proper error handling.
All tests continue to pass with 100% success rate.

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

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

+34 -25
tests/e2e/user_signup_test.go
···
package e2e
import (
+
"Coves/internal/atproto/identity"
+
"Coves/internal/atproto/jetstream"
+
"Coves/internal/core/users"
+
"Coves/internal/db/postgres"
"bytes"
"context"
"database/sql"
···
"testing"
"time"
-
"Coves/internal/atproto/identity"
-
"Coves/internal/atproto/jetstream"
-
"Coves/internal/core/users"
-
"Coves/internal/db/postgres"
_ "github.com/lib/pq"
"github.com/pressly/goose/v3"
)
···
// - Test database on localhost:5434
//
// Run with:
-
// make e2e-up # Start infrastructure
-
// go run ./cmd/server & # Start AppView
-
// go test ./tests/integration -run TestE2E_UserSignup -v
+
//
+
// make e2e-up # Start infrastructure
+
// go run ./cmd/server & # Start AppView
+
// go test ./tests/integration -run TestE2E_UserSignup -v
func TestE2E_UserSignup(t *testing.T) {
if testing.Short() {
t.Skip("Skipping E2E test in short mode")
···
}
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
// Set up services
userRepo := postgres.NewUserRepository(db)
···
if err != nil {
return "", fmt.Errorf("failed to create invite code: %w", err)
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
var errorResp map[string]interface{}
-
json.NewDecoder(resp.Body).Decode(&errorResp)
+
if err := json.NewDecoder(resp.Body).Decode(&errorResp); err != nil {
+
return "", fmt.Errorf("PDS admin API returned status %d (failed to decode error: %w)", resp.StatusCode, err)
+
}
return "", fmt.Errorf("PDS admin API returned status %d: %v", resp.StatusCode, errorResp)
}
···
if err != nil {
return "", fmt.Errorf("failed to call signup endpoint: %w", err)
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
var errorResp map[string]interface{}
-
json.NewDecoder(resp.Body).Decode(&errorResp)
+
if err := json.NewDecoder(resp.Body).Decode(&errorResp); err != nil {
+
return "", fmt.Errorf("signup endpoint returned status %d (failed to decode error: %w)", resp.StatusCode, err)
+
}
return "", fmt.Errorf("signup endpoint returned status %d: %v", resp.StatusCode, errorResp)
}
var result struct {
-
DID string `json:"did"`
-
Handle string `json:"handle"`
-
AccessJwt string `json:"accessJwt"`
-
RefreshJwt string `json:"refreshJwt"`
+
DID string `json:"did"`
+
Handle string `json:"handle"`
+
AccessJwt string `json:"accessJwt"`
+
RefreshJwt string `json:"refreshJwt"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
···
t.Logf("PDS not available: %v", err)
return false
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
return resp.StatusCode == http.StatusOK
}
···
t.Logf("Jetstream not available: %v", err)
return false
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
return resp.StatusCode == http.StatusOK
}
···
t.Logf("AppView not available: %v", err)
return false
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
return resp.StatusCode == http.StatusOK
}
···
t.Fatalf("Failed to connect to test database: %v", err)
}
-
if err := db.Ping(); err != nil {
-
t.Fatalf("Failed to ping test database: %v", err)
+
if pingErr := db.Ping(); pingErr != nil {
+
t.Fatalf("Failed to ping test database: %v", pingErr)
}
-
if err := goose.SetDialect("postgres"); err != nil {
-
t.Fatalf("Failed to set goose dialect: %v", err)
+
if dialectErr := goose.SetDialect("postgres"); dialectErr != nil {
+
t.Fatalf("Failed to set goose dialect: %v", dialectErr)
}
-
if err := goose.Up(db, "../../internal/db/migrations"); err != nil {
-
t.Fatalf("Failed to run migrations: %v", err)
+
if migrateErr := goose.Up(db, "../../internal/db/migrations"); migrateErr != nil {
+
t.Fatalf("Failed to run migrations: %v", migrateErr)
}
// Clean up any existing test data
+43 -28
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"
"context"
"fmt"
"testing"
"time"
-
-
"Coves/internal/atproto/did"
-
"Coves/internal/atproto/jetstream"
-
"Coves/internal/core/communities"
-
"Coves/internal/db/postgres"
)
func TestCommunityConsumer_HandleCommunityProfile(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
consumer := jetstream.NewCommunityEventConsumer(repo)
···
ctx := context.Background()
t.Run("creates community from firehose event", func(t *testing.T) {
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
// Simulate a Jetstream commit event
···
}
// Handle the event
-
err := consumer.HandleEvent(ctx, event)
-
if err != nil {
+
if err := consumer.HandleEvent(ctx, event); err != nil {
t.Fatalf("Failed to handle event: %v", err)
}
···
})
t.Run("updates existing community", func(t *testing.T) {
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
handle := fmt.Sprintf("!update-test-%s@coves.local", uniqueSuffix)
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, initialCommunity)
-
if err != nil {
+
if _, err := repo.Create(ctx, initialCommunity); err != nil {
t.Fatalf("Failed to create initial community: %v", err)
}
···
}
// Handle the update
-
err = consumer.HandleEvent(ctx, updateEvent)
-
if err != nil {
+
if err := consumer.HandleEvent(ctx, updateEvent); err != nil {
t.Fatalf("Failed to handle update event: %v", err)
}
···
})
t.Run("deletes community", func(t *testing.T) {
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
// Create community to delete
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create community: %v", err)
}
···
}
// Handle the delete
-
err = consumer.HandleEvent(ctx, deleteEvent)
-
if err != nil {
+
if err := consumer.HandleEvent(ctx, deleteEvent); err != nil {
t.Fatalf("Failed to handle delete event: %v", err)
}
// Verify community was deleted
-
_, err = repo.GetByDID(ctx, communityDID)
-
if err != communities.ErrCommunityNotFound {
+
if _, err := repo.GetByDID(ctx, communityDID); err != communities.ErrCommunityNotFound {
t.Errorf("Expected ErrCommunityNotFound, got: %v", err)
}
})
···
func TestCommunityConsumer_HandleSubscription(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
consumer := jetstream.NewCommunityEventConsumer(repo)
···
t.Run("creates subscription from event", func(t *testing.T) {
// Create a community first
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create community: %v", err)
}
···
}
// Handle the subscription
-
err = consumer.HandleEvent(ctx, subEvent)
-
if err != nil {
+
if err := consumer.HandleEvent(ctx, subEvent); err != nil {
t.Fatalf("Failed to handle subscription event: %v", err)
}
···
func TestCommunityConsumer_IgnoresNonCommunityEvents(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
consumer := jetstream.NewCommunityEventConsumer(repo)
+41 -18
tests/integration/community_credentials_test.go
···
package integration
import (
+
"Coves/internal/atproto/did"
+
"Coves/internal/core/communities"
+
"Coves/internal/db/postgres"
"context"
"fmt"
"testing"
"time"
-
-
"Coves/internal/atproto/did"
-
"Coves/internal/core/communities"
-
"Coves/internal/db/postgres"
)
// TestCommunityRepository_CredentialPersistence tests that PDS credentials are properly persisted
func TestCommunityRepository_CredentialPersistence(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
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, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
···
})
t.Run("handles empty credentials gracefully", func(t *testing.T) {
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
// Community without PDS credentials (e.g., from Jetstream consumer)
···
// TestCommunityRepository_EncryptedCredentials tests encryption at rest
func TestCommunityRepository_EncryptedCredentials(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
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, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
accessToken := "sensitive_access_token_xyz123"
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create community: %v", err)
}
···
FROM communities
WHERE did = $1
`
-
err = db.QueryRowContext(ctx, query, communityDID).Scan(&encryptedAccess, &encryptedRefresh)
-
if err != nil {
+
if err := db.QueryRowContext(ctx, query, communityDID).Scan(&encryptedAccess, &encryptedRefresh); err != nil {
t.Fatalf("Failed to query encrypted data: %v", err)
}
···
})
t.Run("encryption handles special characters", func(t *testing.T) {
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
// Token with special characters
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create community with special chars: %v", err)
}
···
// TestCommunityRepository_V2OwnershipModel tests that communities are self-owned
func TestCommunityRepository_V2OwnershipModel(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
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, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
+99 -44
tests/integration/community_e2e_test.go
···
package integration
import (
+
"Coves/internal/api/routes"
+
"Coves/internal/atproto/did"
+
"Coves/internal/atproto/identity"
+
"Coves/internal/atproto/jetstream"
+
"Coves/internal/core/communities"
+
"Coves/internal/core/users"
+
"Coves/internal/db/postgres"
"bytes"
"context"
"database/sql"
···
"github.com/gorilla/websocket"
_ "github.com/lib/pq"
"github.com/pressly/goose/v3"
-
-
"Coves/internal/api/routes"
-
"Coves/internal/atproto/did"
-
"Coves/internal/atproto/identity"
-
"Coves/internal/atproto/jetstream"
-
"Coves/internal/core/communities"
-
"Coves/internal/core/users"
-
"Coves/internal/db/postgres"
)
// TestCommunity_E2E is a TRUE end-to-end test covering the complete flow:
···
if err != nil {
t.Fatalf("Failed to connect to test database: %v", err)
}
-
defer db.Close()
+
defer func() {
+
if closeErr := db.Close(); closeErr != nil {
+
t.Logf("Failed to close database: %v", closeErr)
+
}
+
}()
// Run migrations
-
if err := goose.SetDialect("postgres"); err != nil {
-
t.Fatalf("Failed to set goose dialect: %v", err)
+
if dialectErr := goose.SetDialect("postgres"); dialectErr != nil {
+
t.Fatalf("Failed to set goose dialect: %v", dialectErr)
}
-
if err := goose.Up(db, "../../internal/db/migrations"); err != nil {
-
t.Fatalf("Failed to run migrations: %v", err)
+
if migrateErr := goose.Up(db, "../../internal/db/migrations"); migrateErr != nil {
+
t.Fatalf("Failed to run migrations: %v", migrateErr)
}
// Check if PDS is running
···
if err != nil {
t.Skipf("PDS not running at %s: %v", pdsURL, err)
}
-
healthResp.Body.Close()
+
func() {
+
if closeErr := healthResp.Body.Close(); closeErr != nil {
+
t.Logf("Failed to close health response: %v", closeErr)
+
}
+
}()
// Setup dependencies
communityRepo := postgres.NewCommunityRepository(db)
···
if err != nil {
t.Fatalf("Failed to query PDS: %v", err)
}
-
defer pdsResp.Body.Close()
+
defer func() { _ = pdsResp.Body.Close() }()
if pdsResp.StatusCode != http.StatusOK {
-
body, _ := io.ReadAll(pdsResp.Body)
+
body, readErr := io.ReadAll(pdsResp.Body)
+
if readErr != nil {
+
t.Fatalf("PDS returned status %d (failed to read body: %v)", pdsResp.StatusCode, readErr)
+
}
t.Fatalf("PDS returned status %d: %s", pdsResp.StatusCode, string(body))
}
var pdsRecord struct {
+
Value map[string]interface{} `json:"value"`
URI string `json:"uri"`
CID string `json:"cid"`
-
Value map[string]interface{} `json:"value"`
}
if err := json.NewDecoder(pdsResp.Body).Decode(&pdsRecord); err != nil {
···
t.Logf(" CID: %s", pdsRecord.CID)
// Print full record for inspection
-
recordJSON, _ := json.MarshalIndent(pdsRecord.Value, " ", " ")
-
t.Logf(" Record value:\n %s", string(recordJSON))
+
recordJSON, marshalErr := json.MarshalIndent(pdsRecord.Value, " ", " ")
+
if marshalErr != nil {
+
t.Logf(" Failed to marshal record: %v", marshalErr)
+
} else {
+
t.Logf(" Record value:\n %s", string(recordJSON))
+
}
// V2: DID is NOT in the record - it's in the repository URI
// The record should have handle, name, etc. but no 'did' field
···
// Part 3: XRPC HTTP Endpoints
// ====================================================================================
t.Run("3. XRPC HTTP Endpoints", func(t *testing.T) {
-
t.Run("Create via XRPC endpoint", func(t *testing.T) {
// Use Unix timestamp (seconds) instead of UnixNano to keep handle short
createReq := map[string]interface{}{
···
"allowExternalDiscovery": true,
}
-
reqBody, _ := json.Marshal(createReq)
+
reqBody, marshalErr := json.Marshal(createReq)
+
if marshalErr != nil {
+
t.Fatalf("Failed to marshal request: %v", marshalErr)
+
}
// Step 1: Client POSTs to XRPC endpoint
t.Logf("📡 Client → POST /xrpc/social.coves.community.create")
···
if err != nil {
t.Fatalf("Failed to POST: %v", err)
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
-
body, _ := io.ReadAll(resp.Body)
+
body, readErr := io.ReadAll(resp.Body)
+
if readErr != nil {
+
t.Fatalf("Expected 200, got %d (failed to read body: %v)", resp.StatusCode, readErr)
+
}
t.Logf("❌ XRPC Create Failed")
t.Logf(" Status: %d", resp.StatusCode)
t.Logf(" Response: %s", string(body))
···
Handle string `json:"handle"`
}
-
json.NewDecoder(resp.Body).Decode(&createResp)
+
if err := json.NewDecoder(resp.Body).Decode(&createResp); err != nil {
+
t.Fatalf("Failed to decode create response: %v", err)
+
}
t.Logf("✅ XRPC response received:")
t.Logf(" DID: %s", createResp.DID)
···
CID: createResp.CID,
},
}
-
consumer.HandleEvent(context.Background(), &event)
+
if handleErr := consumer.HandleEvent(context.Background(), &event); handleErr != nil {
+
t.Logf("Warning: failed to handle event: %v", handleErr)
+
}
// Step 3: Verify it's indexed in AppView
t.Logf("🔍 Querying AppView to verify indexing...")
···
if err != nil {
t.Fatalf("Failed to GET: %v", err)
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
-
body, _ := io.ReadAll(resp.Body)
+
body, readErr := io.ReadAll(resp.Body)
+
if readErr != nil {
+
t.Fatalf("Expected 200, got %d (failed to read body: %v)", resp.StatusCode, readErr)
+
}
t.Fatalf("Expected 200, got %d: %s", resp.StatusCode, string(body))
}
var getCommunity communities.Community
-
json.NewDecoder(resp.Body).Decode(&getCommunity)
+
if err := json.NewDecoder(resp.Body).Decode(&getCommunity); err != nil {
+
t.Fatalf("Failed to decode get response: %v", err)
+
}
-
t.Logf("✅ Retrieved via XRPC HTTP endpoint:")
+
t.Logf("Retrieved via XRPC HTTP endpoint:")
t.Logf(" DID: %s", getCommunity.DID)
t.Logf(" DisplayName: %s", getCommunity.DisplayName)
···
if err != nil {
t.Fatalf("Failed to GET list: %v", err)
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
-
body, _ := io.ReadAll(resp.Body)
+
body, readErr := io.ReadAll(resp.Body)
+
if readErr != nil {
+
t.Fatalf("Expected 200, got %d (failed to read body: %v)", resp.StatusCode, readErr)
+
}
t.Fatalf("Expected 200, got %d: %s", resp.StatusCode, string(body))
}
···
Total int `json:"total"`
}
-
json.NewDecoder(resp.Body).Decode(&listResp)
+
if err := json.NewDecoder(resp.Body).Decode(&listResp); err != nil {
+
t.Fatalf("Failed to decode list response: %v", err)
+
}
t.Logf("✅ Listed %d communities via XRPC", len(listResp.Communities))
···
collection := "social.coves.community.profile"
rkey := extractRKeyFromURI(community.RecordURI)
-
pdsResp, _ := http.Get(fmt.Sprintf("%s/xrpc/com.atproto.repo.getRecord?repo=%s&collection=%s&rkey=%s",
+
pdsResp, pdsErr := http.Get(fmt.Sprintf("%s/xrpc/com.atproto.repo.getRecord?repo=%s&collection=%s&rkey=%s",
pdsURL, instanceDID, collection, rkey))
-
defer pdsResp.Body.Close()
+
if pdsErr != nil {
+
t.Fatalf("Failed to fetch PDS record: %v", pdsErr)
+
}
+
defer func() {
+
if closeErr := pdsResp.Body.Close(); closeErr != nil {
+
t.Logf("Failed to close PDS response: %v", closeErr)
+
}
+
}()
var pdsRecord struct {
-
CID string `json:"cid"`
Value map[string]interface{} `json:"value"`
+
CID string `json:"cid"`
}
-
json.NewDecoder(pdsResp.Body).Decode(&pdsRecord)
+
if decodeErr := json.NewDecoder(pdsResp.Body).Decode(&pdsRecord); decodeErr != nil {
+
t.Fatalf("Failed to decode PDS record: %v", decodeErr)
+
}
// Simulate firehose event
event := jetstream.JetstreamEvent{
···
},
}
-
consumer.HandleEvent(context.Background(), &event)
+
if handleErr := consumer.HandleEvent(context.Background(), &event); handleErr != nil {
+
t.Logf("Warning: failed to handle event: %v", handleErr)
+
}
return community
}
···
"password": password,
}
-
reqBody, _ := json.Marshal(sessionReq)
+
reqBody, marshalErr := json.Marshal(sessionReq)
+
if marshalErr != nil {
+
return "", "", fmt.Errorf("failed to marshal session request: %w", marshalErr)
+
}
resp, err := http.Post(
pdsURL+"/xrpc/com.atproto.server.createSession",
"application/json",
···
if err != nil {
return "", "", fmt.Errorf("failed to create session: %w", err)
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
-
body, _ := io.ReadAll(resp.Body)
+
body, readErr := io.ReadAll(resp.Body)
+
if readErr != nil {
+
return "", "", fmt.Errorf("PDS auth failed (status %d, failed to read body: %w)", resp.StatusCode, readErr)
+
}
return "", "", fmt.Errorf("PDS auth failed (status %d): %s", resp.StatusCode, string(body))
}
···
if err != nil {
return "", "", fmt.Errorf("failed to query PDS: %w", err)
}
-
defer resp.Body.Close()
+
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
-
body, _ := io.ReadAll(resp.Body)
+
body, readErr := io.ReadAll(resp.Body)
+
if readErr != nil {
+
return "", "", fmt.Errorf("account not found (status %d, failed to read body: %w)", resp.StatusCode, readErr)
+
}
return "", "", fmt.Errorf("account not found (status %d): %s", resp.StatusCode, string(body))
}
···
if err != nil {
return fmt.Errorf("failed to connect to Jetstream: %w", err)
}
-
defer conn.Close()
+
defer func() { _ = conn.Close() }()
// Read messages until we find our event or receive done signal
for {
···
return ctx.Err()
default:
// Set read deadline to avoid blocking forever
-
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
+
if err := conn.SetReadDeadline(time.Now().Add(5 * time.Second)); err != nil {
+
return fmt.Errorf("failed to set read deadline: %w", err)
+
}
var event jetstream.JetstreamEvent
err := conn.ReadJSON(&event)
+88 -43
tests/integration/community_repo_test.go
···
package integration
import (
+
"Coves/internal/atproto/did"
+
"Coves/internal/core/communities"
+
"Coves/internal/db/postgres"
"context"
"fmt"
"testing"
"time"
-
-
"Coves/internal/atproto/did"
-
"Coves/internal/core/communities"
-
"Coves/internal/db/postgres"
)
func TestCommunityRepository_Create(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
t.Run("creates community successfully", func(t *testing.T) {
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
// Generate unique handle using timestamp to avoid collisions
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
···
})
t.Run("returns error for duplicate DID", func(t *testing.T) {
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
DID: communityDID,
···
}
// Create first time
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("First create failed: %v", err)
}
// Try to create again with same DID
-
_, err = repo.Create(ctx, community)
-
if 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, _ := didGen.GenerateCommunityDID()
+
did1, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate first community DID: %v", err)
+
}
community1 := &communities.Community{
DID: did1,
Handle: handle,
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community1)
-
if err != nil {
+
if _, err := repo.Create(ctx, community1); err != nil {
t.Fatalf("First create failed: %v", err)
}
// Second community with different DID but same handle
-
did2, _ := didGen.GenerateCommunityDID()
+
did2, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate second community DID: %v", err)
+
}
community2 := &communities.Community{
DID: did2,
Handle: handle, // Same handle!
···
UpdatedAt: time.Now(),
}
-
_, err = repo.Create(ctx, community2)
-
if err != communities.ErrHandleTaken {
+
if _, err = repo.Create(ctx, community2); err != communities.ErrHandleTaken {
t.Errorf("Expected ErrHandleTaken, got: %v", err)
}
})
···
func TestCommunityRepository_GetByDID(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %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, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
DID: communityDID,
···
})
t.Run("returns error for non-existent community", func(t *testing.T) {
-
fakeDID, _ := didGen.GenerateCommunityDID()
-
_, err := repo.GetByDID(ctx, fakeDID)
-
if err != communities.ErrCommunityNotFound {
+
fakeDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate fake DID: %v", err)
+
}
+
if _, err := repo.GetByDID(ctx, fakeDID); err != communities.ErrCommunityNotFound {
t.Errorf("Expected ErrCommunityNotFound, got: %v", err)
}
})
···
func TestCommunityRepository_GetByHandle(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %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, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
handle := fmt.Sprintf("!handle-lookup-%s@coves.local", uniqueSuffix)
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create community: %v", err)
}
···
func TestCommunityRepository_Subscriptions(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
didGen := did.NewGenerator(true, "https://plc.directory")
ctx := context.Background()
// Create a community for subscription tests
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
DID: communityDID,
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create community: %v", err)
}
···
SubscribedAt: time.Now(),
}
-
_, err := repo.Subscribe(ctx, sub)
-
if err != nil {
+
if _, err := repo.Subscribe(ctx, sub); err != nil {
t.Fatalf("First subscription failed: %v", err)
}
···
func TestCommunityRepository_List(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
didGen := did.NewGenerator(true, "https://plc.directory")
···
// Create multiple communities
baseSuffix := time.Now().UnixNano()
for i := 0; i < 5; i++ {
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
community := &communities.Community{
DID: communityDID,
Handle: fmt.Sprintf("!list-test-%d-%d@coves.local", baseSuffix, i),
···
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create community %d: %v", i, err)
}
time.Sleep(10 * time.Millisecond) // Ensure different timestamps
···
t.Run("filters by visibility", func(t *testing.T) {
// Create an unlisted community
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
DID: communityDID,
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create unlisted community: %v", err)
}
···
func TestCommunityRepository_Search(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
didGen := did.NewGenerator(true, "https://plc.directory")
···
t.Run("searches communities by name", func(t *testing.T) {
// Create a community with searchable name
-
communityDID, _ := didGen.GenerateCommunityDID()
+
communityDID, err := didGen.GenerateCommunityDID()
+
if err != nil {
+
t.Fatalf("Failed to generate community DID: %v", err)
+
}
uniqueSuffix := fmt.Sprintf("%d", time.Now().UnixNano())
community := &communities.Community{
DID: communityDID,
···
UpdatedAt: time.Now(),
}
-
_, err := repo.Create(ctx, community)
-
if err != nil {
+
if _, err := repo.Create(ctx, community); err != nil {
t.Fatalf("Failed to create community: %v", err)
}
+13 -6
tests/integration/community_v2_validation_test.go
···
package integration
import (
+
"Coves/internal/atproto/jetstream"
+
"Coves/internal/core/communities"
+
"Coves/internal/db/postgres"
"context"
"testing"
"time"
-
-
"Coves/internal/atproto/jetstream"
-
"Coves/internal/core/communities"
-
"Coves/internal/db/postgres"
)
// TestCommunityConsumer_V2RKeyValidation tests that only V2 communities (rkey="self") are accepted
func TestCommunityConsumer_V2RKeyValidation(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
consumer := jetstream.NewCommunityEventConsumer(repo)
···
// TestCommunityConsumer_HandleField tests the V2 handle field
func TestCommunityConsumer_HandleField(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
repo := postgres.NewCommunityRepository(db)
consumer := jetstream.NewCommunityEventConsumer(repo)
+36 -11
tests/integration/identity_resolution_test.go
···
package integration
import (
+
"Coves/internal/atproto/identity"
"context"
"fmt"
"os"
"testing"
"time"
-
-
"Coves/internal/atproto/identity"
)
// uniqueID generates a unique identifier for test isolation
···
// TestIdentityCache tests the PostgreSQL identity cache operations
func TestIdentityCache(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
cache := identity.NewPostgresCache(db, 5*time.Minute)
ctx := context.Background()
···
}
// Cleanup
-
cache.Delete(ctx, "alice.test")
+
if delErr := cache.Delete(ctx, "alice.test"); delErr != nil {
+
t.Logf("Failed to delete cache entry: %v", delErr)
+
}
})
t.Run("DID is Case Sensitive", func(t *testing.T) {
···
}
// Cleanup
-
cache.Delete(ctx, "did:plc:CaseSensitive")
+
if delErr := cache.Delete(ctx, "did:plc:CaseSensitive"); delErr != nil {
+
t.Logf("Failed to delete cache entry: %v", delErr)
+
}
})
}
// TestIdentityCacheTTL tests that expired cache entries are not returned
func TestIdentityCacheTTL(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
// Create cache with very short TTL (reduced from 1s to 100ms for faster, less flaky tests)
ttl := 100 * time.Millisecond
···
// TestIdentityResolverWithCache tests the caching resolver behavior
func TestIdentityResolverWithCache(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
cache := identity.NewPostgresCache(db, 5*time.Minute)
// Clean slate
-
_, _ = db.Exec("TRUNCATE identity_cache")
+
if _, err := db.Exec("TRUNCATE identity_cache"); err != nil {
+
t.Logf("Warning: failed to truncate identity_cache: %v", err)
+
}
// Create resolver with caching
resolver := identity.NewResolver(db, identity.Config{
···
}
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
resolver := identity.NewResolver(db, identity.Config{
PLCURL: "https://plc.directory",
···
testCases := []struct {
name string
handle string
-
expectError bool
expectedMethod identity.ResolutionMethod
+
expectError bool
}{
{
name: "Resolve bsky.app (well-known handle)",
···
}
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
resolver := identity.NewResolver(db, identity.Config{
PLCURL: "https://plc.directory",
+13 -6
tests/integration/jetstream_consumer_test.go
···
package integration
import (
-
"context"
-
"testing"
-
"time"
-
"Coves/internal/atproto/identity"
"Coves/internal/atproto/jetstream"
"Coves/internal/core/users"
"Coves/internal/db/postgres"
+
"context"
+
"testing"
+
"time"
)
func TestUserIndexingFromJetstream(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
// Wire up dependencies
userRepo := postgres.NewUserRepository(db)
···
func TestUserServiceIdempotency(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
userRepo := postgres.NewUserRepository(db)
resolver := identity.NewResolver(db, identity.DefaultConfig())
+74 -32
tests/integration/oauth_test.go
···
package integration
import (
+
"Coves/internal/api/handlers/oauth"
+
"Coves/internal/atproto/identity"
"bytes"
"context"
"encoding/json"
···
"os"
"testing"
-
"Coves/internal/api/handlers/oauth"
-
"Coves/internal/atproto/identity"
oauthCore "Coves/internal/core/oauth"
"github.com/lestrrat-go/jwx/v2/jwk"
···
// TestOAuthClientMetadata tests the /oauth/client-metadata.json endpoint
func TestOAuthClientMetadata(t *testing.T) {
tests := []struct {
-
name string
-
appviewURL string
-
expectedClientID string
-
expectedJWKSURI string
-
expectedRedirect string
+
name string
+
appviewURL string
+
expectedClientID string
+
expectedJWKSURI string
+
expectedRedirect string
}{
{
-
name: "localhost development",
-
appviewURL: "http://localhost:8081",
-
expectedClientID: "http://localhost?redirect_uri=http://localhost:8081/oauth/callback&scope=atproto%20transition:generic",
-
expectedJWKSURI: "", // No JWKS URI for localhost
-
expectedRedirect: "http://localhost:8081/oauth/callback",
+
name: "localhost development",
+
appviewURL: "http://localhost:8081",
+
expectedClientID: "http://localhost?redirect_uri=http://localhost:8081/oauth/callback&scope=atproto%20transition:generic",
+
expectedJWKSURI: "", // No JWKS URI for localhost
+
expectedRedirect: "http://localhost:8081/oauth/callback",
},
{
-
name: "production HTTPS",
-
appviewURL: "https://coves.social",
-
expectedClientID: "https://coves.social/oauth/client-metadata.json",
-
expectedJWKSURI: "https://coves.social/oauth/jwks.json",
-
expectedRedirect: "https://coves.social/oauth/callback",
+
name: "production HTTPS",
+
appviewURL: "https://coves.social",
+
expectedClientID: "https://coves.social/oauth/client-metadata.json",
+
expectedJWKSURI: "https://coves.social/oauth/jwks.json",
+
expectedRedirect: "https://coves.social/oauth/callback",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set environment
-
os.Setenv("APPVIEW_PUBLIC_URL", tt.appviewURL)
-
defer os.Unsetenv("APPVIEW_PUBLIC_URL")
+
if err := os.Setenv("APPVIEW_PUBLIC_URL", tt.appviewURL); err != nil {
+
t.Fatalf("Failed to set APPVIEW_PUBLIC_URL: %v", err)
+
}
+
defer func() {
+
if err := os.Unsetenv("APPVIEW_PUBLIC_URL"); err != nil {
+
t.Logf("Failed to unset APPVIEW_PUBLIC_URL: %v", err)
+
}
+
}()
// Create request
req := httptest.NewRequest("GET", "/oauth/client-metadata.json", nil)
···
t.Run(tt.name, func(t *testing.T) {
// Set environment
if tt.envValue != "" {
-
os.Setenv("OAUTH_PRIVATE_JWK", tt.envValue)
-
defer os.Unsetenv("OAUTH_PRIVATE_JWK")
+
if err := os.Setenv("OAUTH_PRIVATE_JWK", tt.envValue); err != nil {
+
t.Fatalf("Failed to set OAUTH_PRIVATE_JWK: %v", err)
+
}
+
defer func() {
+
if err := os.Unsetenv("OAUTH_PRIVATE_JWK"); err != nil {
+
t.Logf("Failed to unset OAUTH_PRIVATE_JWK: %v", err)
+
}
+
}()
}
// Create request
···
// Setup test database
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
// Create session store
sessionStore := oauthCore.NewPostgresSessionStore(db)
···
t.Run(tt.name, func(t *testing.T) {
// Set environment
if tt.envJWK != "" {
-
os.Setenv("OAUTH_PRIVATE_JWK", tt.envJWK)
-
defer os.Unsetenv("OAUTH_PRIVATE_JWK")
+
if err := os.Setenv("OAUTH_PRIVATE_JWK", tt.envJWK); err != nil {
+
t.Fatalf("Failed to set OAUTH_PRIVATE_JWK: %v", err)
+
}
+
defer func() {
+
if err := os.Unsetenv("OAUTH_PRIVATE_JWK"); err != nil {
+
t.Logf("Failed to unset OAUTH_PRIVATE_JWK: %v", err)
+
}
+
}()
} else {
-
os.Unsetenv("OAUTH_PRIVATE_JWK")
+
if err := os.Unsetenv("OAUTH_PRIVATE_JWK"); err != nil {
+
t.Logf("Failed to unset OAUTH_PRIVATE_JWK: %v", err)
+
}
}
// Create mock identity resolver for validation tests
···
handler := oauth.NewLoginHandler(mockResolver, sessionStore)
// Create request
-
bodyBytes, _ := json.Marshal(tt.requestBody)
+
bodyBytes, marshalErr := json.Marshal(tt.requestBody)
+
if marshalErr != nil {
+
t.Fatalf("Failed to marshal request body: %v", marshalErr)
+
}
req := httptest.NewRequest("POST", "/oauth/login", bytes.NewReader(bodyBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
···
// Setup test database
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
// Create session store
sessionStore := oauthCore.NewPostgresSessionStore(db)
···
testJWK := `{"alg":"ES256","crv":"P-256","d":"9tCMceYSgyZfO5KYOCm3rWEhXLqq2l4LjP7-PJtJKyk","kid":"oauth-client-key","kty":"EC","use":"sig","x":"EOYWEgZ2d-smTO6jh0f-9B7YSFYdlrvlryjuXTCrOjE","y":"_FR2jBcWNxoJl5cd1eq9sYtAs33No9AVtd42UyyWYi4"}`
tests := []struct {
-
name string
queryParams map[string]string
+
name string
expectedStatus int
}{
{
···
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set environment
-
os.Setenv("OAUTH_PRIVATE_JWK", testJWK)
-
defer os.Unsetenv("OAUTH_PRIVATE_JWK")
+
if err := os.Setenv("OAUTH_PRIVATE_JWK", testJWK); err != nil {
+
t.Fatalf("Failed to set OAUTH_PRIVATE_JWK: %v", err)
+
}
+
defer func() {
+
if err := os.Unsetenv("OAUTH_PRIVATE_JWK"); err != nil {
+
t.Logf("Failed to unset OAUTH_PRIVATE_JWK: %v", err)
+
}
+
}()
// Create handler
handler := oauth.NewCallbackHandler(sessionStore)
···
}
// Verify public key doesn't have private component
-
pubKeyJSON, _ := json.Marshal(pubKey)
+
pubKeyJSON, marshalErr := json.Marshal(pubKey)
+
if marshalErr != nil {
+
t.Fatalf("failed to marshal public key: %v", marshalErr)
+
}
var pubKeyMap map[string]interface{}
-
json.Unmarshal(pubKeyJSON, &pubKeyMap)
+
if unmarshalErr := json.Unmarshal(pubKeyJSON, &pubKeyMap); unmarshalErr != nil {
+
t.Fatalf("failed to unmarshal public key: %v", unmarshalErr)
+
}
if _, hasPrivate := pubKeyMap["d"]; hasPrivate {
t.Error("SECURITY: public key should not contain private 'd' component!")
+31 -17
tests/integration/user_test.go
···
package integration
import (
+
"Coves/internal/api/routes"
+
"Coves/internal/atproto/identity"
+
"Coves/internal/core/users"
+
"Coves/internal/db/postgres"
"context"
"database/sql"
"encoding/json"
···
"github.com/go-chi/chi/v5"
_ "github.com/lib/pq"
"github.com/pressly/goose/v3"
-
-
"Coves/internal/api/routes"
-
"Coves/internal/atproto/identity"
-
"Coves/internal/core/users"
-
"Coves/internal/db/postgres"
)
func setupTestDB(t *testing.T) *sql.DB {
···
t.Fatalf("Failed to connect to test database: %v", err)
}
-
if err := db.Ping(); err != nil {
-
t.Fatalf("Failed to ping test database: %v", err)
+
if pingErr := db.Ping(); pingErr != nil {
+
t.Fatalf("Failed to ping test database: %v", pingErr)
}
-
if err := goose.SetDialect("postgres"); err != nil {
-
t.Fatalf("Failed to set goose dialect: %v", err)
+
if dialectErr := goose.SetDialect("postgres"); dialectErr != nil {
+
t.Fatalf("Failed to set goose dialect: %v", dialectErr)
}
-
if err := goose.Up(db, "../../internal/db/migrations"); err != nil {
-
t.Fatalf("Failed to run migrations: %v", err)
+
if migrateErr := goose.Up(db, "../../internal/db/migrations"); migrateErr != nil {
+
t.Fatalf("Failed to run migrations: %v", migrateErr)
}
// Clean up any existing test data
···
func TestUserCreationAndRetrieval(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
// Wire up dependencies
userRepo := postgres.NewUserRepository(db)
···
func TestGetProfileEndpoint(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
// Wire up dependencies
userRepo := postgres.NewUserRepository(db)
···
// TestDuplicateCreation tests that duplicate DID/handle creation fails properly
func TestDuplicateCreation(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
userRepo := postgres.NewUserRepository(db)
resolver := identity.NewResolver(db, identity.DefaultConfig())
···
Handle: "different.test", // Different handle, same DID
PDSURL: "http://localhost:3001",
})
-
// Should return existing user, not error
if err != nil {
t.Fatalf("Expected idempotent behavior, got error: %v", err)
···
// TestHandleValidation tests atProto handle validation rules
func TestHandleValidation(t *testing.T) {
db := setupTestDB(t)
-
defer db.Close()
+
defer func() {
+
if err := db.Close(); err != nil {
+
t.Logf("Failed to close database: %v", err)
+
}
+
}()
userRepo := postgres.NewUserRepository(db)
resolver := identity.NewResolver(db, identity.DefaultConfig())
···
did string
handle string
pdsURL string
-
shouldError bool
errorMsg string
+
shouldError bool
}{
{
name: "Valid handle with hyphen",
+29 -23
tests/lexicon_validation_test.go
···
for _, filePath := range lexiconFiles {
// Convert file path to schema ID
// e.g., ../internal/atproto/lexicon/social/coves/actor/profile.json -> social.coves.actor.profile
-
relPath, _ := filepath.Rel(schemaPath, filePath)
+
relPath, err := filepath.Rel(schemaPath, filePath)
+
if err != nil {
+
t.Fatalf("Failed to get relative path for %s: %v", filePath, err)
+
}
relPath = strings.TrimSuffix(relPath, ".json")
schemaID := strings.ReplaceAll(relPath, string(filepath.Separator), ".")
t.Run(schemaID, func(t *testing.T) {
-
if _, err := catalog.Resolve(schemaID); err != nil {
-
t.Errorf("Failed to resolve schema %s: %v", schemaID, err)
+
if _, resolveErr := catalog.Resolve(schemaID); resolveErr != nil {
+
t.Errorf("Failed to resolve schema %s: %v", schemaID, resolveErr)
}
})
}
···
// Test specific cross-references that should work
crossRefs := map[string]string{
-
"social.coves.richtext.facet#byteSlice": "byteSlice definition in facet schema",
+
"social.coves.richtext.facet#byteSlice": "byteSlice definition in facet schema",
"social.coves.actor.profile#geoLocation": "geoLocation definition in actor profile",
"social.coves.community.rules#rule": "rule definition in community rules",
}
···
// Test cases for ValidateRecord
tests := []struct {
-
name string
-
recordType string
-
recordData map[string]interface{}
-
shouldFail bool
+
recordData map[string]interface{}
+
name string
+
recordType string
errorContains string
+
shouldFail bool
}{
{
name: "Valid actor profile",
···
recordType: "social.coves.community.profile",
recordData: map[string]interface{}{
"$type": "social.coves.community.profile",
+
"handle": "programming.communities.coves.social",
"name": "programming",
"displayName": "Programming Community",
-
"creator": "did:plc:creator123",
+
"createdBy": "did:plc:creator123",
+
"hostedBy": "did:plc:coves123",
+
"visibility": "public",
"moderationType": "moderator",
"federatedFrom": "coves",
"createdAt": "2023-12-01T08:00:00Z",
···
name: "Valid post record",
recordType: "social.coves.post.record",
recordData: map[string]interface{}{
-
"$type": "social.coves.post.record",
-
"community": "did:plc:programming123",
-
"postType": "text",
-
"title": "Test Post",
-
"content": "This is a test post",
-
"createdAt": "2025-01-09T14:30:00Z",
+
"$type": "social.coves.post.record",
+
"community": "did:plc:programming123",
+
"postType": "text",
+
"title": "Test Post",
+
"content": "This is a test post",
+
"createdAt": "2025-01-09T14:30:00Z",
},
shouldFail: false,
},
···
name: "Invalid post record - invalid enum value",
recordType: "social.coves.post.record",
recordData: map[string]interface{}{
-
"$type": "social.coves.post.record",
-
"community": "did:plc:programming123",
-
"postType": "invalid-type",
-
"title": "Test Post",
-
"content": "This is a test post",
-
"createdAt": "2025-01-09T14:30:00Z",
+
"$type": "social.coves.post.record",
+
"community": "did:plc:programming123",
+
"postType": "invalid-type",
+
"title": "Test Post",
+
"content": "This is a test post",
+
"createdAt": "2025-01-09T14:30:00Z",
},
shouldFail: true,
errorContains: "string val not in required enum",
···
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := lexicon.ValidateRecord(&catalog, tt.recordData, tt.recordType, lexicon.AllowLenientDatetime)
-
+
if tt.shouldFail {
if err == nil {
t.Errorf("Expected validation to fail but it passed")
···
if err != nil {
t.Errorf("Expected lenient validation to pass, got error: %v", err)
}
-
}
+
}
+8 -5
tests/unit/community_service_test.go
···
package unit
import (
+
"Coves/internal/atproto/did"
+
"Coves/internal/core/communities"
"context"
"fmt"
"net/http"
···
"sync/atomic"
"testing"
"time"
-
-
"Coves/internal/atproto/did"
-
"Coves/internal/core/communities"
)
// mockCommunityRepo is a minimal mock for testing service layer
···
time.Sleep(15 * time.Second)
w.WriteHeader(http.StatusOK)
-
w.Write([]byte(`{"uri":"at://did:plc:test/collection/self","cid":"bafyrei123"}`))
+
if _, err := w.Write([]byte(`{"uri":"at://did:plc:test/collection/self","cid":"bafyrei123"}`)); err != nil {
+
t.Errorf("Failed to write response: %v", err)
+
}
}))
defer slowPDS.Close()
···
}
w.WriteHeader(http.StatusOK)
-
w.Write([]byte(`{"uri":"at://did:plc:community/social.coves.community.profile/self","cid":"bafyrei456"}`))
+
if _, err := w.Write([]byte(`{"uri":"at://did:plc:community/social.coves.community.profile/self","cid":"bafyrei456"}`)); err != nil {
+
t.Errorf("Failed to write response: %v", err)
+
}
}))
defer mockPDS.Close()