internal/aggregator/article_test.go (view raw)
1package aggregator
2
3import (
4 "strings"
5 "testing"
6 "time"
7)
8
9func TestArticleToMap(t *testing.T) {
10 tests := []struct {
11 name string
12 article *Article
13 checkFn func(map[string]any) bool
14 }{
15 {
16 name: "full article with all fields",
17 article: &Article{
18 Title: "Test Article Title",
19 Link: "https://example.com/article",
20 Content: "This is the article content",
21 Published: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
22 Author: "John Doe",
23 SourceFeed: "Test Feed",
24 Summary: "This is a summary",
25 },
26 checkFn: func(m map[string]any) bool {
27 return m["title"] == "Test Article Title" &&
28 m["link"] == "https://example.com/article" &&
29 m["author"] == "John Doe" &&
30 m["source_feed"] == "Test Feed" &&
31 m["summary"] == "This is a summary" &&
32 strings.Contains(m["content"].(string), "article content")
33 },
34 },
35 {
36 name: "article with truncated content",
37 article: &Article{
38 Title: "Long Article",
39 Link: "https://example.com/long",
40 Content: strings.Repeat("x", 600), // More than 500 chars
41 },
42 checkFn: func(m map[string]any) bool {
43 content := m["content"].(string)
44 // 600 chars > 500, so truncated to 500 + "... [truncated]" (15 chars) = 515
45 return len(content) == 515 &&
46 strings.HasSuffix(content, "... [truncated]")
47 },
48 },
49 {
50 name: "article with zero time",
51 article: &Article{
52 Title: "No Date Article",
53 Link: "https://example.com/nodate",
54 Content: "Content",
55 Published: time.Time{}, // Zero time
56 },
57 checkFn: func(m map[string]any) bool {
58 _, hasPublished := m["published"]
59 return !hasPublished
60 },
61 },
62 {
63 name: "article without author",
64 article: &Article{
65 Title: "Anonymous Article",
66 Link: "https://example.com/anon",
67 Content: "Content",
68 Author: "",
69 SourceFeed: "Feed",
70 },
71 checkFn: func(m map[string]any) bool {
72 return m["author"] == ""
73 },
74 },
75 }
76
77 for _, tt := range tests {
78 t.Run(tt.name, func(t *testing.T) {
79 result := tt.article.ToMap()
80 if !tt.checkFn(result) {
81 t.Errorf("ToMap() produced unexpected result: %v", result)
82 }
83 })
84 }
85}
86
87func TestArticleStruct(t *testing.T) {
88 t.Run("empty article", func(t *testing.T) {
89 article := &Article{}
90 m := article.ToMap()
91
92 if m["title"] != "" {
93 t.Errorf("Expected empty title, got %q", m["title"])
94 }
95 if m["link"] != "" {
96 t.Errorf("Expected empty link, got %q", m["link"])
97 }
98 })
99
100 t.Run("article with only title and link", func(t *testing.T) {
101 article := &Article{
102 Title: "Minimal Article",
103 Link: "https://example.com/minimal",
104 }
105 m := article.ToMap()
106
107 if m["title"] != "Minimal Article" {
108 t.Errorf("Expected title 'Minimal Article', got %q", m["title"])
109 }
110 if m["link"] != "https://example.com/minimal" {
111 t.Errorf("Expected link 'https://example.com/minimal', got %q", m["link"])
112 }
113 })
114
115 t.Run("article with future date", func(t *testing.T) {
116 futureDate := time.Now().Add(24 * time.Hour)
117 article := &Article{
118 Title: "Future Article",
119 Link: "https://example.com/future",
120 Content: "Content",
121 Published: futureDate,
122 }
123 m := article.ToMap()
124
125 if published, ok := m["published"].(string); !ok {
126 t.Error("Expected published field to be set")
127 } else if !strings.Contains(published, futureDate.Format("2006-01-02")) {
128 t.Errorf("Expected published date %s, got %s", futureDate.Format(time.RFC3339), published)
129 }
130 })
131}