460 lines
11 KiB
Go
460 lines
11 KiB
Go
package news
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"dk/internal/database"
|
|
)
|
|
|
|
func setupTestDB(t *testing.T) *database.DB {
|
|
testDB := "test_news.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 news table
|
|
createTable := `CREATE TABLE news (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
author INTEGER NOT NULL,
|
|
posted INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
content TEXT NOT NULL
|
|
)`
|
|
|
|
if err := db.Exec(createTable); err != nil {
|
|
t.Fatalf("Failed to create news table: %v", err)
|
|
}
|
|
|
|
// Insert test data with specific timestamps for predictable testing
|
|
now := time.Now().Unix()
|
|
testNews := `INSERT INTO news (author, posted, content) VALUES
|
|
(1, ?, 'First news post about game updates'),
|
|
(2, ?, 'Second post from different author'),
|
|
(1, ?, 'Third post - recent update'),
|
|
(3, ?, 'Fourth post from admin'),
|
|
(2, ?, 'Fifth post - maintenance notice')`
|
|
|
|
timestamps := []any{
|
|
now - 86400*7, // 1 week ago
|
|
now - 86400*5, // 5 days ago
|
|
now - 86400*2, // 2 days ago
|
|
now - 86400*1, // 1 day ago
|
|
now - 3600, // 1 hour ago
|
|
}
|
|
|
|
if err := db.Exec(testNews, timestamps...); err != nil {
|
|
t.Fatalf("Failed to insert test news: %v", err)
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
func TestFind(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Test finding existing news
|
|
news, err := Find(db, 1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to find news: %v", err)
|
|
}
|
|
|
|
if news.ID != 1 {
|
|
t.Errorf("Expected ID 1, got %d", news.ID)
|
|
}
|
|
if news.Author != 1 {
|
|
t.Errorf("Expected author 1, got %d", news.Author)
|
|
}
|
|
if news.Content != "First news post about game updates" {
|
|
t.Errorf("Expected specific content, got '%s'", news.Content)
|
|
}
|
|
if news.Posted == 0 {
|
|
t.Error("Expected non-zero posted timestamp")
|
|
}
|
|
|
|
// Test finding non-existent news
|
|
_, err = Find(db, 999)
|
|
if err == nil {
|
|
t.Error("Expected error when finding non-existent news")
|
|
}
|
|
}
|
|
|
|
func TestAll(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
newsPosts, err := All(db)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get all news: %v", err)
|
|
}
|
|
|
|
if len(newsPosts) != 5 {
|
|
t.Errorf("Expected 5 news posts, got %d", len(newsPosts))
|
|
}
|
|
|
|
// Check ordering (newest first)
|
|
if len(newsPosts) >= 2 {
|
|
if newsPosts[0].Posted < newsPosts[1].Posted {
|
|
t.Error("Expected news to be ordered by posted time (newest first)")
|
|
}
|
|
}
|
|
|
|
// First post should be the most recent (1 hour ago)
|
|
if newsPosts[0].Content != "Fifth post - maintenance notice" {
|
|
t.Errorf("Expected newest post first, got '%s'", newsPosts[0].Content)
|
|
}
|
|
}
|
|
|
|
func TestByAuthor(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Test posts by author 1
|
|
author1Posts, err := ByAuthor(db, 1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get news by author: %v", err)
|
|
}
|
|
|
|
if len(author1Posts) != 2 {
|
|
t.Errorf("Expected 2 posts by author 1, got %d", len(author1Posts))
|
|
}
|
|
|
|
// Verify all posts are by author 1
|
|
for _, post := range author1Posts {
|
|
if post.Author != 1 {
|
|
t.Errorf("Expected author 1, got %d", post.Author)
|
|
}
|
|
}
|
|
|
|
// Check ordering (newest first)
|
|
if len(author1Posts) == 2 {
|
|
if author1Posts[0].Content != "Third post - recent update" {
|
|
t.Errorf("Expected newest post by author 1 first")
|
|
}
|
|
}
|
|
|
|
// Test author with no posts
|
|
noPosts, err := ByAuthor(db, 999)
|
|
if err != nil {
|
|
t.Fatalf("Failed to query non-existent author: %v", err)
|
|
}
|
|
|
|
if len(noPosts) != 0 {
|
|
t.Errorf("Expected 0 posts by non-existent author, got %d", len(noPosts))
|
|
}
|
|
}
|
|
|
|
func TestRecent(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Test getting 3 most recent posts
|
|
recentPosts, err := Recent(db, 3)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get recent news: %v", err)
|
|
}
|
|
|
|
if len(recentPosts) != 3 {
|
|
t.Errorf("Expected 3 recent posts, got %d", len(recentPosts))
|
|
}
|
|
|
|
// Check ordering (newest first)
|
|
if len(recentPosts) >= 2 {
|
|
if recentPosts[0].Posted < recentPosts[1].Posted {
|
|
t.Error("Expected recent posts to be ordered newest first")
|
|
}
|
|
}
|
|
|
|
// Test getting more posts than exist
|
|
allRecentPosts, err := Recent(db, 10)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get recent news with high limit: %v", err)
|
|
}
|
|
|
|
if len(allRecentPosts) != 5 {
|
|
t.Errorf("Expected 5 posts (all available), got %d", len(allRecentPosts))
|
|
}
|
|
}
|
|
|
|
func TestSince(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Test posts since 3 days ago
|
|
threeDaysAgo := time.Now().AddDate(0, 0, -3).Unix()
|
|
recentPosts, err := Since(db, threeDaysAgo)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get news since timestamp: %v", err)
|
|
}
|
|
|
|
// Should get posts from 2 days ago, 1 day ago, and 1 hour ago
|
|
expectedCount := 3
|
|
if len(recentPosts) != expectedCount {
|
|
t.Errorf("Expected %d posts since 3 days ago, got %d", expectedCount, len(recentPosts))
|
|
}
|
|
|
|
// Verify all posts are since the timestamp
|
|
for _, post := range recentPosts {
|
|
if post.Posted < threeDaysAgo {
|
|
t.Errorf("Post with timestamp %d is before the 'since' timestamp %d", post.Posted, threeDaysAgo)
|
|
}
|
|
}
|
|
|
|
// Test with future timestamp (should return no posts)
|
|
futurePosts, err := Since(db, time.Now().AddDate(0, 0, 1).Unix())
|
|
if err != nil {
|
|
t.Fatalf("Failed to query future timestamp: %v", err)
|
|
}
|
|
|
|
if len(futurePosts) != 0 {
|
|
t.Errorf("Expected 0 posts since future timestamp, got %d", len(futurePosts))
|
|
}
|
|
}
|
|
|
|
func TestBetween(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Test posts between 6 days ago and 1 day ago
|
|
start := time.Now().AddDate(0, 0, -6).Unix()
|
|
end := time.Now().AddDate(0, 0, -1).Unix()
|
|
|
|
betweenPosts, err := Between(db, start, end)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get news between timestamps: %v", err)
|
|
}
|
|
|
|
// Should get posts from 5 days ago, 2 days ago, and 1 day ago
|
|
expectedCount := 3
|
|
if len(betweenPosts) != expectedCount {
|
|
t.Errorf("Expected %d posts between timestamps, got %d", expectedCount, len(betweenPosts))
|
|
}
|
|
|
|
// Verify all posts are within the range
|
|
for _, post := range betweenPosts {
|
|
if post.Posted < start || post.Posted > end {
|
|
t.Errorf("Post with timestamp %d is outside range [%d, %d]", post.Posted, start, end)
|
|
}
|
|
}
|
|
|
|
// Test with narrow range (should return fewer posts)
|
|
narrowStart := time.Now().AddDate(0, 0, -2).Unix()
|
|
narrowEnd := time.Now().AddDate(0, 0, -1).Unix()
|
|
|
|
narrowPosts, err := Between(db, narrowStart, narrowEnd)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get news in narrow range: %v", err)
|
|
}
|
|
|
|
if len(narrowPosts) != 2 { // 2 days ago and 1 day ago
|
|
t.Errorf("Expected 2 posts in narrow range, got %d", len(narrowPosts))
|
|
}
|
|
}
|
|
|
|
func TestBuilder(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Create new news using builder
|
|
testTime := time.Now()
|
|
news, err := NewBuilder(db).
|
|
WithAuthor(5).
|
|
WithContent("Test news content from builder").
|
|
WithPostedTime(testTime).
|
|
Create()
|
|
|
|
if err != nil {
|
|
t.Fatalf("Failed to create news with builder: %v", err)
|
|
}
|
|
|
|
if news.ID == 0 {
|
|
t.Error("Expected non-zero ID after creation")
|
|
}
|
|
if news.Author != 5 {
|
|
t.Errorf("Expected author 5, got %d", news.Author)
|
|
}
|
|
if news.Content != "Test news content from builder" {
|
|
t.Errorf("Expected specific content, got '%s'", news.Content)
|
|
}
|
|
if news.Posted != testTime.Unix() {
|
|
t.Errorf("Expected posted time %d, got %d", testTime.Unix(), news.Posted)
|
|
}
|
|
|
|
// Verify it was saved to database
|
|
foundNews, err := Find(db, news.ID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to find created news: %v", err)
|
|
}
|
|
|
|
if foundNews.Content != "Test news content from builder" {
|
|
t.Errorf("Created news not found in database")
|
|
}
|
|
|
|
// Test builder with default timestamp
|
|
defaultNews, err := NewBuilder(db).
|
|
WithAuthor(1).
|
|
WithContent("News with default timestamp").
|
|
Create()
|
|
|
|
if err != nil {
|
|
t.Fatalf("Failed to create news with default timestamp: %v", err)
|
|
}
|
|
|
|
// Should have recent timestamp (within last minute)
|
|
if time.Since(defaultNews.PostedTime()) > time.Minute {
|
|
t.Error("Expected default timestamp to be recent")
|
|
}
|
|
}
|
|
|
|
func TestSave(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
news, err := Find(db, 1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to find news: %v", err)
|
|
}
|
|
|
|
// Modify news
|
|
news.Author = 999
|
|
news.Content = "Updated content"
|
|
news.Posted = time.Now().Unix()
|
|
|
|
// Save changes
|
|
err = news.Save()
|
|
if err != nil {
|
|
t.Fatalf("Failed to save news: %v", err)
|
|
}
|
|
|
|
// Verify changes were saved
|
|
updatedNews, err := Find(db, 1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to find updated news: %v", err)
|
|
}
|
|
|
|
if updatedNews.Author != 999 {
|
|
t.Errorf("Expected updated author 999, got %d", updatedNews.Author)
|
|
}
|
|
if updatedNews.Content != "Updated content" {
|
|
t.Errorf("Expected updated content, got '%s'", updatedNews.Content)
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
news, err := Find(db, 1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to find news: %v", err)
|
|
}
|
|
|
|
// Delete news
|
|
err = news.Delete()
|
|
if err != nil {
|
|
t.Fatalf("Failed to delete news: %v", err)
|
|
}
|
|
|
|
// Verify news was deleted
|
|
_, err = Find(db, 1)
|
|
if err == nil {
|
|
t.Error("Expected error when finding deleted news")
|
|
}
|
|
}
|
|
|
|
func TestUtilityMethods(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
news, _ := Find(db, 1)
|
|
|
|
// Test PostedTime
|
|
postedTime := news.PostedTime()
|
|
if postedTime.IsZero() {
|
|
t.Error("Expected non-zero posted time")
|
|
}
|
|
|
|
// Test IsRecent (should be false for old posts initially)
|
|
if news.IsRecent() {
|
|
t.Error("Expected old news post not to be recent")
|
|
}
|
|
|
|
// Test SetPostedTime
|
|
newTime := time.Now().Add(-2 * time.Hour)
|
|
news.SetPostedTime(newTime)
|
|
if news.Posted != newTime.Unix() {
|
|
t.Errorf("Expected posted timestamp %d, got %d", newTime.Unix(), news.Posted)
|
|
}
|
|
|
|
// Test IsRecent (should be true after setting to 2 hours ago)
|
|
if !news.IsRecent() {
|
|
t.Error("Expected news post from 2 hours ago to be recent")
|
|
}
|
|
|
|
// Create recent post
|
|
recentNews, _ := NewBuilder(db).
|
|
WithAuthor(1).
|
|
WithContent("Recent post").
|
|
Create()
|
|
|
|
if !recentNews.IsRecent() {
|
|
t.Error("Expected newly created post to be recent")
|
|
}
|
|
|
|
// Test Age
|
|
age := news.Age()
|
|
if age < 0 {
|
|
t.Error("Expected positive age")
|
|
}
|
|
|
|
// Test IsAuthor
|
|
if !news.IsAuthor(news.Author) {
|
|
t.Error("Expected IsAuthor to return true for correct author")
|
|
}
|
|
if news.IsAuthor(999) {
|
|
t.Error("Expected IsAuthor to return false for incorrect author")
|
|
}
|
|
|
|
// Test Preview
|
|
longContent := "This is a very long content that should be truncated when preview is called"
|
|
news.Content = longContent
|
|
|
|
preview := news.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 := news.Preview(100) // Longer than content
|
|
if shortPreview != longContent {
|
|
t.Error("Expected short content to not be truncated")
|
|
}
|
|
|
|
// Test WordCount
|
|
news.Content = "This is a test with five words"
|
|
wordCount := news.WordCount()
|
|
if wordCount != 7 {
|
|
t.Errorf("Expected 7 words, got %d", wordCount)
|
|
}
|
|
|
|
news.Content = ""
|
|
emptyWordCount := news.WordCount()
|
|
if emptyWordCount != 0 {
|
|
t.Errorf("Expected 0 words for empty content, got %d", emptyWordCount)
|
|
}
|
|
|
|
news.Content = "OneWord"
|
|
oneWordCount := news.WordCount()
|
|
if oneWordCount != 1 {
|
|
t.Errorf("Expected 1 word, got %d", oneWordCount)
|
|
}
|
|
}
|