package babble import ( "os" "testing" "time" "dk/internal/database" ) func setupTestDB(t *testing.T) *database.DB { testDB := "test_babble.db" t.Cleanup(func() { os.Remove(testDB) }) db, err := database.Open(testDB) if err != nil { t.Fatalf("Failed to open test database: %v", err) } // Create babble table createTable := `CREATE TABLE babble ( id INTEGER PRIMARY KEY AUTOINCREMENT, posted INTEGER NOT NULL DEFAULT (unixepoch()), author TEXT NOT NULL DEFAULT '', babble TEXT NOT NULL DEFAULT '' )` if err := db.Exec(createTable); err != nil { t.Fatalf("Failed to create babble table: %v", err) } // Insert test data with specific timestamps for predictable testing now := time.Now().Unix() testBabble := `INSERT INTO babble (posted, author, babble) VALUES (?, 'Alice', 'Hello everyone! Welcome to the game'), (?, 'Bob', 'Thanks Alice! @Alice this game is great'), (?, 'Charlie', 'Anyone want to team up for the dungeon?'), (?, 'Alice', 'I can help @Charlie, let me know'), (?, 'David', 'Server lag is really bad right now...'), (?, 'Eve', 'Quick question about spell mechanics')` timestamps := []interface{}{ now - 3600*6, // 6 hours ago now - 3600*4, // 4 hours ago now - 3600*2, // 2 hours ago now - 3600*1, // 1 hour ago now - 1800, // 30 minutes ago now - 300, // 5 minutes ago } if err := db.Exec(testBabble, timestamps...); err != nil { t.Fatalf("Failed to insert test babble: %v", err) } return db } func TestFind(t *testing.T) { db := setupTestDB(t) defer db.Close() // Test finding existing babble babble, err := Find(db, 1) if err != nil { t.Fatalf("Failed to find babble: %v", err) } if babble.ID != 1 { t.Errorf("Expected ID 1, got %d", babble.ID) } if babble.Author != "Alice" { t.Errorf("Expected author 'Alice', got '%s'", babble.Author) } if babble.Babble != "Hello everyone! Welcome to the game" { t.Errorf("Expected specific message, got '%s'", babble.Babble) } if babble.Posted == 0 { t.Error("Expected non-zero posted timestamp") } // Test finding non-existent babble _, err = Find(db, 999) if err == nil { t.Error("Expected error when finding non-existent babble") } } func TestAll(t *testing.T) { db := setupTestDB(t) defer db.Close() babbles, err := All(db) if err != nil { t.Fatalf("Failed to get all babble: %v", err) } if len(babbles) != 6 { t.Errorf("Expected 6 babble messages, got %d", len(babbles)) } // Check ordering (newest first) if len(babbles) >= 2 { if babbles[0].Posted < babbles[1].Posted { t.Error("Expected babble to be ordered by posted time (newest first)") } } // First message should be the most recent (5 minutes ago) if babbles[0].Author != "Eve" { t.Errorf("Expected newest message from Eve, got from '%s'", babbles[0].Author) } } func TestByAuthor(t *testing.T) { db := setupTestDB(t) defer db.Close() // Test messages by Alice aliceMessages, err := ByAuthor(db, "Alice") if err != nil { t.Fatalf("Failed to get babble by author: %v", err) } if len(aliceMessages) != 2 { t.Errorf("Expected 2 messages by Alice, got %d", len(aliceMessages)) } // Verify all messages are by Alice for _, message := range aliceMessages { if message.Author != "Alice" { t.Errorf("Expected author 'Alice', got '%s'", message.Author) } } // Check ordering (newest first) if len(aliceMessages) == 2 { if aliceMessages[0].Babble != "I can help @Charlie, let me know" { t.Errorf("Expected newest message by Alice first") } } // Test case insensitive search aliceMessagesLower, err := ByAuthor(db, "alice") if err != nil { t.Fatalf("Failed to get babble by lowercase author: %v", err) } if len(aliceMessagesLower) != 2 { t.Errorf("Expected case insensitive search to find 2 messages, got %d", len(aliceMessagesLower)) } // Test author with no messages noMessages, err := ByAuthor(db, "NonexistentUser") if err != nil { t.Fatalf("Failed to query non-existent author: %v", err) } if len(noMessages) != 0 { t.Errorf("Expected 0 messages by non-existent author, got %d", len(noMessages)) } } func TestRecent(t *testing.T) { db := setupTestDB(t) defer db.Close() // Test getting 3 most recent messages recentMessages, err := Recent(db, 3) if err != nil { t.Fatalf("Failed to get recent babble: %v", err) } if len(recentMessages) != 3 { t.Errorf("Expected 3 recent messages, got %d", len(recentMessages)) } // Check ordering (newest first) if len(recentMessages) >= 2 { if recentMessages[0].Posted < recentMessages[1].Posted { t.Error("Expected recent messages to be ordered newest first") } } // Test getting more messages than exist allRecentMessages, err := Recent(db, 10) if err != nil { t.Fatalf("Failed to get recent babble with high limit: %v", err) } if len(allRecentMessages) != 6 { t.Errorf("Expected 6 messages (all available), got %d", len(allRecentMessages)) } } func TestSince(t *testing.T) { db := setupTestDB(t) defer db.Close() // Test messages since 3 hours ago threeHoursAgo := time.Now().Add(-3 * time.Hour).Unix() recentMessages, err := Since(db, threeHoursAgo) if err != nil { t.Fatalf("Failed to get babble since timestamp: %v", err) } // Should get messages from 2 hours ago, 1 hour ago, 30 minutes ago, and 5 minutes ago expectedCount := 4 if len(recentMessages) != expectedCount { t.Errorf("Expected %d messages since 3 hours ago, got %d", expectedCount, len(recentMessages)) } // Verify all messages are since the timestamp for _, message := range recentMessages { if message.Posted < threeHoursAgo { t.Errorf("Message with timestamp %d is before the 'since' timestamp %d", message.Posted, threeHoursAgo) } } // Test with future timestamp (should return no messages) futureMessages, err := Since(db, time.Now().Add(time.Hour).Unix()) if err != nil { t.Fatalf("Failed to query future timestamp: %v", err) } if len(futureMessages) != 0 { t.Errorf("Expected 0 messages since future timestamp, got %d", len(futureMessages)) } } func TestBetween(t *testing.T) { db := setupTestDB(t) defer db.Close() // Test messages between 5 hours ago and 1 hour ago start := time.Now().Add(-5 * time.Hour).Unix() end := time.Now().Add(-1 * time.Hour).Unix() betweenMessages, err := Between(db, start, end) if err != nil { t.Fatalf("Failed to get babble between timestamps: %v", err) } // Should get messages from 4 hours ago, 2 hours ago, and 1 hour ago (inclusive end) expectedCount := 3 if len(betweenMessages) != expectedCount { t.Errorf("Expected %d messages between timestamps, got %d", expectedCount, len(betweenMessages)) } // Verify all messages are within the range for _, message := range betweenMessages { if message.Posted < start || message.Posted > end { t.Errorf("Message with timestamp %d is outside range [%d, %d]", message.Posted, start, end) } } } func TestSearch(t *testing.T) { db := setupTestDB(t) defer db.Close() // Test searching for "game" gameMessages, err := Search(db, "game") if err != nil { t.Fatalf("Failed to search babble: %v", err) } expectedCount := 2 // Alice's welcome message and Bob's response if len(gameMessages) != expectedCount { t.Errorf("Expected %d messages containing 'game', got %d", expectedCount, len(gameMessages)) } // Verify all messages contain the search term for _, message := range gameMessages { if !message.Contains("game") { t.Errorf("Message '%s' does not contain search term 'game'", message.Babble) } } // Test case insensitive search gameMessagesUpper, err := Search(db, "GAME") if err != nil { t.Fatalf("Failed to search babble with uppercase: %v", err) } if len(gameMessagesUpper) != expectedCount { t.Error("Expected case insensitive search to find same results") } // Test search with no results noResults, err := Search(db, "nonexistentterm") if err != nil { t.Fatalf("Failed to search for non-existent term: %v", err) } if len(noResults) != 0 { t.Errorf("Expected 0 results for non-existent term, got %d", len(noResults)) } } func TestRecentByAuthor(t *testing.T) { db := setupTestDB(t) defer db.Close() // Test recent messages by Alice (limit 1) aliceRecent, err := RecentByAuthor(db, "Alice", 1) if err != nil { t.Fatalf("Failed to get recent babble by author: %v", err) } if len(aliceRecent) != 1 { t.Errorf("Expected 1 recent message by Alice, got %d", len(aliceRecent)) } if len(aliceRecent) > 0 && aliceRecent[0].Babble != "I can help @Charlie, let me know" { t.Error("Expected most recent message by Alice") } // Test with higher limit aliceAll, err := RecentByAuthor(db, "Alice", 5) if err != nil { t.Fatalf("Failed to get all recent messages by Alice: %v", err) } if len(aliceAll) != 2 { t.Errorf("Expected 2 total messages by Alice, got %d", len(aliceAll)) } } func TestBuilder(t *testing.T) { db := setupTestDB(t) defer db.Close() // Create new babble using builder testTime := time.Now() babble, err := NewBuilder(db). WithAuthor("TestUser"). WithBabble("Test message from builder"). WithPostedTime(testTime). Create() if err != nil { t.Fatalf("Failed to create babble with builder: %v", err) } if babble.ID == 0 { t.Error("Expected non-zero ID after creation") } if babble.Author != "TestUser" { t.Errorf("Expected author 'TestUser', got '%s'", babble.Author) } if babble.Babble != "Test message from builder" { t.Errorf("Expected specific message, got '%s'", babble.Babble) } if babble.Posted != testTime.Unix() { t.Errorf("Expected posted time %d, got %d", testTime.Unix(), babble.Posted) } // Test WithMessage alias babble2, err := NewBuilder(db). WithAuthor("TestUser2"). WithMessage("Using WithMessage alias"). Create() if err != nil { t.Fatalf("Failed to create babble with WithMessage: %v", err) } if babble2.Babble != "Using WithMessage alias" { t.Errorf("WithMessage alias failed, got '%s'", babble2.Babble) } // Verify it was saved to database foundBabble, err := Find(db, babble.ID) if err != nil { t.Fatalf("Failed to find created babble: %v", err) } if foundBabble.Babble != "Test message from builder" { t.Errorf("Created babble not found in database") } // Test builder with default timestamp defaultBabble, err := NewBuilder(db). WithAuthor("DefaultUser"). WithBabble("Message with default timestamp"). Create() if err != nil { t.Fatalf("Failed to create babble with default timestamp: %v", err) } // Should have recent timestamp (within last minute) if time.Since(defaultBabble.PostedTime()) > time.Minute { t.Error("Expected default timestamp to be recent") } } func TestSave(t *testing.T) { db := setupTestDB(t) defer db.Close() babble, err := Find(db, 1) if err != nil { t.Fatalf("Failed to find babble: %v", err) } // Modify babble babble.Author = "UpdatedAuthor" babble.Babble = "Updated message content" babble.Posted = time.Now().Unix() // Save changes err = babble.Save() if err != nil { t.Fatalf("Failed to save babble: %v", err) } // Verify changes were saved updatedBabble, err := Find(db, 1) if err != nil { t.Fatalf("Failed to find updated babble: %v", err) } if updatedBabble.Author != "UpdatedAuthor" { t.Errorf("Expected updated author 'UpdatedAuthor', got '%s'", updatedBabble.Author) } if updatedBabble.Babble != "Updated message content" { t.Errorf("Expected updated message, got '%s'", updatedBabble.Babble) } } func TestDelete(t *testing.T) { db := setupTestDB(t) defer db.Close() babble, err := Find(db, 1) if err != nil { t.Fatalf("Failed to find babble: %v", err) } // Delete babble err = babble.Delete() if err != nil { t.Fatalf("Failed to delete babble: %v", err) } // Verify babble was deleted _, err = Find(db, 1) if err == nil { t.Error("Expected error when finding deleted babble") } } func TestUtilityMethods(t *testing.T) { db := setupTestDB(t) defer db.Close() babble, _ := Find(db, 1) // Test PostedTime postedTime := babble.PostedTime() if postedTime.IsZero() { t.Error("Expected non-zero posted time") } // Test SetPostedTime newTime := time.Now().Add(-30 * time.Minute) babble.SetPostedTime(newTime) if babble.Posted != newTime.Unix() { t.Errorf("Expected posted timestamp %d, got %d", newTime.Unix(), babble.Posted) } // Test IsRecent (should be true for 30 minutes ago) if !babble.IsRecent() { t.Error("Expected message from 30 minutes ago to be recent") } // Test Age age := babble.Age() if age < 0 { t.Error("Expected positive age") } // Test IsAuthor if !babble.IsAuthor("Alice") { t.Error("Expected IsAuthor to return true for correct author") } if !babble.IsAuthor("alice") { // Test case insensitive t.Error("Expected IsAuthor to be case insensitive") } if babble.IsAuthor("Bob") { t.Error("Expected IsAuthor to return false for incorrect author") } // Test Preview longMessage := "This is a very long chat message that should be truncated when preview is called for display purposes" babble.Babble = longMessage preview := babble.Preview(20) if len(preview) > 20 { t.Errorf("Expected preview length <= 20, got %d", len(preview)) } if preview[len(preview)-3:] != "..." { t.Error("Expected preview to end with ellipsis") } shortPreview := babble.Preview(200) // Longer than message if shortPreview != longMessage { t.Error("Expected short message to not be truncated") } // Test WordCount babble.Babble = "This is a test with five words" wordCount := babble.WordCount() if wordCount != 7 { t.Errorf("Expected 7 words, got %d", wordCount) } // Test Length expectedLength := len(babble.Babble) if babble.Length() != expectedLength { t.Errorf("Expected length %d, got %d", expectedLength, babble.Length()) } // Test Contains if !babble.Contains("test") { t.Error("Expected message to contain 'test'") } if !babble.Contains("TEST") { // Case insensitive t.Error("Expected Contains to be case insensitive") } if babble.Contains("nonexistent") { t.Error("Expected message not to contain 'nonexistent'") } // Test IsEmpty babble.Babble = "" if !babble.IsEmpty() { t.Error("Expected empty message to be empty") } babble.Babble = " " if !babble.IsEmpty() { t.Error("Expected whitespace-only message to be empty") } babble.Babble = "Not empty" if babble.IsEmpty() { t.Error("Expected non-empty message not to be empty") } // Test IsLongMessage shortMsg := "Short" babble.Babble = shortMsg if babble.IsLongMessage(100) { t.Error("Expected short message not to be long") } if !babble.IsLongMessage(3) { t.Error("Expected message longer than threshold to be long") } } func TestMentionMethods(t *testing.T) { db := setupTestDB(t) defer db.Close() // Test GetMentions babble, _ := Find(db, 2) // Bob's message: "Thanks Alice! @Alice this game is great" mentions := babble.GetMentions() expectedMentions := []string{"Alice"} if len(mentions) != len(expectedMentions) { t.Errorf("Expected %d mentions, got %d", len(expectedMentions), len(mentions)) } for i, expected := range expectedMentions { if i < len(mentions) && mentions[i] != expected { t.Errorf("Expected mention '%s' at position %d, got '%s'", expected, i, mentions[i]) } } // Test HasMention if !babble.HasMention("Alice") { t.Error("Expected message to mention Alice") } if !babble.HasMention("alice") { // Case insensitive t.Error("Expected HasMention to be case insensitive") } if babble.HasMention("Bob") { t.Error("Expected message not to mention Bob") } // Test message with multiple mentions and punctuation babble.Babble = "Hey @Alice, @Bob! Can you help @Charlie?" mentions = babble.GetMentions() expectedMentions = []string{"Alice", "Bob", "Charlie"} if len(mentions) != len(expectedMentions) { t.Errorf("Expected %d mentions, got %d: %v", len(expectedMentions), len(mentions), mentions) } for _, expected := range expectedMentions { if !babble.HasMention(expected) { t.Errorf("Expected message to mention %s", expected) } } // Test message with no mentions babble.Babble = "No mentions in this message" mentions = babble.GetMentions() if len(mentions) != 0 { t.Errorf("Expected 0 mentions, got %d", len(mentions)) } // Test malformed mentions (should be ignored) babble.Babble = "Just @ alone or @" mentions = babble.GetMentions() if len(mentions) != 0 { t.Errorf("Expected 0 mentions for malformed @, got %d", len(mentions)) } }