@@ -3,40 +3,33 @@ package stats
33import (
44 "bytes"
55 "encoding/json"
6+ "fmt"
67 "net/http"
78 "net/http/httptest"
8- "path/filepath"
99 "testing"
1010 "time"
1111
12- "github.com/AdguardTeam/AdGuardHome/internal/agh"
1312 "github.com/AdguardTeam/AdGuardHome/internal/aghalg"
14- "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
15- "github.com/AdguardTeam/golibs/logutil/slogutil"
13+ "github.com/AdguardTeam/golibs/netutil"
1614 "github.com/AdguardTeam/golibs/testutil"
1715 "github.com/AdguardTeam/golibs/timeutil"
1816 "github.com/stretchr/testify/assert"
1917 "github.com/stretchr/testify/require"
2018)
2119
20+ // Common domain values for tests.
21+ const (
22+ TestDomain1 = "example.com"
23+ TestDomain2 = "example.org"
24+ )
25+
2226func TestHandleStatsConfig (t * testing.T ) {
2327 const (
2428 smallIvl = 1 * time .Minute
2529 minIvl = 1 * time .Hour
2630 maxIvl = 365 * timeutil .Day
2731 )
2832
29- conf := Config {
30- Logger : slogutil .NewDiscardLogger (),
31- UnitID : func () (id uint32 ) { return 0 },
32- ConfigModifier : agh.EmptyConfigModifier {},
33- ShouldCountClient : func ([]string ) bool { return true },
34- HTTPReg : aghhttp.EmptyRegistrar {},
35- Filename : filepath .Join (t .TempDir (), "stats.db" ),
36- Limit : time .Hour * 24 ,
37- Enabled : true ,
38- }
39-
4033 testCases := []struct {
4134 name string
4235 wantErr string
@@ -98,8 +91,7 @@ func TestHandleStatsConfig(t *testing.T) {
9891
9992 for _ , tc := range testCases {
10093 t .Run (tc .name , func (t * testing.T ) {
101- s , err := New (conf )
102- require .NoError (t , err )
94+ s := newTestStatsCtx (t , Config {Enabled : true })
10395
10496 s .Start ()
10597 testutil .CleanupAndRequireSuccess (t , s .Close )
@@ -138,3 +130,112 @@ func TestHandleStatsConfig(t *testing.T) {
138130 })
139131 }
140132}
133+
134+ // populateTestData is a helper that creates test entries in db. s must not be
135+ // nil.
136+ func populateTestData (tb testing.TB , s * StatsCtx ) {
137+ tb .Helper ()
138+
139+ oldUnitID := newUnitID () - 1
140+ oldUnit := & unitDB {
141+ NResult : make ([]uint64 , resultLast ),
142+ Domains : []countPair {{Name : TestDomain1 , Count : 1 }},
143+ NTotal : 1 ,
144+ }
145+
146+ db := s .db .Load ()
147+ tx , err := db .Begin (true )
148+ require .NoError (tb , err )
149+
150+ err = s .flushUnitToDB (oldUnit , tx , uint32 (oldUnitID ))
151+ require .NoError (tb , err )
152+
153+ err = finishTxn (tx , true )
154+ require .NoError (tb , err )
155+
156+ s .Update (& Entry {
157+ Client : netutil .IPv4Localhost ().String (),
158+ Domain : TestDomain2 ,
159+ ProcessingTime : 3 * time .Minute ,
160+ Result : RNotFiltered ,
161+ })
162+ }
163+
164+ func TestStatsCtx_handleStats (t * testing.T ) {
165+ testCases := []struct {
166+ name string
167+ wantErr string
168+ wantTopQueriedDomains []topAddrs
169+ wantDNSQueries uint64
170+ wantCode int
171+ recent int64
172+ }{{
173+ name : "short_interval" ,
174+ wantErr : "recent: out of range: must be no less than 3600000, got 240000\n " ,
175+ wantCode : http .StatusBadRequest ,
176+ recent : 4 * time .Minute .Milliseconds (),
177+ }, {
178+ name : "long_interval" ,
179+ wantErr : "recent: out of range: must be no greater than 86400000, got 259200000\n " ,
180+ wantCode : http .StatusBadRequest ,
181+ recent : 72 * time .Hour .Milliseconds (),
182+ }, {
183+ name : "interval_is_not_multiple_of_hour" ,
184+ wantErr : "recent: must be a multiple of 1 hour\n " ,
185+ wantCode : http .StatusBadRequest ,
186+ recent : time .Hour .Milliseconds () + 1 ,
187+ }, {
188+ name : "no_interval" ,
189+ wantCode : http .StatusOK ,
190+ wantDNSQueries : 2 ,
191+ wantTopQueriedDomains : []topAddrs {{
192+ TestDomain1 : 1 ,
193+ }, {
194+ TestDomain2 : 1 ,
195+ }},
196+ }, {
197+ name : "valid_interval" ,
198+ wantCode : http .StatusOK ,
199+ wantDNSQueries : 1 ,
200+ wantTopQueriedDomains : []topAddrs {{
201+ TestDomain2 : 1 ,
202+ }},
203+ recent : time .Hour .Milliseconds (),
204+ }}
205+
206+ s := newTestStatsCtx (t , Config {
207+ Enabled : true ,
208+ })
209+
210+ s .Start ()
211+ defer testutil .CleanupAndRequireSuccess (t , s .Close )
212+
213+ populateTestData (t , s )
214+ for _ , tc := range testCases {
215+ t .Run (tc .name , func (t * testing.T ) {
216+ url := "/control/stats"
217+ if tc .recent != 0 {
218+ url += fmt .Sprintf ("?recent=%d" , tc .recent )
219+ }
220+
221+ req := httptest .NewRequest (http .MethodGet , url , nil )
222+ rw := httptest .NewRecorder ()
223+
224+ s .handleStats (rw , req )
225+ require .Equal (t , tc .wantCode , rw .Code )
226+
227+ if rw .Code != http .StatusOK {
228+ require .Equal (t , tc .wantErr , rw .Body .String ())
229+
230+ return
231+ }
232+
233+ ans := StatsResp {}
234+ err := json .Unmarshal (rw .Body .Bytes (), & ans )
235+ require .NoError (t , err )
236+
237+ assert .Equal (t , tc .wantDNSQueries , ans .NumDNSQueries )
238+ assert .ElementsMatch (t , tc .wantTopQueriedDomains , ans .TopQueried )
239+ })
240+ }
241+ }
0 commit comments