controller_browser_test.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package sub
  2. import (
  3. "net/http"
  4. "net/http/httptest"
  5. "regexp"
  6. "strings"
  7. "testing"
  8. "github.com/gin-gonic/gin"
  9. "github.com/nicksnyder/go-i18n/v2/i18n"
  10. "golang.org/x/text/language"
  11. )
  12. func TestIsBrowserSubscriptionRequest(t *testing.T) {
  13. gin.SetMode(gin.TestMode)
  14. tests := []struct {
  15. name string
  16. accept string
  17. ua string
  18. dest string
  19. mode string
  20. query string
  21. want bool
  22. }{
  23. {name: "explicit html query is not implicit navigation", query: "?html=1", want: false},
  24. {name: "html accept", accept: "text/html,application/xhtml+xml", want: true},
  25. {name: "browser navigation with wildcard accept", accept: "*/*", ua: "Mozilla/5.0 Safari/605.1.15", dest: "document", mode: "navigate", want: true},
  26. {name: "browser ua fallback", accept: "*/*", ua: "Mozilla/5.0 Chrome/126.0.0.0", want: true},
  27. {name: "vpn client wildcard", accept: "*/*", ua: "Incy/3.3.0", want: false},
  28. {name: "vpn client with mozilla token", accept: "*/*", ua: "Mozilla/5.0 Incy/3.3.0", want: false},
  29. {name: "plain client", accept: "*/*", ua: "Go-http-client/2.0", want: false},
  30. }
  31. for _, tt := range tests {
  32. t.Run(tt.name, func(t *testing.T) {
  33. w := httptest.NewRecorder()
  34. c, _ := gin.CreateTestContext(w)
  35. req := httptest.NewRequest(http.MethodGet, "/sub/abc"+tt.query, nil)
  36. if tt.accept != "" {
  37. req.Header.Set("Accept", tt.accept)
  38. }
  39. if tt.ua != "" {
  40. req.Header.Set("User-Agent", tt.ua)
  41. }
  42. if tt.dest != "" {
  43. req.Header.Set("Sec-Fetch-Dest", tt.dest)
  44. }
  45. if tt.mode != "" {
  46. req.Header.Set("Sec-Fetch-Mode", tt.mode)
  47. }
  48. c.Request = req
  49. if got := (&SUBController{}).isBrowserSubscriptionRequest(c); got != tt.want {
  50. t.Fatalf("isBrowserSubscriptionRequest() = %v, want %v", got, tt.want)
  51. }
  52. })
  53. }
  54. }
  55. func TestBrowserClassificationHonorsConfiguredFormatMatchers(t *testing.T) {
  56. cases := []struct {
  57. name string
  58. new func() *SUBController
  59. }{
  60. {"clash", func() *SUBController {
  61. return &SUBController{subClashAutoDetect: true, clashEnabled: true, clashUserAgent: regexp.MustCompile(`Custom-Client`)}
  62. }},
  63. {"json", func() *SUBController {
  64. return &SUBController{jsonAutoDetect: true, jsonEnabled: true, jsonUserAgent: regexp.MustCompile(`Custom-Client`)}
  65. }},
  66. }
  67. for _, tc := range cases {
  68. t.Run(tc.name, func(t *testing.T) {
  69. c, _ := gin.CreateTestContext(httptest.NewRecorder())
  70. c.Request = httptest.NewRequest(http.MethodGet, "/sub/abc", nil)
  71. c.Request.Header.Set("User-Agent", "Mozilla/5.0 Custom-Client/1.0")
  72. if tc.new().isBrowserSubscriptionRequest(c) {
  73. t.Fatal("configured subscription client was classified as a browser")
  74. }
  75. })
  76. }
  77. }
  78. func TestSubscriptionCopyPageUsesRequestLocale(t *testing.T) {
  79. bundle := i18n.NewBundle(language.English)
  80. for id, text := range map[string]string{
  81. "subCopyPageTitle": "Titre localisé",
  82. "subCopyPageHeading": "En-tête localisé",
  83. "subCopyPageInstructions": "Instructions localisées",
  84. } {
  85. bundle.AddMessages(language.French, &i18n.Message{ID: id, Other: text})
  86. }
  87. w := httptest.NewRecorder()
  88. c, _ := gin.CreateTestContext(w)
  89. c.Request = httptest.NewRequest(http.MethodGet, "/sub/abc", nil)
  90. c.Request.Header.Set("Accept-Language", "fr-FR")
  91. c.Set("localizer", i18n.NewLocalizer(bundle, "fr-FR"))
  92. (&SUBController{}).serveSubscriptionCopyPage(c)
  93. if body := w.Body.String(); !strings.Contains(body, `<html lang="fr-FR">`) ||
  94. !strings.Contains(body, "Titre localisé") || !strings.Contains(body, "Instructions localisées") {
  95. t.Fatalf("copy page was not localized from the request: %s", body)
  96. }
  97. }
  98. func TestExplicitSubPageRequest(t *testing.T) {
  99. gin.SetMode(gin.TestMode)
  100. tests := []struct {
  101. name string
  102. query string
  103. want bool
  104. }{
  105. {name: "html=1", query: "?html=1", want: true},
  106. {name: "view=html", query: "?view=HTML", want: true},
  107. {name: "no query", query: "", want: false},
  108. {name: "unrelated query", query: "?format=info", want: false},
  109. }
  110. for _, tt := range tests {
  111. t.Run(tt.name, func(t *testing.T) {
  112. w := httptest.NewRecorder()
  113. c, _ := gin.CreateTestContext(w)
  114. c.Request = httptest.NewRequest(http.MethodGet, "/sub/abc"+tt.query, nil)
  115. if got := explicitSubPageRequest(c); got != tt.want {
  116. t.Fatalf("explicitSubPageRequest() = %v, want %v", got, tt.want)
  117. }
  118. })
  119. }
  120. }