Skip to content

Commit 1fa9194

Browse files
committed
fix: walk full window when telemetry events --types is set
The archive endpoint filters only by category server-side, so --types is applied client-side after a page is fetched. In default (non --all) mode only the first page was read, silently dropping matching events on later pages. Route through the auto-pager whenever a type filter is active so results are complete.
1 parent 0e4cc08 commit 1fa9194

4 files changed

Lines changed: 26 additions & 14 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ Per-category updates are partial — only categories you name are changed; other
325325
- `--limit <n>` - Max events per page (1-100, default 20)
326326
- `--since <t>` / `--until <t>` - Window bounds as an RFC-3339 timestamp or a duration like `5m` (default window: last 5m)
327327
- `--categories <list>` - Filter by event category (`console`, `network`, `page`, `interaction`, `control`, `connection`, `system`, `screenshot`, `captcha`, `monitor`)
328-
- `--types <list>` - Filter by event type (e.g. `network_response`, `console_error`)
328+
- `--types <list>` - Filter by event type (e.g. `network_response`, `console_error`); filtered client-side, so this walks every page in the window for complete results
329329
- `--all` - Fetch every page in the window instead of just the first
330330
- `-o, --output json` - Output newline-delimited JSON envelopes
331331
- Default output: tab-separated `<time>\t<seq>\t[<category>]\t<type>`

cmd/browsers.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2755,15 +2755,16 @@ followed automatically by Chromium.`,
27552755
Short: "Read recorded telemetry events",
27562756
Long: "Read recorded telemetry events for a browser session, in ascending sequence order.\n\n" +
27572757
"By default this returns the first page of events in the window (most recent window is the\n" +
2758-
"last 5m; adjust with --since/--until). Use --all to walk every page and dump the full archive.",
2758+
"last 5m; adjust with --since/--until). Use --all to walk every page and dump the full archive.\n\n" +
2759+
"--types filters client-side, so it walks every page in the window (like --all) to return complete results.",
27592760
Args: cobra.ExactArgs(1),
27602761
RunE: runBrowsersTelemetryEvents,
27612762
}
27622763
telemetryEvents.Flags().Int64("limit", 0, "Max events per page (1-100, default 20)")
27632764
telemetryEvents.Flags().String("since", "", "Window start: RFC-3339 timestamp or duration like 5m (default 5m)")
27642765
telemetryEvents.Flags().String("until", "", "Window end (exclusive): RFC-3339 timestamp or duration like 5m")
27652766
telemetryEvents.Flags().StringSlice("categories", []string{}, "Filter by event category (console,network,page,interaction,control,connection,system,screenshot,captcha,monitor)")
2766-
telemetryEvents.Flags().StringSlice("types", []string{}, "Filter by event type (e.g. network_response,console_error)")
2767+
telemetryEvents.Flags().StringSlice("types", []string{}, "Filter by event type (e.g. network_response,console_error); walks every page in the window")
27672768
telemetryEvents.Flags().Bool("all", false, "Fetch every page (the full archive in the window) instead of just the first")
27682769
telemetryEvents.Flags().StringP("output", "o", "", "Output format: json for newline-delimited JSON envelopes")
27692770
telemetryRoot.AddCommand(telemetryEvents)

cmd/browsers_telemetry.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,11 @@ func (b BrowsersCmd) TelemetryEvents(ctx context.Context, in BrowsersTelemetryEv
302302
return nil
303303
}
304304

305-
if in.All {
305+
// A --types filter is client-side (the archive endpoint filters only by
306+
// category), so it must see every page to be complete: fetching a single page
307+
// could drop matching events on later pages. Walk the whole window whenever a
308+
// type filter is active, the same as --all.
309+
if in.All || len(in.Types) > 0 {
306310
pager := b.telemetry.EventsAutoPaging(ctx, sessionID, params, opts...)
307311
for pager.Next() {
308312
if err := emit(pager.Current()); err != nil {

cmd/browsers_telemetry_test.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (f *FakeBrowserTelemetryService) EventsAutoPaging(ctx context.Context, id s
5858
if f.EventsAutoPagingFunc != nil {
5959
return f.EventsAutoPagingFunc()
6060
}
61-
return nil
61+
return pagination.NewOffsetPaginationAutoPager(&pagination.OffsetPagination[kernel.BrowserTelemetryEventsResponse]{}, nil)
6262
}
6363

6464
func telemetryEventsPage(t *testing.T, raws ...string) *pagination.OffsetPagination[kernel.BrowserTelemetryEventsResponse] {
@@ -169,17 +169,25 @@ func TestTelemetryEvents_InvalidLimitErrors(t *testing.T) {
169169
}
170170
}
171171

172-
func TestTelemetryEvents_TypesFilterDropsNonMatching(t *testing.T) {
172+
// A --types filter is client-side, so it must scan every page in the window to be
173+
// complete. Setting --types (without --all) must therefore route through the
174+
// auto-pager, not the single-page fetch that could drop matches on later pages.
175+
func TestTelemetryEvents_TypesFilterWalksAllPages(t *testing.T) {
173176
setupStdoutCapture(t)
174177
fakeBrowsers := &FakeBrowsersService{GetFunc: func(ctx context.Context, id string, query kernel.BrowserGetParams, opts ...option.RequestOption) (*kernel.BrowserGetResponse, error) {
175178
return &kernel.BrowserGetResponse{SessionID: id}, nil
176179
}}
177-
fakeTelemetry := &FakeBrowserTelemetryService{EventsFunc: func(id string, query kernel.BrowserTelemetryEventsParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.BrowserTelemetryEventsResponse], error) {
178-
return telemetryEventsPage(t,
179-
`{"event":{"type":"network_request","category":"network","ts":1000000},"seq":1}`,
180-
`{"event":{"type":"network_response","category":"network","ts":2000000},"seq":2}`,
181-
), nil
182-
}}
180+
autoPaged := false
181+
fakeTelemetry := &FakeBrowserTelemetryService{
182+
EventsFunc: func(id string, query kernel.BrowserTelemetryEventsParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.BrowserTelemetryEventsResponse], error) {
183+
t.Fatalf("single-page Events must not be called when --types is set")
184+
return nil, nil
185+
},
186+
EventsAutoPagingFunc: func() *pagination.OffsetPaginationAutoPager[kernel.BrowserTelemetryEventsResponse] {
187+
autoPaged = true
188+
return pagination.NewOffsetPaginationAutoPager(&pagination.OffsetPagination[kernel.BrowserTelemetryEventsResponse]{}, nil)
189+
},
190+
}
183191
b := BrowsersCmd{browsers: fakeBrowsers, telemetry: fakeTelemetry}
184192

185193
err := b.TelemetryEvents(context.Background(), BrowsersTelemetryEventsInput{
@@ -188,8 +196,7 @@ func TestTelemetryEvents_TypesFilterDropsNonMatching(t *testing.T) {
188196
})
189197

190198
assert.NoError(t, err)
191-
assert.Contains(t, outBuf.String(), "network_response")
192-
assert.NotContains(t, outBuf.String(), "network_request")
199+
assert.True(t, autoPaged, "--types must walk every page so the client-side filter is complete")
193200
}
194201

195202
func TestTelemetryEvents_FallsBackToIdentifierWhenGetFails(t *testing.T) {

0 commit comments

Comments
 (0)