transferURLs_test.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * Copyright (c) 2018, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package parameters
  20. import (
  21. "encoding/base64"
  22. "testing"
  23. )
  24. func TestTransferURLs(t *testing.T) {
  25. decodedA := "a.example.com"
  26. encodedA := base64.StdEncoding.EncodeToString([]byte(decodedA))
  27. encodedB := base64.StdEncoding.EncodeToString([]byte("b.example.com"))
  28. encodedC := base64.StdEncoding.EncodeToString([]byte("c.example.com"))
  29. testCases := []struct {
  30. description string
  31. transferURLs TransferURLs
  32. attempts int
  33. expectedValid bool
  34. expectedCanonicalURL string
  35. expectedDistinctSelections int
  36. }{
  37. {
  38. "missing OnlyAfterAttempts = 0",
  39. TransferURLs{
  40. {
  41. URL: encodedA,
  42. OnlyAfterAttempts: 1,
  43. },
  44. },
  45. 1,
  46. false,
  47. decodedA,
  48. 0,
  49. },
  50. {
  51. "single URL, multiple attempts",
  52. TransferURLs{
  53. {
  54. URL: encodedA,
  55. OnlyAfterAttempts: 0,
  56. },
  57. },
  58. 2,
  59. true,
  60. decodedA,
  61. 1,
  62. },
  63. {
  64. "single URL, multiple attempts, fronting spec",
  65. TransferURLs{
  66. {
  67. URL: encodedA,
  68. OnlyAfterAttempts: 0,
  69. FrontingSpecs: []*FrontingSpec{
  70. {
  71. FrontingProviderID: "frontingProvider",
  72. Addresses: []string{"example.org"},
  73. VerifyServerName: "example.com",
  74. Host: "example.org",
  75. SkipVerify: false,
  76. },
  77. },
  78. },
  79. },
  80. 2,
  81. true,
  82. decodedA,
  83. 1,
  84. },
  85. {
  86. "single URL, multiple attempts, fronting spec, skip verify set",
  87. TransferURLs{
  88. {
  89. URL: encodedA,
  90. OnlyAfterAttempts: 0,
  91. FrontingSpecs: []*FrontingSpec{
  92. {
  93. FrontingProviderID: "frontingProvider",
  94. Addresses: []string{"example.org"},
  95. Host: "example.org",
  96. SkipVerify: true,
  97. },
  98. },
  99. },
  100. },
  101. 2,
  102. true,
  103. decodedA,
  104. 1,
  105. },
  106. {
  107. "multiple URLs, single attempt",
  108. TransferURLs{
  109. {
  110. URL: encodedA,
  111. OnlyAfterAttempts: 0,
  112. },
  113. {
  114. URL: encodedB,
  115. OnlyAfterAttempts: 1,
  116. },
  117. {
  118. URL: encodedC,
  119. OnlyAfterAttempts: 1,
  120. },
  121. },
  122. 1,
  123. true,
  124. decodedA,
  125. 1,
  126. },
  127. {
  128. "multiple URLs, multiple attempts",
  129. TransferURLs{
  130. {
  131. URL: encodedA,
  132. OnlyAfterAttempts: 0,
  133. },
  134. {
  135. URL: encodedB,
  136. OnlyAfterAttempts: 1,
  137. },
  138. {
  139. URL: encodedC,
  140. OnlyAfterAttempts: 1,
  141. },
  142. },
  143. 2,
  144. true,
  145. decodedA,
  146. 3,
  147. },
  148. {
  149. "multiple URLs, multiple attempts",
  150. TransferURLs{
  151. {
  152. URL: encodedA,
  153. OnlyAfterAttempts: 0,
  154. },
  155. {
  156. URL: encodedB,
  157. OnlyAfterAttempts: 1,
  158. },
  159. {
  160. URL: encodedC,
  161. OnlyAfterAttempts: 3,
  162. },
  163. },
  164. 4,
  165. true,
  166. decodedA,
  167. 3,
  168. },
  169. }
  170. for _, testCase := range testCases {
  171. t.Run(testCase.description, func(t *testing.T) {
  172. err := testCase.transferURLs.DecodeAndValidate()
  173. if testCase.expectedValid {
  174. if err != nil {
  175. t.Fatalf("unexpected validation error: %s", err)
  176. }
  177. } else {
  178. if err == nil {
  179. t.Fatalf("expected validation error")
  180. }
  181. return
  182. }
  183. // Track distinct selections for each attempt; the
  184. // expected number of distinct should be for at least
  185. // one particular attempt.
  186. attemptDistinctSelections := make(map[int]map[string]int)
  187. for i := 0; i < testCase.attempts; i++ {
  188. attemptDistinctSelections[i] = make(map[string]int)
  189. }
  190. // Perform enough runs to account for random selection.
  191. runs := 1000
  192. attempt := 0
  193. for i := 0; i < runs; i++ {
  194. canonicalURL := testCase.transferURLs.CanonicalURL()
  195. if canonicalURL != testCase.expectedCanonicalURL {
  196. t.Fatalf("unexpected canonical URL: %s", canonicalURL)
  197. }
  198. transferUrl := testCase.transferURLs.Select(attempt)
  199. if transferUrl.SkipVerify {
  200. t.Fatalf("unexpected skipVerify")
  201. }
  202. attemptDistinctSelections[attempt][transferUrl.URL] += 1
  203. attempt = (attempt + 1) % testCase.attempts
  204. }
  205. maxDistinctSelections := 0
  206. for _, m := range attemptDistinctSelections {
  207. if len(m) > maxDistinctSelections {
  208. maxDistinctSelections = len(m)
  209. }
  210. }
  211. if maxDistinctSelections != testCase.expectedDistinctSelections {
  212. t.Fatalf("got %d distinct selections, expected %d",
  213. maxDistinctSelections,
  214. testCase.expectedDistinctSelections)
  215. }
  216. })
  217. }
  218. }