2025-08-07 18:31:42 +02:00
//go:build !nowebpush
2023-05-29 17:57:21 +02:00
package server
import (
2023-06-02 13:22:54 +02:00
"encoding/json"
"fmt"
2023-05-29 17:57:21 +02:00
"io"
"net/http"
"net/http/httptest"
2023-06-16 21:59:07 -04:00
"net/netip"
2025-08-07 18:54:37 +02:00
"path/filepath"
2023-05-30 14:23:03 -04:00
"strings"
2023-05-29 17:57:21 +02:00
"sync/atomic"
"testing"
2023-06-09 23:17:48 -04:00
"time"
2026-02-16 12:13:10 -05:00
"github.com/SherClockHolmes/webpush-go"
"github.com/stretchr/testify/require"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
2023-05-29 17:57:21 +02:00
)
2023-06-02 16:52:35 +02:00
const (
2023-06-08 23:09:38 -04:00
testWebPushEndpoint = "https://updates.push.services.mozilla.com/wpush/v1/AAABBCCCDDEEEFFF"
2023-06-02 16:52:35 +02:00
)
2025-08-07 18:54:37 +02:00
func TestServer_WebPush_Enabled ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
conf := newTestConfig ( t , databaseURL )
2026-02-19 20:48:01 -05:00
conf . WebRoot = "" // Disable web app
s := newTestServer ( t , conf )
2025-08-07 18:54:37 +02:00
2026-02-19 20:48:01 -05:00
rr := request ( t , s , "GET" , "/manifest.webmanifest" , "" , nil )
require . Equal ( t , 404 , rr . Code )
2025-08-07 18:54:37 +02:00
2026-02-19 21:36:09 -05:00
conf2 := newTestConfig ( t , databaseURL )
2026-02-19 20:48:01 -05:00
s2 := newTestServer ( t , conf2 )
2025-08-07 18:54:37 +02:00
2026-02-19 20:48:01 -05:00
rr = request ( t , s2 , "GET" , "/manifest.webmanifest" , "" , nil )
require . Equal ( t , 404 , rr . Code )
2025-08-07 18:54:37 +02:00
2026-02-19 21:36:09 -05:00
conf3 := newTestConfigWithWebPush ( t , databaseURL )
2026-02-19 20:48:01 -05:00
s3 := newTestServer ( t , conf3 )
2025-08-07 18:54:37 +02:00
2026-02-19 20:48:01 -05:00
rr = request ( t , s3 , "GET" , "/manifest.webmanifest" , "" , nil )
require . Equal ( t , 200 , rr . Code )
require . Equal ( t , "application/manifest+json" , rr . Header ( ) . Get ( "Content-Type" ) )
2025-08-07 18:54:37 +02:00
2026-02-19 20:48:01 -05:00
} )
2025-08-07 18:54:37 +02:00
}
2023-06-17 21:40:08 +02:00
func TestServer_WebPush_Disabled ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfig ( t , databaseURL ) )
2023-06-17 21:40:08 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , [ ] string { "test-topic" } , testWebPushEndpoint ) , nil )
require . Equal ( t , 404 , response . Code )
} )
2023-06-17 21:40:08 +02:00
}
2023-06-02 13:22:54 +02:00
func TestServer_WebPush_TopicAdd ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , [ ] string { "test-topic" } , testWebPushEndpoint ) , nil )
require . Equal ( t , 200 , response . Code )
require . Equal ( t , ` { "success":true} ` + "\n" , response . Body . String ( ) )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
subs , err := s . webPush . SubscriptionsForTopic ( "test-topic" )
require . Nil ( t , err )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
require . Len ( t , subs , 1 )
require . Equal ( t , subs [ 0 ] . Endpoint , testWebPushEndpoint )
require . Equal ( t , subs [ 0 ] . P256dh , "p256dh-key" )
require . Equal ( t , subs [ 0 ] . Auth , "auth-key" )
require . Equal ( t , subs [ 0 ] . UserID , "" )
} )
2023-05-29 17:57:21 +02:00
}
2023-06-02 16:52:35 +02:00
func TestServer_WebPush_TopicAdd_InvalidEndpoint ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2023-06-02 16:52:35 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , [ ] string { "test-topic" } , "https://ddos-target.example.com/webpush" ) , nil )
require . Equal ( t , 400 , response . Code )
require . Equal ( t , ` { "code":40039,"http":400,"error":"invalid request: web push endpoint unknown"} ` + "\n" , response . Body . String ( ) )
} )
2023-06-02 16:52:35 +02:00
}
2026-04-21 10:40:52 -04:00
func TestServer_WebPush_EndpointRegex ( t * testing . T ) {
// Synthetic endpoint samples representing each supported push service host shape.
allowed := [ ] string {
// Google FCM (legacy send, webpush, preprod webpush)
"https://fcm.googleapis.com/fcm/send/FAKETOKEN:APA91b-placeholder-not-a-real-token" ,
"https://fcm.googleapis.com/wp/FAKETOKEN:APA91b-placeholder-not-a-real-token" ,
"https://fcm.googleapis.com/preprod/wp/FAKETOKEN:APA91b-placeholder-not-a-real-token" ,
"https://jmt17.google.com/fcm/send/FAKETOKEN:APA91b-placeholder-not-a-real-token" ,
// Mozilla autopush (v1 legacy, v2 current, plus AWS-hosted infra)
"https://updates.push.services.mozilla.com/wpush/v1/placeholder-not-a-real-token" ,
"https://updates.push.services.mozilla.com/wpush/v2/placeholder-not-a-real-token" ,
"https://autopush.mozaws.net/wpush/v1/placeholder-not-a-real-token" ,
// Apple Web Push
"https://web.push.apple.com/placeholder-not-a-real-token" ,
// Microsoft WNS: instance-specific "wns2-<region>" prefix is wildcarded
"https://wns2-bn3p.notify.windows.com/w/?token=placeholder" ,
"https://wns2-ch1p.notify.windows.com/w/?token=placeholder" ,
"https://wns2-par02p.notify.windows.com/w/?token=placeholder" ,
"https://wns2-pn1p.notify.windows.com/w/?token=placeholder" ,
"https://wns2-am3p.notify.windows.com/w/?token=placeholder" ,
}
denied := [ ] string {
// HTTP (not HTTPS)
"http://fcm.googleapis.com/fcm/send/abc" ,
// Unrelated host
"https://attacker.example.com/webpush" ,
// GHSA-w9hq-5jg7-q4j7 bypass: allowed host embedded in path
"https://attacker.com/x.google.com/push" ,
"https://attacker.example.com/fcm.googleapis.com/fcm/send/abc" ,
"https://evil.test/web.push.apple.com/3/device/abc" ,
"https://ntfytest.requestcatcher.com/path.google.com/push" ,
"https://ntfytest.requestcatcher.com/a.google.com/toto" ,
"https://ntfytest.requestcatcher.com/bypass.google.com/test" ,
"https://webhook.site/86e94e2e-2af4-4a31-a80b-e2f335cc6495/path.google.com/push" ,
"https://webhook.site/86e94e2e-2af4-4a31-a80b-e2f335cc6495/bypass.google.com/" ,
// Allowed host as a prefix of a different host (no separating slash)
"https://fcm.googleapis.com.attacker.com/fcm/send/abc" ,
"https://web.push.apple.com.evil.test/tok" ,
// Allowed host as a suffix of a different host (no separating dot)
"https://evilgoogle.com/" ,
"https://notapple.com/" ,
// Credentials/userinfo in the URL pointing at a different host
"https://fcm.googleapis.com@attacker.com/fcm/send/abc" ,
// Previously allowed by the wildcard allowlist but not actually used by Web Push
"https://api.push.apple.com/3/device/abc" ,
"https://android.googleapis.com/send/xyz" ,
"https://login.microsoft.com/anything" ,
// Bare notify.windows.com with no subdomain label
"https://notify.windows.com/w/?token=abc" ,
}
for _ , endpoint := range allowed {
require . Truef ( t , webPushEndpointAllowed ( endpoint ) , "expected endpoint to be allowed: %s" , endpoint )
}
for _ , endpoint := range denied {
require . Falsef ( t , webPushEndpointAllowed ( endpoint ) , "expected endpoint to be denied: %s" , endpoint )
}
}
func TestServer_WebPush_TopicAdd_BypassAttempt ( t * testing . T ) {
// Regression test for GHSA-w9hq-5jg7-q4j7: the allow-list regex previously had no
// end anchor, so a URL like https://attacker.example.com/x.google.com/... passed
// validation and caused the server to deliver push payloads to attacker-controlled
// endpoints (SSRF + message exfiltration via attacker-supplied p256dh key).
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , [ ] string { "test-topic" } , "https://attacker.example.com/x.google.com/push" ) , nil )
require . Equal ( t , 400 , response . Code )
require . Equal ( t , ` { "code":40039,"http":400,"error":"invalid request: web push endpoint unknown"} ` + "\n" , response . Body . String ( ) )
} )
}
2023-06-02 16:52:35 +02:00
func TestServer_WebPush_TopicAdd_TooManyTopics ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2023-06-02 16:52:35 +02:00
2026-02-19 20:48:01 -05:00
topicList := make ( [ ] string , 51 )
for i := range topicList {
topicList [ i ] = util . RandomString ( 5 )
}
2023-06-02 16:52:35 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , topicList , testWebPushEndpoint ) , nil )
require . Equal ( t , 400 , response . Code )
require . Equal ( t , ` { "code":40040,"http":400,"error":"invalid request: too many web push topic subscriptions"} ` + "\n" , response . Body . String ( ) )
} )
2023-06-02 16:52:35 +02:00
}
2023-06-02 13:22:54 +02:00
func TestServer_WebPush_TopicUnsubscribe ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2023-06-02 13:22:54 +02:00
2026-02-19 20:48:01 -05:00
addSubscription ( t , s , testWebPushEndpoint , "test-topic" )
requireSubscriptionCount ( t , s , "test-topic" , 1 )
2023-06-02 13:22:54 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , [ ] string { } , testWebPushEndpoint ) , nil )
require . Equal ( t , 200 , response . Code )
require . Equal ( t , ` { "success":true} ` + "\n" , response . Body . String ( ) )
2023-06-02 13:22:54 +02:00
2026-02-19 20:48:01 -05:00
requireSubscriptionCount ( t , s , "test-topic" , 0 )
} )
2023-06-02 13:22:54 +02:00
}
2023-06-17 21:44:21 +02:00
func TestServer_WebPush_Delete ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2023-06-17 21:44:21 +02:00
2026-02-19 20:48:01 -05:00
addSubscription ( t , s , testWebPushEndpoint , "test-topic" )
requireSubscriptionCount ( t , s , "test-topic" , 1 )
2023-06-17 21:44:21 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "DELETE" , "/v1/webpush" , fmt . Sprintf ( ` { "endpoint":"%s"} ` , testWebPushEndpoint ) , nil )
require . Equal ( t , 200 , response . Code )
require . Equal ( t , ` { "success":true} ` + "\n" , response . Body . String ( ) )
2023-06-17 21:44:21 +02:00
2026-02-19 20:48:01 -05:00
requireSubscriptionCount ( t , s , "test-topic" , 0 )
} )
2023-06-17 21:44:21 +02:00
}
2023-05-29 17:57:21 +02:00
func TestServer_WebPush_TopicSubscribeProtected_Allowed ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
config := configureAuth ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2026-02-19 20:48:01 -05:00
config . AuthDefault = user . PermissionDenyAll
s := newTestServer ( t , config )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
require . Nil ( t , s . userManager . AddUser ( "ben" , "ben" , user . RoleUser , false ) )
require . Nil ( t , s . userManager . AllowAccess ( "ben" , "test-topic" , user . PermissionReadWrite ) )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , [ ] string { "test-topic" } , testWebPushEndpoint ) , map [ string ] string {
"Authorization" : util . BasicAuth ( "ben" , "ben" ) ,
} )
require . Equal ( t , 200 , response . Code )
require . Equal ( t , ` { "success":true} ` + "\n" , response . Body . String ( ) )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
subs , err := s . webPush . SubscriptionsForTopic ( "test-topic" )
require . Nil ( t , err )
require . Len ( t , subs , 1 )
require . True ( t , strings . HasPrefix ( subs [ 0 ] . UserID , "u_" ) )
} )
2023-05-29 17:57:21 +02:00
}
func TestServer_WebPush_TopicSubscribeProtected_Denied ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
config := configureAuth ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2026-02-19 20:48:01 -05:00
config . AuthDefault = user . PermissionDenyAll
s := newTestServer ( t , config )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , [ ] string { "test-topic" } , testWebPushEndpoint ) , nil )
require . Equal ( t , 403 , response . Code )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
requireSubscriptionCount ( t , s , "test-topic" , 0 )
} )
2023-05-29 17:57:21 +02:00
}
func TestServer_WebPush_DeleteAccountUnsubscribe ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
config := configureAuth ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2026-02-19 20:48:01 -05:00
s := newTestServer ( t , config )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
require . Nil ( t , s . userManager . AddUser ( "ben" , "ben" , user . RoleUser , false ) )
require . Nil ( t , s . userManager . AllowAccess ( "ben" , "test-topic" , user . PermissionReadWrite ) )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
response := request ( t , s , "POST" , "/v1/webpush" , payloadForTopics ( t , [ ] string { "test-topic" } , testWebPushEndpoint ) , map [ string ] string {
"Authorization" : util . BasicAuth ( "ben" , "ben" ) ,
} )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
require . Equal ( t , 200 , response . Code )
require . Equal ( t , ` { "success":true} ` + "\n" , response . Body . String ( ) )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
requireSubscriptionCount ( t , s , "test-topic" , 1 )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
request ( t , s , "DELETE" , "/v1/account" , ` { "password":"ben"} ` , map [ string ] string {
"Authorization" : util . BasicAuth ( "ben" , "ben" ) ,
} )
// should've been deleted with the account
requireSubscriptionCount ( t , s , "test-topic" , 0 )
2023-05-29 17:57:21 +02:00
} )
}
func TestServer_WebPush_Publish ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2026-02-19 20:48:01 -05:00
var received atomic . Bool
pushService := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
_ , err := io . ReadAll ( r . Body )
require . Nil ( t , err )
require . Equal ( t , "/push-receive" , r . URL . Path )
require . Equal ( t , "high" , r . Header . Get ( "Urgency" ) )
require . Equal ( t , "" , r . Header . Get ( "Topic" ) )
received . Store ( true )
} ) )
defer pushService . Close ( )
addSubscription ( t , s , pushService . URL + "/push-receive" , "test-topic" )
request ( t , s , "POST" , "/test-topic" , "web push test" , nil )
waitFor ( t , func ( ) bool {
return received . Load ( )
} )
2023-05-29 17:57:21 +02:00
} )
}
2023-06-08 21:45:52 -04:00
func TestServer_WebPush_Publish_RemoveOnError ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
var received atomic . Bool
pushService := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
_ , err := io . ReadAll ( r . Body )
require . Nil ( t , err )
w . WriteHeader ( http . StatusGone )
received . Store ( true )
} ) )
defer pushService . Close ( )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
addSubscription ( t , s , pushService . URL + "/push-receive" , "test-topic" , "test-topic-abc" )
requireSubscriptionCount ( t , s , "test-topic" , 1 )
requireSubscriptionCount ( t , s , "test-topic-abc" , 1 )
2023-05-29 17:57:21 +02:00
2026-02-19 20:48:01 -05:00
request ( t , s , "POST" , "/test-topic" , "web push test" , nil )
2023-05-29 17:57:21 +02:00
2026-03-21 16:54:16 -04:00
// Receiving the 410 should've caused the publisher to expire all subscriptions on the endpoint
2026-02-19 20:48:01 -05:00
waitFor ( t , func ( ) bool {
2026-03-21 16:54:16 -04:00
subs , err := s . webPush . SubscriptionsForTopic ( "test-topic" )
require . Nil ( t , err )
return len ( subs ) == 0
2026-02-19 20:48:01 -05:00
} )
requireSubscriptionCount ( t , s , "test-topic-abc" , 0 )
} )
2023-05-29 17:57:21 +02:00
}
2023-06-02 14:45:05 +02:00
func TestServer_WebPush_Expiry ( t * testing . T ) {
2026-02-19 21:36:09 -05:00
forEachBackend ( t , func ( t * testing . T , databaseURL string ) {
s := newTestServer ( t , newTestConfigWithWebPush ( t , databaseURL ) )
2023-06-02 14:45:05 +02:00
2026-02-19 20:48:01 -05:00
var received atomic . Bool
2023-06-02 14:45:05 +02:00
2026-02-19 20:48:01 -05:00
pushService := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
_ , err := io . ReadAll ( r . Body )
require . Nil ( t , err )
w . WriteHeader ( 200 )
w . Write ( [ ] byte ( ` ` ) )
received . Store ( true )
} ) )
defer pushService . Close ( )
2023-06-02 14:45:05 +02:00
2026-02-19 20:48:01 -05:00
endpoint := pushService . URL + "/push-receive"
addSubscription ( t , s , endpoint , "test-topic" )
requireSubscriptionCount ( t , s , "test-topic" , 1 )
2023-06-02 14:45:05 +02:00
2026-02-19 20:48:01 -05:00
require . Nil ( t , s . webPush . SetSubscriptionUpdatedAt ( endpoint , time . Now ( ) . Add ( - 55 * 24 * time . Hour ) . Unix ( ) ) )
2023-06-02 14:45:05 +02:00
2026-02-19 20:48:01 -05:00
s . pruneAndNotifyWebPushSubscriptions ( )
requireSubscriptionCount ( t , s , "test-topic" , 1 )
2023-06-02 14:45:05 +02:00
2026-02-19 20:48:01 -05:00
waitFor ( t , func ( ) bool {
return received . Load ( )
} )
2023-06-02 14:45:05 +02:00
2026-02-19 20:48:01 -05:00
require . Nil ( t , s . webPush . SetSubscriptionUpdatedAt ( endpoint , time . Now ( ) . Add ( - 60 * 24 * time . Hour ) . Unix ( ) ) )
2023-06-02 14:45:05 +02:00
2026-02-19 20:48:01 -05:00
s . pruneAndNotifyWebPushSubscriptions ( )
waitFor ( t , func ( ) bool {
subs , err := s . webPush . SubscriptionsForTopic ( "test-topic" )
require . Nil ( t , err )
return len ( subs ) == 0
} )
2023-06-08 21:45:52 -04:00
} )
2023-06-02 14:45:05 +02:00
}
2023-06-02 16:52:35 +02:00
func payloadForTopics ( t * testing . T , topics [ ] string , endpoint string ) string {
2023-06-02 14:45:05 +02:00
topicsJSON , err := json . Marshal ( topics )
2023-06-02 13:22:54 +02:00
require . Nil ( t , err )
return fmt . Sprintf ( ` {
"topics" : % s ,
2023-06-08 23:09:38 -04:00
"endpoint" : "%s" ,
"p256dh" : "p256dh-key" ,
"auth" : "auth-key"
2023-06-02 16:52:35 +02:00
} ` , topicsJSON , endpoint )
2023-06-02 13:22:54 +02:00
}
2023-06-08 23:09:38 -04:00
func addSubscription ( t * testing . T , s * Server , endpoint string , topics ... string ) {
2023-06-16 21:59:07 -04:00
require . Nil ( t , s . webPush . UpsertSubscription ( endpoint , "kSC3T8aN1JCQxxPdrFLrZg" , "BMKKbxdUU_xLS7G1Wh5AN8PvWOjCzkCuKZYb8apcqYrDxjOF_2piggBnoJLQYx9IeSD70fNuwawI3e9Y8m3S3PE" , "u_123" , netip . MustParseAddr ( "1.2.3.4" ) , topics ) ) // Test auth and p256dh
2023-05-29 17:57:21 +02:00
}
func requireSubscriptionCount ( t * testing . T , s * Server , topic string , expectedLength int ) {
2023-06-08 23:09:38 -04:00
subs , err := s . webPush . SubscriptionsForTopic ( topic )
2023-05-31 18:02:04 +02:00
require . Nil ( t , err )
2023-05-29 17:57:21 +02:00
require . Len ( t , subs , expectedLength )
}
2025-08-07 18:54:37 +02:00
2026-02-19 21:36:09 -05:00
func newTestConfigWithWebPush ( t * testing . T , databaseURL string ) * Config {
conf := newTestConfig ( t , databaseURL )
2025-08-07 18:54:37 +02:00
privateKey , publicKey , err := webpush . GenerateVAPIDKeys ( )
require . Nil ( t , err )
2026-02-19 20:48:01 -05:00
if conf . DatabaseURL == "" {
conf . WebPushFile = filepath . Join ( t . TempDir ( ) , "webpush.db" )
}
2025-08-07 18:54:37 +02:00
conf . WebPushEmailAddress = "testing@example.com"
conf . WebPushPrivateKey = privateKey
conf . WebPushPublicKey = publicKey
return conf
}