Skip to content

Commit 632a220

Browse files
test(tray): expand ActivityStreamService coverage with 20 new tests
Add tests covering previously untested behaviour in ActivityStreamService: - Category defaults to "general" when null or whitespace input - Whitespace/empty title is rejected (no-op Add) - Insertion order (newest-first) - GetItems maxItems limiting and zero-items edge case - GetItems category filtering: exact match, dot-prefix sub-categories, case-insensitive matching, and non-match guard - 'all' category selector returns every item - Clear and Add both raise the Updated event - BuildSupportBundle maxItems parameter limits output - BuildSupportBundle includes session= and node= fields when set - BuildSupportBundle truncates long node IDs to 16 chars + '...' - BuildSupportBundle omits session/node fields when not provided Total: 22 tests (was 2). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ef6ac8a commit 632a220

1 file changed

Lines changed: 258 additions & 0 deletions

File tree

tests/OpenClaw.Tray.Tests/ActivityStreamServiceTests.cs

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,262 @@ public void BuildSupportBundle_DefaultIncludesStoredActivityWindow()
5151
Assert.Contains("bundle-item-399", bundle);
5252
Assert.Contains("bundle-item-000", bundle);
5353
}
54+
55+
// ── Add: field defaults ──
56+
57+
[Fact]
58+
public void Add_WithAllOptionalFields_StoresFieldValues()
59+
{
60+
ActivityStreamService.Add(
61+
"sessions",
62+
"My Session",
63+
details: "some detail",
64+
icon: "🔥",
65+
dashboardPath: "/dashboard/abc",
66+
sessionKey: "sk-123",
67+
nodeId: "node-xyz");
68+
69+
var items = ActivityStreamService.GetItems();
70+
71+
Assert.Single(items);
72+
var item = items[0];
73+
Assert.Equal("sessions", item.Category);
74+
Assert.Equal("My Session", item.Title);
75+
Assert.Equal("some detail", item.Details);
76+
Assert.Equal("🔥", item.Icon);
77+
Assert.Equal("/dashboard/abc", item.DashboardPath);
78+
Assert.Equal("sk-123", item.SessionKey);
79+
Assert.Equal("node-xyz", item.NodeId);
80+
}
81+
82+
[Fact]
83+
public void Add_WithNullCategory_DefaultsToGeneral()
84+
{
85+
ActivityStreamService.Add(null!, "Title");
86+
87+
var items = ActivityStreamService.GetItems();
88+
89+
Assert.Equal("general", items[0].Category);
90+
}
91+
92+
[Fact]
93+
public void Add_WithWhitespaceCategory_DefaultsToGeneral()
94+
{
95+
ActivityStreamService.Add(" ", "Title");
96+
97+
var items = ActivityStreamService.GetItems();
98+
99+
Assert.Equal("general", items[0].Category);
100+
}
101+
102+
[Fact]
103+
public void Add_WithEmptyTitle_DoesNotAddItem()
104+
{
105+
ActivityStreamService.Add("test", "");
106+
107+
var items = ActivityStreamService.GetItems();
108+
109+
Assert.Empty(items);
110+
}
111+
112+
[Fact]
113+
public void Add_WithWhitespaceTitle_DoesNotAddItem()
114+
{
115+
ActivityStreamService.Add("test", " ");
116+
117+
var items = ActivityStreamService.GetItems();
118+
119+
Assert.Empty(items);
120+
}
121+
122+
[Fact]
123+
public void Add_StoresItemsNewestFirst()
124+
{
125+
ActivityStreamService.Add("test", "first");
126+
ActivityStreamService.Add("test", "second");
127+
128+
var items = ActivityStreamService.GetItems();
129+
130+
Assert.Equal("second", items[0].Title);
131+
Assert.Equal("first", items[1].Title);
132+
}
133+
134+
// ── GetItems: filtering and limiting ──
135+
136+
[Fact]
137+
public void GetItems_WithMaxItemsLessThanStored_ReturnsOnlyNewest()
138+
{
139+
ActivityStreamService.Add("test", "old");
140+
ActivityStreamService.Add("test", "newer");
141+
ActivityStreamService.Add("test", "newest");
142+
143+
var items = ActivityStreamService.GetItems(maxItems: 2);
144+
145+
Assert.Equal(2, items.Count);
146+
Assert.Equal("newest", items[0].Title);
147+
Assert.Equal("newer", items[1].Title);
148+
}
149+
150+
[Fact]
151+
public void GetItems_WithMaxItemsZero_ReturnsEmpty()
152+
{
153+
ActivityStreamService.Add("test", "item");
154+
155+
var items = ActivityStreamService.GetItems(maxItems: 0);
156+
157+
Assert.Empty(items);
158+
}
159+
160+
[Fact]
161+
public void GetItems_WithCategoryFilter_ReturnsOnlyMatchingCategory()
162+
{
163+
ActivityStreamService.Add("sessions", "session item");
164+
ActivityStreamService.Add("nodes", "node item");
165+
ActivityStreamService.Add("sessions", "another session");
166+
167+
var items = ActivityStreamService.GetItems(category: "sessions");
168+
169+
Assert.Equal(2, items.Count);
170+
Assert.All(items, item => Assert.Equal("sessions", item.Category));
171+
}
172+
173+
[Fact]
174+
public void GetItems_WithCategoryAll_ReturnsEverything()
175+
{
176+
ActivityStreamService.Add("sessions", "s");
177+
ActivityStreamService.Add("nodes", "n");
178+
179+
var items = ActivityStreamService.GetItems(category: "all");
180+
181+
Assert.Equal(2, items.Count);
182+
}
183+
184+
[Fact]
185+
public void GetItems_CategoryFilter_IsCaseInsensitive()
186+
{
187+
ActivityStreamService.Add("Sessions", "uppercase S");
188+
189+
var items = ActivityStreamService.GetItems(category: "sessions");
190+
191+
Assert.Single(items);
192+
}
193+
194+
[Fact]
195+
public void GetItems_CategoryFilter_MatchesDotPrefixSubcategories()
196+
{
197+
ActivityStreamService.Add("sessions.chat", "chat session");
198+
ActivityStreamService.Add("sessions.mcp", "mcp session");
199+
ActivityStreamService.Add("nodes", "unrelated");
200+
201+
var items = ActivityStreamService.GetItems(category: "sessions");
202+
203+
Assert.Equal(2, items.Count);
204+
Assert.DoesNotContain(items, item => item.Category == "nodes");
205+
}
206+
207+
[Fact]
208+
public void GetItems_CategoryFilter_DoesNotMatchPartialPrefix()
209+
{
210+
ActivityStreamService.Add("sessionsExtra", "should not match");
211+
ActivityStreamService.Add("sessions.sub", "should match");
212+
213+
var items = ActivityStreamService.GetItems(category: "sessions");
214+
215+
Assert.Single(items);
216+
Assert.Equal("sessions.sub", items[0].Category);
217+
}
218+
219+
// ── Clear ──
220+
221+
[Fact]
222+
public void Clear_RemovesAllItems()
223+
{
224+
ActivityStreamService.Add("test", "item1");
225+
ActivityStreamService.Add("test", "item2");
226+
227+
ActivityStreamService.Clear();
228+
229+
Assert.Empty(ActivityStreamService.GetItems());
230+
}
231+
232+
[Fact]
233+
public void Clear_RaisesUpdatedEvent()
234+
{
235+
var raised = false;
236+
EventHandler handler = (_, _) => raised = true;
237+
ActivityStreamService.Updated += handler;
238+
try
239+
{
240+
ActivityStreamService.Clear();
241+
Assert.True(raised);
242+
}
243+
finally
244+
{
245+
ActivityStreamService.Updated -= handler;
246+
}
247+
}
248+
249+
[Fact]
250+
public void Add_RaisesUpdatedEvent()
251+
{
252+
var raised = false;
253+
EventHandler handler = (_, _) => raised = true;
254+
ActivityStreamService.Updated += handler;
255+
try
256+
{
257+
ActivityStreamService.Add("test", "item");
258+
Assert.True(raised);
259+
}
260+
finally
261+
{
262+
ActivityStreamService.Updated -= handler;
263+
}
264+
}
265+
266+
// ── BuildSupportBundle ──
267+
268+
[Fact]
269+
public void BuildSupportBundle_WithMaxItemsParam_LimitsOutput()
270+
{
271+
for (var i = 0; i < 10; i++)
272+
ActivityStreamService.Add("test", $"item-{i}");
273+
274+
var bundle = ActivityStreamService.BuildSupportBundle(maxItems: 3);
275+
276+
Assert.Contains("Items: 3", bundle);
277+
}
278+
279+
[Fact]
280+
public void BuildSupportBundle_IncludesSessionKeyAndNodeIdWhenPresent()
281+
{
282+
ActivityStreamService.Add("test", "title", sessionKey: "sk-abc", nodeId: "nd-xyz");
283+
284+
var bundle = ActivityStreamService.BuildSupportBundle();
285+
286+
Assert.Contains("session=sk-abc", bundle);
287+
Assert.Contains("node=nd-xyz", bundle);
288+
}
289+
290+
[Fact]
291+
public void BuildSupportBundle_TruncatesLongNodeId()
292+
{
293+
var longNodeId = new string('a', 32);
294+
ActivityStreamService.Add("test", "title", nodeId: longNodeId);
295+
296+
var bundle = ActivityStreamService.BuildSupportBundle();
297+
298+
Assert.Contains("node=" + new string('a', 16) + "...", bundle);
299+
Assert.DoesNotContain("node=" + longNodeId, bundle);
300+
}
301+
302+
[Fact]
303+
public void BuildSupportBundle_OmitsSessionAndNodeFieldsWhenAbsent()
304+
{
305+
ActivityStreamService.Add("test", "plain item");
306+
307+
var bundle = ActivityStreamService.BuildSupportBundle();
308+
309+
Assert.DoesNotContain("session=", bundle);
310+
Assert.DoesNotContain("node=", bundle);
311+
}
54312
}

0 commit comments

Comments
 (0)