@@ -23,7 +23,7 @@ func Test200_Success(t *testing.T) {
2323 h .SetupAnalytics (workspace .ID )
2424 rootKey := h .CreateRootKey (workspace .ID , "api.*.read_analytics" )
2525
26- now := h . Clock .Now ().UnixMilli ()
26+ now := time .Now ().UnixMilli ()
2727
2828 // Buffer some key verifications
2929 for i := range 5 {
@@ -61,7 +61,7 @@ func Test200_Success(t *testing.T) {
6161 }
6262
6363 // Wait for buffered data to be available
64- time .Sleep (2 * time .Second )
64+ time .Sleep (5 * time .Second )
6565
6666 res := testutil .CallRoute [Request , Response ](h , route , headers , req )
6767 t .Logf ("Status: %d, RawBody: %s" , res .Status , res .RawBody )
@@ -85,7 +85,7 @@ func Test200_PermissionFiltersByApiId(t *testing.T) {
8585 // Create root key with permission ONLY for api1
8686 rootKey := h .CreateRootKey (workspace .ID , "api." + api1 .ID + ".read_analytics" )
8787
88- now := h . Clock .Now ().UnixMilli ()
88+ now := time .Now ().UnixMilli ()
8989
9090 // Buffer verifications for api1
9191 for i := range 3 {
@@ -166,7 +166,7 @@ func Test200_PermissionFiltersByKeySpaceId(t *testing.T) {
166166 // Create root key with permission ONLY for api1
167167 rootKey := h .CreateRootKey (workspace .ID , "api." + api1 .ID + ".read_analytics" )
168168
169- now := h . Clock .Now ().UnixMilli ()
169+ now := time .Now ().UnixMilli ()
170170
171171 // Buffer verifications for api1
172172 for i := range 3 {
@@ -239,3 +239,240 @@ func Test200_PermissionFiltersByKeySpaceId(t *testing.T) {
239239 require .Equal (c , float64 (3 ), count )
240240 }, 30 * time .Second , time .Second )
241241}
242+ func Test200_QueryWithin30DaysRetention (t * testing.T ) {
243+ h := testutil .NewHarness (t )
244+
245+ workspace := h .CreateWorkspace ()
246+ api := h .CreateApi (seed.CreateApiRequest {
247+ WorkspaceID : workspace .ID ,
248+ })
249+ h .SetupAnalytics (workspace .ID )
250+ rootKey := h .CreateRootKey (workspace .ID , "api.*.read_analytics" )
251+
252+ now := time .Now ().UnixMilli ()
253+
254+ // Buffer verification from 7 days ago (within 30-day retention)
255+ h .ClickHouse .BufferKeyVerification (schema.KeyVerification {
256+ RequestID : uid .New (uid .RequestPrefix ),
257+ Time : now - (7 * 24 * 60 * 60 * 1000 ), // 7 days ago
258+ WorkspaceID : workspace .ID ,
259+ KeySpaceID : api .KeyAuthID .String ,
260+ KeyID : uid .New (uid .KeyPrefix ),
261+ Region : "us-west-1" ,
262+ Outcome : "VALID" ,
263+ IdentityID : "" ,
264+ Tags : []string {},
265+ })
266+
267+ route := & Handler {
268+ Logger : h .Logger ,
269+ DB : h .DB ,
270+ Keys : h .Keys ,
271+ ClickHouse : h .ClickHouse ,
272+ AnalyticsConnectionManager : h .AnalyticsConnectionManager ,
273+ Caches : h .Caches ,
274+ }
275+ h .Register (route )
276+
277+ headers := http.Header {
278+ "Authorization" : []string {"Bearer " + rootKey },
279+ "Content-Type" : []string {"application/json" },
280+ }
281+
282+ // Query last 7 days (within 30-day retention)
283+ req := Request {
284+ Query : "SELECT COUNT(*) as count FROM key_verifications_v1 WHERE time >= now() - INTERVAL 7 DAY" ,
285+ }
286+
287+ time .Sleep (5 * time .Second ) // Wait for data
288+
289+ res := testutil .CallRoute [Request , Response ](h , route , headers , req )
290+ require .Equal (t , 200 , res .Status , "Query within retention should succeed" )
291+ require .NotNil (t , res .Body )
292+ }
293+
294+ func Test200_QueryAtExact30DayRetentionLimit (t * testing.T ) {
295+ h := testutil .NewHarness (t )
296+
297+ workspace := h .CreateWorkspace ()
298+ h .SetupAnalytics (workspace .ID )
299+ rootKey := h .CreateRootKey (workspace .ID , "api.*.read_analytics" )
300+
301+ route := & Handler {
302+ Logger : h .Logger ,
303+ DB : h .DB ,
304+ Keys : h .Keys ,
305+ ClickHouse : h .ClickHouse ,
306+ AnalyticsConnectionManager : h .AnalyticsConnectionManager ,
307+ Caches : h .Caches ,
308+ }
309+ h .Register (route )
310+
311+ headers := http.Header {
312+ "Authorization" : []string {"Bearer " + rootKey },
313+ "Content-Type" : []string {"application/json" },
314+ }
315+
316+ // Query exactly 30 days (at retention limit)
317+ req := Request {
318+ Query : "SELECT COUNT(*) as count FROM key_verifications_v1 WHERE time >= now() - INTERVAL 30 DAY" ,
319+ }
320+
321+ res := testutil .CallRoute [Request , Response ](h , route , headers , req )
322+ require .Equal (t , 200 , res .Status , "Query at retention limit should succeed" )
323+ require .NotNil (t , res .Body )
324+ }
325+
326+ func Test200_QueryWithCustomRetention90Days (t * testing.T ) {
327+ h := testutil .NewHarness (t )
328+
329+ workspace := h .CreateWorkspace ()
330+ h .SetupAnalytics (workspace .ID , testutil .WithRetentionDays (90 )) // 90-day retention
331+ rootKey := h .CreateRootKey (workspace .ID , "api.*.read_analytics" )
332+
333+ route := & Handler {
334+ Logger : h .Logger ,
335+ DB : h .DB ,
336+ Keys : h .Keys ,
337+ ClickHouse : h .ClickHouse ,
338+ AnalyticsConnectionManager : h .AnalyticsConnectionManager ,
339+ Caches : h .Caches ,
340+ }
341+ h .Register (route )
342+
343+ headers := http.Header {
344+ "Authorization" : []string {"Bearer " + rootKey },
345+ "Content-Type" : []string {"application/json" },
346+ }
347+
348+ // Query 60 days (within 90-day retention)
349+ req := Request {
350+ Query : "SELECT COUNT(*) as count FROM key_verifications_v1 WHERE time >= now() - INTERVAL 60 DAY" ,
351+ }
352+
353+ res := testutil .CallRoute [Request , Response ](h , route , headers , req )
354+ require .Equal (t , 200 , res .Status , "Query within custom retention should succeed" )
355+ require .NotNil (t , res .Body )
356+ }
357+
358+ func Test200_RLSWorkspaceIsolation (t * testing.T ) {
359+ h := testutil .NewHarness (t )
360+
361+ // Create two separate workspaces
362+ workspace1 := h .CreateWorkspace ()
363+ workspace2 := h .CreateWorkspace ()
364+
365+ api1 := h .CreateApi (seed.CreateApiRequest {
366+ WorkspaceID : workspace1 .ID ,
367+ })
368+ api2 := h .CreateApi (seed.CreateApiRequest {
369+ WorkspaceID : workspace2 .ID ,
370+ })
371+
372+ // Setup analytics for both workspaces
373+ h .SetupAnalytics (workspace1 .ID )
374+ h .SetupAnalytics (workspace2 .ID )
375+
376+ rootKey1 := h .CreateRootKey (workspace1 .ID , "api.*.read_analytics" )
377+
378+ // Use actual current time for analytics data since ClickHouse's now() uses real time, not mock clock
379+ now := time .Now ().UnixMilli ()
380+
381+ // Buffer data for workspace 1
382+ for i := range 5 {
383+ h .ClickHouse .BufferKeyVerification (schema.KeyVerification {
384+ RequestID : uid .New (uid .RequestPrefix ),
385+ Time : now - int64 (i * 1000 ),
386+ WorkspaceID : workspace1 .ID ,
387+ KeySpaceID : api1 .KeyAuthID .String ,
388+ KeyID : uid .New (uid .KeyPrefix ),
389+ Region : "us-west-1" ,
390+ Outcome : "VALID" ,
391+ IdentityID : "" ,
392+ Tags : []string {},
393+ })
394+ }
395+
396+ // Buffer data for workspace 2 (should NOT be accessible by workspace1's key)
397+ for i := range 10 {
398+ h .ClickHouse .BufferKeyVerification (schema.KeyVerification {
399+ RequestID : uid .New (uid .RequestPrefix ),
400+ Time : now - int64 (i * 1000 ),
401+ WorkspaceID : workspace2 .ID ,
402+ KeySpaceID : api2 .KeyAuthID .String ,
403+ KeyID : uid .New (uid .KeyPrefix ),
404+ Region : "us-east-1" ,
405+ Outcome : "VALID" ,
406+ IdentityID : "" ,
407+ Tags : []string {},
408+ })
409+ }
410+
411+ route := & Handler {
412+ Logger : h .Logger ,
413+ DB : h .DB ,
414+ Keys : h .Keys ,
415+ ClickHouse : h .ClickHouse ,
416+ AnalyticsConnectionManager : h .AnalyticsConnectionManager ,
417+ Caches : h .Caches ,
418+ }
419+ h .Register (route )
420+
421+ headers := http.Header {
422+ "Authorization" : []string {"Bearer " + rootKey1 },
423+ "Content-Type" : []string {"application/json" },
424+ }
425+
426+ // Query all verifications - should only return workspace1's data due to RLS
427+ req := Request {
428+ Query : "SELECT COUNT(*) as count FROM key_verifications_v1 WHERE time >= now() - INTERVAL 1 DAY" ,
429+ }
430+
431+ time .Sleep (10 * time .Second ) // Wait for data to be flushed to ClickHouse
432+
433+ res := testutil .CallRoute [Request , Response ](h , route , headers , req )
434+ require .Equal (t , 200 , res .Status )
435+ require .NotNil (t , res .Body )
436+ require .Len (t , res .Body .Data , 1 )
437+
438+ // Verify only workspace1's data is returned (5 verifications), not workspace2's (10)
439+ count , ok := res .Body .Data [0 ]["count" ]
440+ require .True (t , ok )
441+ require .Equal (t , float64 (5 ), count , "RLS should filter to only workspace1's data" )
442+ }
443+
444+ func Test200_QueryWithoutTimeFilter_AutoAddsFilter (t * testing.T ) {
445+ h := testutil .NewHarness (t )
446+
447+ workspace := h .CreateWorkspace ()
448+ h .SetupAnalytics (workspace .ID )
449+ rootKey := h .CreateRootKey (workspace .ID , "api.*.read_analytics" )
450+
451+ route := & Handler {
452+ Logger : h .Logger ,
453+ DB : h .DB ,
454+ Keys : h .Keys ,
455+ ClickHouse : h .ClickHouse ,
456+ AnalyticsConnectionManager : h .AnalyticsConnectionManager ,
457+ Caches : h .Caches ,
458+ }
459+ h .Register (route )
460+
461+ headers := http.Header {
462+ "Authorization" : []string {"Bearer " + rootKey },
463+ "Content-Type" : []string {"application/json" },
464+ }
465+
466+ // Query without time filter - should auto-add time >= now() - INTERVAL 30 DAY
467+ req := Request {
468+ Query : "SELECT COUNT(*) as count FROM key_verifications_v1" ,
469+ }
470+
471+ res := testutil .CallRoute [Request , Response ](h , route , headers , req )
472+ if res .Status != 200 {
473+ t .Logf ("Response status: %d" , res .Status )
474+ t .Logf ("Response body: %s" , res .RawBody )
475+ }
476+ require .Equal (t , 200 , res .Status , "Query without time filter should succeed with auto-added filter" )
477+ require .NotNil (t , res .Body )
478+ }
0 commit comments