server_account_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. package server
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "github.com/stretchr/testify/require"
  6. "heckel.io/ntfy/user"
  7. "heckel.io/ntfy/util"
  8. "io"
  9. "testing"
  10. "time"
  11. )
  12. func TestAccount_Signup_Success(t *testing.T) {
  13. conf := newTestConfigWithAuthFile(t)
  14. conf.EnableSignup = true
  15. s := newTestServer(t, conf)
  16. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  17. require.Equal(t, 200, rr.Code)
  18. rr = request(t, s, "POST", "/v1/account/token", "", map[string]string{
  19. "Authorization": util.BasicAuth("phil", "mypass"),
  20. })
  21. require.Equal(t, 200, rr.Code)
  22. token, _ := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body))
  23. require.NotEmpty(t, token.Token)
  24. require.True(t, time.Now().Add(71*time.Hour).Unix() < token.Expires)
  25. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  26. "Authorization": util.BearerAuth(token.Token),
  27. })
  28. require.Equal(t, 200, rr.Code)
  29. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  30. require.Equal(t, "phil", account.Username)
  31. require.Equal(t, "user", account.Role)
  32. }
  33. func TestAccount_Signup_UserExists(t *testing.T) {
  34. conf := newTestConfigWithAuthFile(t)
  35. conf.EnableSignup = true
  36. s := newTestServer(t, conf)
  37. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  38. require.Equal(t, 200, rr.Code)
  39. rr = request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  40. require.Equal(t, 409, rr.Code)
  41. require.Equal(t, 40901, toHTTPError(t, rr.Body.String()).Code)
  42. }
  43. func TestAccount_Signup_LimitReached(t *testing.T) {
  44. conf := newTestConfigWithAuthFile(t)
  45. conf.EnableSignup = true
  46. s := newTestServer(t, conf)
  47. for i := 0; i < 3; i++ {
  48. rr := request(t, s, "POST", "/v1/account", fmt.Sprintf(`{"username":"phil%d", "password":"mypass"}`, i), nil)
  49. require.Equal(t, 200, rr.Code)
  50. }
  51. rr := request(t, s, "POST", "/v1/account", `{"username":"thiswontwork", "password":"mypass"}`, nil)
  52. require.Equal(t, 429, rr.Code)
  53. require.Equal(t, 42906, toHTTPError(t, rr.Body.String()).Code)
  54. }
  55. func TestAccount_Signup_AsUser(t *testing.T) {
  56. conf := newTestConfigWithAuthFile(t)
  57. conf.EnableSignup = true
  58. s := newTestServer(t, conf)
  59. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin))
  60. require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser))
  61. rr := request(t, s, "POST", "/v1/account", `{"username":"emma", "password":"emma"}`, map[string]string{
  62. "Authorization": util.BasicAuth("phil", "phil"),
  63. })
  64. require.Equal(t, 200, rr.Code)
  65. rr = request(t, s, "POST", "/v1/account", `{"username":"marian", "password":"marian"}`, map[string]string{
  66. "Authorization": util.BasicAuth("ben", "ben"),
  67. })
  68. require.Equal(t, 401, rr.Code)
  69. }
  70. func TestAccount_Signup_Disabled(t *testing.T) {
  71. conf := newTestConfigWithAuthFile(t)
  72. conf.EnableSignup = false
  73. s := newTestServer(t, conf)
  74. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  75. require.Equal(t, 400, rr.Code)
  76. require.Equal(t, 40022, toHTTPError(t, rr.Body.String()).Code)
  77. }
  78. func TestAccount_Get_Anonymous(t *testing.T) {
  79. conf := newTestConfigWithAuthFile(t)
  80. conf.VisitorRequestLimitReplenish = 86 * time.Second
  81. conf.VisitorEmailLimitReplenish = time.Hour
  82. conf.VisitorAttachmentTotalSizeLimit = 5123
  83. conf.AttachmentFileSizeLimit = 512
  84. s := newTestServer(t, conf)
  85. s.smtpSender = &testMailer{}
  86. rr := request(t, s, "GET", "/v1/account", "", nil)
  87. require.Equal(t, 200, rr.Code)
  88. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  89. require.Equal(t, "*", account.Username)
  90. require.Equal(t, string(user.RoleAnonymous), account.Role)
  91. require.Equal(t, "ip", account.Limits.Basis)
  92. require.Equal(t, int64(1004), account.Limits.Messages) // I hate this
  93. require.Equal(t, int64(24), account.Limits.Emails) // I hate this
  94. require.Equal(t, int64(5123), account.Limits.AttachmentTotalSize)
  95. require.Equal(t, int64(512), account.Limits.AttachmentFileSize)
  96. require.Equal(t, int64(0), account.Stats.Messages)
  97. require.Equal(t, int64(1004), account.Stats.MessagesRemaining)
  98. require.Equal(t, int64(0), account.Stats.Emails)
  99. require.Equal(t, int64(24), account.Stats.EmailsRemaining)
  100. rr = request(t, s, "POST", "/mytopic", "", nil)
  101. require.Equal(t, 200, rr.Code)
  102. rr = request(t, s, "POST", "/mytopic", "", map[string]string{
  103. "Email": "phil@ntfy.sh",
  104. })
  105. require.Equal(t, 200, rr.Code)
  106. rr = request(t, s, "GET", "/v1/account", "", nil)
  107. require.Equal(t, 200, rr.Code)
  108. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  109. require.Equal(t, int64(2), account.Stats.Messages)
  110. require.Equal(t, int64(1002), account.Stats.MessagesRemaining)
  111. require.Equal(t, int64(1), account.Stats.Emails)
  112. require.Equal(t, int64(23), account.Stats.EmailsRemaining)
  113. }
  114. func TestAccount_ChangeSettings(t *testing.T) {
  115. s := newTestServer(t, newTestConfigWithAuthFile(t))
  116. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  117. user, _ := s.userManager.User("phil")
  118. token, _ := s.userManager.CreateToken(user)
  119. rr := request(t, s, "PATCH", "/v1/account/settings", `{"notification": {"sound": "juntos"},"ignored": true}`, map[string]string{
  120. "Authorization": util.BasicAuth("phil", "phil"),
  121. })
  122. require.Equal(t, 200, rr.Code)
  123. rr = request(t, s, "PATCH", "/v1/account/settings", `{"notification": {"delete_after": 86400}, "language": "de"}`, map[string]string{
  124. "Authorization": util.BearerAuth(token.Value),
  125. })
  126. require.Equal(t, 200, rr.Code)
  127. rr = request(t, s, "GET", "/v1/account", `{"username":"marian", "password":"marian"}`, map[string]string{
  128. "Authorization": util.BearerAuth(token.Value),
  129. })
  130. require.Equal(t, 200, rr.Code)
  131. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  132. require.Equal(t, "de", account.Language)
  133. require.Equal(t, 86400, account.Notification.DeleteAfter)
  134. require.Equal(t, "juntos", account.Notification.Sound)
  135. require.Equal(t, 0, account.Notification.MinPriority) // Not set
  136. }
  137. func TestAccount_Subscription_AddUpdateDelete(t *testing.T) {
  138. s := newTestServer(t, newTestConfigWithAuthFile(t))
  139. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  140. rr := request(t, s, "POST", "/v1/account/subscription", `{"base_url": "http://abc.com", "topic": "def"}`, map[string]string{
  141. "Authorization": util.BasicAuth("phil", "phil"),
  142. })
  143. require.Equal(t, 200, rr.Code)
  144. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  145. "Authorization": util.BasicAuth("phil", "phil"),
  146. })
  147. require.Equal(t, 200, rr.Code)
  148. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  149. require.Equal(t, 1, len(account.Subscriptions))
  150. require.NotEmpty(t, account.Subscriptions[0].ID)
  151. require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL)
  152. require.Equal(t, "def", account.Subscriptions[0].Topic)
  153. require.Equal(t, "", account.Subscriptions[0].DisplayName)
  154. subscriptionID := account.Subscriptions[0].ID
  155. rr = request(t, s, "PATCH", "/v1/account/subscription/"+subscriptionID, `{"display_name": "ding dong"}`, map[string]string{
  156. "Authorization": util.BasicAuth("phil", "phil"),
  157. })
  158. require.Equal(t, 200, rr.Code)
  159. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  160. "Authorization": util.BasicAuth("phil", "phil"),
  161. })
  162. require.Equal(t, 200, rr.Code)
  163. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  164. require.Equal(t, 1, len(account.Subscriptions))
  165. require.Equal(t, subscriptionID, account.Subscriptions[0].ID)
  166. require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL)
  167. require.Equal(t, "def", account.Subscriptions[0].Topic)
  168. require.Equal(t, "ding dong", account.Subscriptions[0].DisplayName)
  169. rr = request(t, s, "DELETE", "/v1/account/subscription/"+subscriptionID, "", map[string]string{
  170. "Authorization": util.BasicAuth("phil", "phil"),
  171. })
  172. require.Equal(t, 200, rr.Code)
  173. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  174. "Authorization": util.BasicAuth("phil", "phil"),
  175. })
  176. require.Equal(t, 200, rr.Code)
  177. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  178. require.Equal(t, 0, len(account.Subscriptions))
  179. }
  180. func TestAccount_ChangePassword(t *testing.T) {
  181. s := newTestServer(t, newTestConfigWithAuthFile(t))
  182. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  183. rr := request(t, s, "POST", "/v1/account/password", `{"password": "new password"}`, map[string]string{
  184. "Authorization": util.BasicAuth("phil", "phil"),
  185. })
  186. require.Equal(t, 200, rr.Code)
  187. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  188. "Authorization": util.BasicAuth("phil", "phil"),
  189. })
  190. require.Equal(t, 401, rr.Code)
  191. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  192. "Authorization": util.BasicAuth("phil", "new password"),
  193. })
  194. require.Equal(t, 200, rr.Code)
  195. }
  196. func TestAccount_ChangePassword_NoAccount(t *testing.T) {
  197. s := newTestServer(t, newTestConfigWithAuthFile(t))
  198. rr := request(t, s, "POST", "/v1/account/password", `{"password": "new password"}`, nil)
  199. require.Equal(t, 401, rr.Code)
  200. }
  201. func TestAccount_ExtendToken(t *testing.T) {
  202. s := newTestServer(t, newTestConfigWithAuthFile(t))
  203. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  204. rr := request(t, s, "POST", "/v1/account/token", "", map[string]string{
  205. "Authorization": util.BasicAuth("phil", "phil"),
  206. })
  207. require.Equal(t, 200, rr.Code)
  208. token, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body))
  209. require.Nil(t, err)
  210. time.Sleep(time.Second)
  211. rr = request(t, s, "PATCH", "/v1/account/token", "", map[string]string{
  212. "Authorization": util.BearerAuth(token.Token),
  213. })
  214. require.Equal(t, 200, rr.Code)
  215. extendedToken, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body))
  216. require.Nil(t, err)
  217. require.Equal(t, token.Token, extendedToken.Token)
  218. require.True(t, token.Expires < extendedToken.Expires)
  219. }
  220. func TestAccount_ExtendToken_NoTokenProvided(t *testing.T) {
  221. s := newTestServer(t, newTestConfigWithAuthFile(t))
  222. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  223. rr := request(t, s, "PATCH", "/v1/account/token", "", map[string]string{
  224. "Authorization": util.BasicAuth("phil", "phil"), // Not Bearer!
  225. })
  226. require.Equal(t, 400, rr.Code)
  227. require.Equal(t, 40023, toHTTPError(t, rr.Body.String()).Code)
  228. }
  229. func TestAccount_DeleteToken(t *testing.T) {
  230. s := newTestServer(t, newTestConfigWithAuthFile(t))
  231. require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
  232. rr := request(t, s, "POST", "/v1/account/token", "", map[string]string{
  233. "Authorization": util.BasicAuth("phil", "phil"),
  234. })
  235. require.Equal(t, 200, rr.Code)
  236. token, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body))
  237. require.Nil(t, err)
  238. // Delete token failure (using basic auth)
  239. rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{
  240. "Authorization": util.BasicAuth("phil", "phil"), // Not Bearer!
  241. })
  242. require.Equal(t, 400, rr.Code)
  243. require.Equal(t, 40023, toHTTPError(t, rr.Body.String()).Code)
  244. // Delete token with wrong token
  245. rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{
  246. "Authorization": util.BearerAuth("invalidtoken"),
  247. })
  248. require.Equal(t, 401, rr.Code)
  249. // Delete token with correct token
  250. rr = request(t, s, "DELETE", "/v1/account/token", "", map[string]string{
  251. "Authorization": util.BearerAuth(token.Token),
  252. })
  253. require.Equal(t, 200, rr.Code)
  254. // Cannot get account anymore
  255. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  256. "Authorization": util.BearerAuth(token.Token),
  257. })
  258. require.Equal(t, 401, rr.Code)
  259. }
  260. func TestAccount_Delete_Success(t *testing.T) {
  261. conf := newTestConfigWithAuthFile(t)
  262. conf.EnableSignup = true
  263. s := newTestServer(t, conf)
  264. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  265. require.Equal(t, 200, rr.Code)
  266. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  267. "Authorization": util.BasicAuth("phil", "mypass"),
  268. })
  269. require.Equal(t, 200, rr.Code)
  270. rr = request(t, s, "DELETE", "/v1/account", "", map[string]string{
  271. "Authorization": util.BasicAuth("phil", "mypass"),
  272. })
  273. require.Equal(t, 200, rr.Code)
  274. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  275. "Authorization": util.BasicAuth("phil", "mypass"),
  276. })
  277. require.Equal(t, 401, rr.Code)
  278. }
  279. func TestAccount_Delete_Not_Allowed(t *testing.T) {
  280. conf := newTestConfigWithAuthFile(t)
  281. conf.EnableSignup = true
  282. s := newTestServer(t, conf)
  283. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  284. require.Equal(t, 200, rr.Code)
  285. rr = request(t, s, "DELETE", "/v1/account", "", nil)
  286. require.Equal(t, 401, rr.Code)
  287. }
  288. func TestAccount_Reservation_Add_User_No_Plan_Failure(t *testing.T) {
  289. conf := newTestConfigWithAuthFile(t)
  290. conf.EnableSignup = true
  291. s := newTestServer(t, conf)
  292. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  293. require.Equal(t, 200, rr.Code)
  294. rr = request(t, s, "POST", "/v1/account/access", `{"topic":"mytopic", "everyone":"deny-all"}`, map[string]string{
  295. "Authorization": util.BasicAuth("phil", "mypass"),
  296. })
  297. require.Equal(t, 401, rr.Code)
  298. }
  299. func TestAccount_Reservation_Add_Admin_Success(t *testing.T) {
  300. conf := newTestConfigWithAuthFile(t)
  301. conf.EnableSignup = true
  302. s := newTestServer(t, conf)
  303. require.Nil(t, s.userManager.AddUser("phil", "adminpass", user.RoleAdmin))
  304. rr := request(t, s, "POST", "/v1/account/access", `{"topic":"mytopic","everyone":"deny-all"}`, map[string]string{
  305. "Authorization": util.BasicAuth("phil", "adminpass"),
  306. })
  307. require.Equal(t, 400, rr.Code)
  308. require.Equal(t, 40026, toHTTPError(t, rr.Body.String()).Code)
  309. }
  310. func TestAccount_Reservation_Add_Remove_User_With_Plan_Success(t *testing.T) {
  311. conf := newTestConfigWithAuthFile(t)
  312. conf.EnableSignup = true
  313. s := newTestServer(t, conf)
  314. // Create user
  315. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  316. require.Equal(t, 200, rr.Code)
  317. // Create a plan (hack!)
  318. db, err := sql.Open("sqlite3", conf.AuthFile)
  319. require.Nil(t, err)
  320. _, err = db.Exec(`
  321. INSERT INTO plan (id, code, messages_limit, messages_expiry_duration, emails_limit, attachment_file_size_limit, attachment_total_size_limit, attachment_expiry_duration, topics_limit)
  322. VALUES (1, 'testplan', 10, 86400, 10, 10, 10, 10800, 2);
  323. UPDATE user SET plan_id = 1 WHERE user = 'phil';
  324. `)
  325. require.Nil(t, err)
  326. // Reserve two topics
  327. rr = request(t, s, "POST", "/v1/account/access", `{"topic": "mytopic", "everyone":"deny-all"}`, map[string]string{
  328. "Authorization": util.BasicAuth("phil", "mypass"),
  329. })
  330. require.Equal(t, 200, rr.Code)
  331. rr = request(t, s, "POST", "/v1/account/access", `{"topic": "another", "everyone":"read-only"}`, map[string]string{
  332. "Authorization": util.BasicAuth("phil", "mypass"),
  333. })
  334. require.Equal(t, 200, rr.Code)
  335. // Trying to reserve a third should fail
  336. rr = request(t, s, "POST", "/v1/account/access", `{"topic": "yet-another", "everyone":"deny-all"}`, map[string]string{
  337. "Authorization": util.BasicAuth("phil", "mypass"),
  338. })
  339. require.Equal(t, 429, rr.Code)
  340. // Modify existing should still work
  341. rr = request(t, s, "POST", "/v1/account/access", `{"topic": "another", "everyone":"write-only"}`, map[string]string{
  342. "Authorization": util.BasicAuth("phil", "mypass"),
  343. })
  344. require.Equal(t, 200, rr.Code)
  345. // Check account result
  346. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  347. "Authorization": util.BasicAuth("phil", "mypass"),
  348. })
  349. require.Equal(t, 200, rr.Code)
  350. account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  351. require.Equal(t, 2, len(account.Reservations))
  352. require.Equal(t, "another", account.Reservations[0].Topic)
  353. require.Equal(t, "write-only", account.Reservations[0].Everyone)
  354. require.Equal(t, "mytopic", account.Reservations[1].Topic)
  355. require.Equal(t, "deny-all", account.Reservations[1].Everyone)
  356. // Delete and re-check
  357. rr = request(t, s, "DELETE", "/v1/account/access/another", "", map[string]string{
  358. "Authorization": util.BasicAuth("phil", "mypass"),
  359. })
  360. require.Equal(t, 200, rr.Code)
  361. rr = request(t, s, "GET", "/v1/account", "", map[string]string{
  362. "Authorization": util.BasicAuth("phil", "mypass"),
  363. })
  364. require.Equal(t, 200, rr.Code)
  365. account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body))
  366. require.Equal(t, 1, len(account.Reservations))
  367. require.Equal(t, "mytopic", account.Reservations[0].Topic)
  368. }
  369. func TestAccount_Reservation_Add_Access_By_Anonymous_Fails(t *testing.T) {
  370. conf := newTestConfigWithAuthFile(t)
  371. conf.AuthDefault = user.PermissionReadWrite
  372. conf.EnableSignup = true
  373. s := newTestServer(t, conf)
  374. // Create user
  375. rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
  376. require.Equal(t, 200, rr.Code)
  377. // Create a plan (hack!)
  378. db, err := sql.Open("sqlite3", conf.AuthFile)
  379. require.Nil(t, err)
  380. _, err = db.Exec(`
  381. INSERT INTO plan (id, code, messages_limit, messages_expiry_duration, emails_limit, attachment_file_size_limit, attachment_total_size_limit, attachment_expiry_duration, topics_limit)
  382. VALUES (1, 'testplan', 10, 86400, 10, 10, 10, 10800, 2);
  383. UPDATE user SET plan_id = 1 WHERE user = 'phil';
  384. `)
  385. require.Nil(t, err)
  386. // Reserve a topic
  387. rr = request(t, s, "POST", "/v1/account/access", `{"topic": "mytopic", "everyone":"deny-all"}`, map[string]string{
  388. "Authorization": util.BasicAuth("phil", "mypass"),
  389. })
  390. require.Equal(t, 200, rr.Code)
  391. // Publish a message
  392. rr = request(t, s, "POST", "/mytopic", `Howdy`, map[string]string{
  393. "Authorization": util.BasicAuth("phil", "mypass"),
  394. })
  395. require.Equal(t, 200, rr.Code)
  396. // Publish a message (as anonymous)
  397. rr = request(t, s, "POST", "/mytopic", `Howdy`, nil)
  398. require.Equal(t, 403, rr.Code)
  399. }