package store import ( "context" "os" "testing" "time" "github.com/jackc/pgx/v5/pgxpool" ) // TestIntegration exercises basic CRUD operations against a real database. func TestIntegration(t *testing.T) { url := os.Getenv("MESSENGER_TEST_DATABASE_URL") if url == "" { t.Skip("skipping integration test; MESSENGER_TEST_DATABASE_URL not set") } ctx := context.Background() pool, err := pgxpool.New(ctx, url) if err != nil { t.Fatalf("unable to connect to db: %v", err) } defer pool.Close() db := New(pool) if err := db.Migrate(ctx); err != nil { t.Fatalf("Migrate failed: %v", err) } if _, err := pool.Exec(ctx, "TRUNCATE users, conversations, messages, contacts, sessions RESTART IDENTITY CASCADE"); err != nil { t.Fatalf("truncate failed: %v", err) } // Users u1, err := db.CreateUser(ctx, "user1", "User One", "hash1") if err != nil { t.Fatalf("CreateUser u1: %v", err) } u2, err := db.CreateUser(ctx, "user2", "User Two", "hash2") if err != nil { t.Fatalf("CreateUser u2: %v", err) } if _, err := db.GetUserByUsername(ctx, u1.Username); err != nil { t.Fatalf("GetUserByUsername: %v", err) } if _, err := db.GetUserByID(ctx, u2.ID); err != nil { t.Fatalf("GetUserByID: %v", err) } // Sessions exp := time.Now().Add(time.Hour) sess, err := db.CreateSession(ctx, u1.ID, "rh1", "ua", "127.0.0.1", exp) if err != nil { t.Fatalf("CreateSession: %v", err) } if _, err := db.GetSessionByRefreshHash(ctx, sess.RefreshHash); err != nil { t.Fatalf("GetSessionByRefreshHash: %v", err) } if err := db.RevokeSession(ctx, sess.ID); err != nil { t.Fatalf("RevokeSession: %v", err) } if err := db.RevokeAllSessions(ctx, u1.ID); err != nil { t.Fatalf("RevokeAllSessions: %v", err) } // Contacts if err := db.AddContact(ctx, u1.ID, u2.ID); err != nil { t.Fatalf("AddContact: %v", err) } if ok, err := db.IsContact(ctx, u1.ID, u2.ID); err != nil || !ok { t.Fatalf("IsContact: %v, %v", ok, err) } list, err := db.ListContacts(ctx, u1.ID) if err != nil || len(list) != 1 { t.Fatalf("ListContacts: got %v, err %v", list, err) } if err := db.RemoveContact(ctx, u1.ID, u2.ID); err != nil { t.Fatalf("RemoveContact: %v", err) } // Conversations and messages conv, err := db.CreateDM(ctx, u1.ID, u2.ID) if err != nil { t.Fatalf("CreateDM: %v", err) } if conv2, err := db.CreateDM(ctx, u1.ID, u2.ID); err != nil || conv2.ID != conv.ID { t.Fatalf("CreateDM idempotent: %v, %v", err, conv2) } if ok, err := db.IsMember(ctx, conv.ID, u1.ID); err != nil || !ok { t.Fatalf("IsMember: %v, %v", ok, err) } ids, err := db.ConversationIDsForUser(ctx, u1.ID) if err != nil || len(ids) == 0 { t.Fatalf("ConversationIDsForUser: %v, %v", ids, err) } sums, err := db.ListConversationsForUser(ctx, u1.ID) if err != nil || len(sums) != 1 { t.Fatalf("ListConversationsForUser: %v, %v", sums, err) } msg, err := db.InsertMessage(ctx, conv.ID, u1.ID, "hello") if err != nil { t.Fatalf("InsertMessage: %v", err) } if _, err := db.GetMessageByID(ctx, msg.ID); err != nil { t.Fatalf("GetMessageByID: %v", err) } msgs, err := db.ListMessages(ctx, conv.ID, 0, 10) if err != nil || len(msgs) != 1 { t.Fatalf("ListMessages: %v, %v", msgs, err) } if err := db.UpdateLastRead(ctx, conv.ID, u2.ID); err != nil { t.Fatalf("UpdateLastRead: %v", err) } }