response_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. //go:build unit
  2. package response
  3. import (
  4. "encoding/json"
  5. "errors"
  6. "net/http"
  7. "net/http/httptest"
  8. "testing"
  9. errors2 "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  10. "github.com/gin-gonic/gin"
  11. "github.com/stretchr/testify/require"
  12. )
  13. // ---------- 辅助函数 ----------
  14. // parseResponseBody 从 httptest.ResponseRecorder 中解析 JSON 响应体
  15. func parseResponseBody(t *testing.T, w *httptest.ResponseRecorder) Response {
  16. t.Helper()
  17. var got Response
  18. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &got))
  19. return got
  20. }
  21. // parsePaginatedBody 从响应体中解析分页数据(Data 字段是 PaginatedData)
  22. func parsePaginatedBody(t *testing.T, w *httptest.ResponseRecorder) (Response, PaginatedData) {
  23. t.Helper()
  24. // 先用 raw json 解析,因为 Data 是 any 类型
  25. var raw struct {
  26. Code int `json:"code"`
  27. Message string `json:"message"`
  28. Reason string `json:"reason,omitempty"`
  29. Data json.RawMessage `json:"data,omitempty"`
  30. }
  31. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &raw))
  32. var pd PaginatedData
  33. require.NoError(t, json.Unmarshal(raw.Data, &pd))
  34. return Response{Code: raw.Code, Message: raw.Message, Reason: raw.Reason}, pd
  35. }
  36. // newContextWithQuery 创建一个带有 URL query 参数的 gin.Context 用于测试 ParsePagination
  37. func newContextWithQuery(query string) (*httptest.ResponseRecorder, *gin.Context) {
  38. w := httptest.NewRecorder()
  39. c, _ := gin.CreateTestContext(w)
  40. c.Request = httptest.NewRequest(http.MethodGet, "/?"+query, nil)
  41. return w, c
  42. }
  43. // ---------- 现有测试 ----------
  44. func TestErrorWithDetails(t *testing.T) {
  45. gin.SetMode(gin.TestMode)
  46. tests := []struct {
  47. name string
  48. statusCode int
  49. message string
  50. reason string
  51. metadata map[string]string
  52. want Response
  53. }{
  54. {
  55. name: "plain_error",
  56. statusCode: http.StatusBadRequest,
  57. message: "invalid request",
  58. want: Response{
  59. Code: http.StatusBadRequest,
  60. Message: "invalid request",
  61. },
  62. },
  63. {
  64. name: "structured_error",
  65. statusCode: http.StatusForbidden,
  66. message: "no access",
  67. reason: "FORBIDDEN",
  68. metadata: map[string]string{"k": "v"},
  69. want: Response{
  70. Code: http.StatusForbidden,
  71. Message: "no access",
  72. Reason: "FORBIDDEN",
  73. Metadata: map[string]string{"k": "v"},
  74. },
  75. },
  76. }
  77. for _, tt := range tests {
  78. t.Run(tt.name, func(t *testing.T) {
  79. w := httptest.NewRecorder()
  80. c, _ := gin.CreateTestContext(w)
  81. ErrorWithDetails(c, tt.statusCode, tt.message, tt.reason, tt.metadata)
  82. require.Equal(t, tt.statusCode, w.Code)
  83. var got Response
  84. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &got))
  85. require.Equal(t, tt.want, got)
  86. })
  87. }
  88. }
  89. func TestErrorFrom(t *testing.T) {
  90. gin.SetMode(gin.TestMode)
  91. tests := []struct {
  92. name string
  93. err error
  94. wantWritten bool
  95. wantHTTPCode int
  96. wantBody Response
  97. }{
  98. {
  99. name: "nil_error",
  100. err: nil,
  101. wantWritten: false,
  102. },
  103. {
  104. name: "application_error",
  105. err: errors2.Forbidden("FORBIDDEN", "no access").WithMetadata(map[string]string{"scope": "admin"}),
  106. wantWritten: true,
  107. wantHTTPCode: http.StatusForbidden,
  108. wantBody: Response{
  109. Code: http.StatusForbidden,
  110. Message: "no access",
  111. Reason: "FORBIDDEN",
  112. Metadata: map[string]string{"scope": "admin"},
  113. },
  114. },
  115. {
  116. name: "bad_request_error",
  117. err: errors2.BadRequest("INVALID_REQUEST", "invalid request"),
  118. wantWritten: true,
  119. wantHTTPCode: http.StatusBadRequest,
  120. wantBody: Response{
  121. Code: http.StatusBadRequest,
  122. Message: "invalid request",
  123. Reason: "INVALID_REQUEST",
  124. },
  125. },
  126. {
  127. name: "unauthorized_error",
  128. err: errors2.Unauthorized("UNAUTHORIZED", "unauthorized"),
  129. wantWritten: true,
  130. wantHTTPCode: http.StatusUnauthorized,
  131. wantBody: Response{
  132. Code: http.StatusUnauthorized,
  133. Message: "unauthorized",
  134. Reason: "UNAUTHORIZED",
  135. },
  136. },
  137. {
  138. name: "not_found_error",
  139. err: errors2.NotFound("NOT_FOUND", "not found"),
  140. wantWritten: true,
  141. wantHTTPCode: http.StatusNotFound,
  142. wantBody: Response{
  143. Code: http.StatusNotFound,
  144. Message: "not found",
  145. Reason: "NOT_FOUND",
  146. },
  147. },
  148. {
  149. name: "conflict_error",
  150. err: errors2.Conflict("CONFLICT", "conflict"),
  151. wantWritten: true,
  152. wantHTTPCode: http.StatusConflict,
  153. wantBody: Response{
  154. Code: http.StatusConflict,
  155. Message: "conflict",
  156. Reason: "CONFLICT",
  157. },
  158. },
  159. {
  160. name: "unknown_error_defaults_to_500",
  161. err: errors.New("boom"),
  162. wantWritten: true,
  163. wantHTTPCode: http.StatusInternalServerError,
  164. wantBody: Response{
  165. Code: http.StatusInternalServerError,
  166. Message: errors2.UnknownMessage,
  167. },
  168. },
  169. }
  170. for _, tt := range tests {
  171. t.Run(tt.name, func(t *testing.T) {
  172. w := httptest.NewRecorder()
  173. c, _ := gin.CreateTestContext(w)
  174. written := ErrorFrom(c, tt.err)
  175. require.Equal(t, tt.wantWritten, written)
  176. if !tt.wantWritten {
  177. require.Equal(t, 200, w.Code)
  178. require.Empty(t, w.Body.String())
  179. return
  180. }
  181. require.Equal(t, tt.wantHTTPCode, w.Code)
  182. var got Response
  183. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &got))
  184. require.Equal(t, tt.wantBody, got)
  185. })
  186. }
  187. }
  188. // ---------- 新增测试 ----------
  189. func TestSuccess(t *testing.T) {
  190. gin.SetMode(gin.TestMode)
  191. tests := []struct {
  192. name string
  193. data any
  194. wantCode int
  195. wantBody Response
  196. }{
  197. {
  198. name: "返回字符串数据",
  199. data: "hello",
  200. wantCode: http.StatusOK,
  201. wantBody: Response{Code: 0, Message: "success", Data: "hello"},
  202. },
  203. {
  204. name: "返回nil数据",
  205. data: nil,
  206. wantCode: http.StatusOK,
  207. wantBody: Response{Code: 0, Message: "success"},
  208. },
  209. {
  210. name: "返回map数据",
  211. data: map[string]string{"key": "value"},
  212. wantCode: http.StatusOK,
  213. wantBody: Response{Code: 0, Message: "success"},
  214. },
  215. }
  216. for _, tt := range tests {
  217. t.Run(tt.name, func(t *testing.T) {
  218. w := httptest.NewRecorder()
  219. c, _ := gin.CreateTestContext(w)
  220. Success(c, tt.data)
  221. require.Equal(t, tt.wantCode, w.Code)
  222. // 只验证 code 和 message,data 字段类型在 JSON 反序列化时会变成 map/slice
  223. got := parseResponseBody(t, w)
  224. require.Equal(t, 0, got.Code)
  225. require.Equal(t, "success", got.Message)
  226. if tt.data == nil {
  227. require.Nil(t, got.Data)
  228. } else {
  229. require.NotNil(t, got.Data)
  230. }
  231. })
  232. }
  233. }
  234. func TestCreated(t *testing.T) {
  235. gin.SetMode(gin.TestMode)
  236. tests := []struct {
  237. name string
  238. data any
  239. wantCode int
  240. }{
  241. {
  242. name: "创建成功_返回数据",
  243. data: map[string]int{"id": 42},
  244. wantCode: http.StatusCreated,
  245. },
  246. {
  247. name: "创建成功_nil数据",
  248. data: nil,
  249. wantCode: http.StatusCreated,
  250. },
  251. }
  252. for _, tt := range tests {
  253. t.Run(tt.name, func(t *testing.T) {
  254. w := httptest.NewRecorder()
  255. c, _ := gin.CreateTestContext(w)
  256. Created(c, tt.data)
  257. require.Equal(t, tt.wantCode, w.Code)
  258. got := parseResponseBody(t, w)
  259. require.Equal(t, 0, got.Code)
  260. require.Equal(t, "success", got.Message)
  261. })
  262. }
  263. }
  264. func TestError(t *testing.T) {
  265. gin.SetMode(gin.TestMode)
  266. tests := []struct {
  267. name string
  268. statusCode int
  269. message string
  270. }{
  271. {
  272. name: "400错误",
  273. statusCode: http.StatusBadRequest,
  274. message: "bad request",
  275. },
  276. {
  277. name: "500错误",
  278. statusCode: http.StatusInternalServerError,
  279. message: "internal error",
  280. },
  281. {
  282. name: "自定义状态码",
  283. statusCode: 418,
  284. message: "I'm a teapot",
  285. },
  286. }
  287. for _, tt := range tests {
  288. t.Run(tt.name, func(t *testing.T) {
  289. w := httptest.NewRecorder()
  290. c, _ := gin.CreateTestContext(w)
  291. Error(c, tt.statusCode, tt.message)
  292. require.Equal(t, tt.statusCode, w.Code)
  293. got := parseResponseBody(t, w)
  294. require.Equal(t, tt.statusCode, got.Code)
  295. require.Equal(t, tt.message, got.Message)
  296. require.Empty(t, got.Reason)
  297. require.Nil(t, got.Metadata)
  298. require.Nil(t, got.Data)
  299. })
  300. }
  301. }
  302. func TestBadRequest(t *testing.T) {
  303. gin.SetMode(gin.TestMode)
  304. w := httptest.NewRecorder()
  305. c, _ := gin.CreateTestContext(w)
  306. BadRequest(c, "参数无效")
  307. require.Equal(t, http.StatusBadRequest, w.Code)
  308. got := parseResponseBody(t, w)
  309. require.Equal(t, http.StatusBadRequest, got.Code)
  310. require.Equal(t, "参数无效", got.Message)
  311. }
  312. func TestUnauthorized(t *testing.T) {
  313. gin.SetMode(gin.TestMode)
  314. w := httptest.NewRecorder()
  315. c, _ := gin.CreateTestContext(w)
  316. Unauthorized(c, "未登录")
  317. require.Equal(t, http.StatusUnauthorized, w.Code)
  318. got := parseResponseBody(t, w)
  319. require.Equal(t, http.StatusUnauthorized, got.Code)
  320. require.Equal(t, "未登录", got.Message)
  321. }
  322. func TestForbidden(t *testing.T) {
  323. gin.SetMode(gin.TestMode)
  324. w := httptest.NewRecorder()
  325. c, _ := gin.CreateTestContext(w)
  326. Forbidden(c, "无权限")
  327. require.Equal(t, http.StatusForbidden, w.Code)
  328. got := parseResponseBody(t, w)
  329. require.Equal(t, http.StatusForbidden, got.Code)
  330. require.Equal(t, "无权限", got.Message)
  331. }
  332. func TestNotFound(t *testing.T) {
  333. gin.SetMode(gin.TestMode)
  334. w := httptest.NewRecorder()
  335. c, _ := gin.CreateTestContext(w)
  336. NotFound(c, "资源不存在")
  337. require.Equal(t, http.StatusNotFound, w.Code)
  338. got := parseResponseBody(t, w)
  339. require.Equal(t, http.StatusNotFound, got.Code)
  340. require.Equal(t, "资源不存在", got.Message)
  341. }
  342. func TestInternalError(t *testing.T) {
  343. gin.SetMode(gin.TestMode)
  344. w := httptest.NewRecorder()
  345. c, _ := gin.CreateTestContext(w)
  346. InternalError(c, "服务器内部错误")
  347. require.Equal(t, http.StatusInternalServerError, w.Code)
  348. got := parseResponseBody(t, w)
  349. require.Equal(t, http.StatusInternalServerError, got.Code)
  350. require.Equal(t, "服务器内部错误", got.Message)
  351. }
  352. func TestPaginated(t *testing.T) {
  353. gin.SetMode(gin.TestMode)
  354. tests := []struct {
  355. name string
  356. items any
  357. total int64
  358. page int
  359. pageSize int
  360. wantPages int
  361. wantTotal int64
  362. wantPage int
  363. wantPageSize int
  364. }{
  365. {
  366. name: "标准分页_多页",
  367. items: []string{"a", "b"},
  368. total: 25,
  369. page: 1,
  370. pageSize: 10,
  371. wantPages: 3,
  372. wantTotal: 25,
  373. wantPage: 1,
  374. wantPageSize: 10,
  375. },
  376. {
  377. name: "总数刚好整除",
  378. items: []string{"a"},
  379. total: 20,
  380. page: 2,
  381. pageSize: 10,
  382. wantPages: 2,
  383. wantTotal: 20,
  384. wantPage: 2,
  385. wantPageSize: 10,
  386. },
  387. {
  388. name: "总数为0_pages至少为1",
  389. items: []string{},
  390. total: 0,
  391. page: 1,
  392. pageSize: 10,
  393. wantPages: 1,
  394. wantTotal: 0,
  395. wantPage: 1,
  396. wantPageSize: 10,
  397. },
  398. {
  399. name: "单页数据",
  400. items: []int{1, 2, 3},
  401. total: 3,
  402. page: 1,
  403. pageSize: 20,
  404. wantPages: 1,
  405. wantTotal: 3,
  406. wantPage: 1,
  407. wantPageSize: 20,
  408. },
  409. {
  410. name: "总数为1",
  411. items: []string{"only"},
  412. total: 1,
  413. page: 1,
  414. pageSize: 10,
  415. wantPages: 1,
  416. wantTotal: 1,
  417. wantPage: 1,
  418. wantPageSize: 10,
  419. },
  420. }
  421. for _, tt := range tests {
  422. t.Run(tt.name, func(t *testing.T) {
  423. w := httptest.NewRecorder()
  424. c, _ := gin.CreateTestContext(w)
  425. Paginated(c, tt.items, tt.total, tt.page, tt.pageSize)
  426. require.Equal(t, http.StatusOK, w.Code)
  427. resp, pd := parsePaginatedBody(t, w)
  428. require.Equal(t, 0, resp.Code)
  429. require.Equal(t, "success", resp.Message)
  430. require.Equal(t, tt.wantTotal, pd.Total)
  431. require.Equal(t, tt.wantPage, pd.Page)
  432. require.Equal(t, tt.wantPageSize, pd.PageSize)
  433. require.Equal(t, tt.wantPages, pd.Pages)
  434. })
  435. }
  436. }
  437. func TestPaginatedWithResult(t *testing.T) {
  438. gin.SetMode(gin.TestMode)
  439. tests := []struct {
  440. name string
  441. items any
  442. pagination *PaginationResult
  443. wantTotal int64
  444. wantPage int
  445. wantPageSize int
  446. wantPages int
  447. }{
  448. {
  449. name: "正常分页结果",
  450. items: []string{"a", "b"},
  451. pagination: &PaginationResult{
  452. Total: 50,
  453. Page: 3,
  454. PageSize: 10,
  455. Pages: 5,
  456. },
  457. wantTotal: 50,
  458. wantPage: 3,
  459. wantPageSize: 10,
  460. wantPages: 5,
  461. },
  462. {
  463. name: "pagination为nil_使用默认值",
  464. items: []string{},
  465. pagination: nil,
  466. wantTotal: 0,
  467. wantPage: 1,
  468. wantPageSize: 20,
  469. wantPages: 1,
  470. },
  471. {
  472. name: "单页结果",
  473. items: []int{1},
  474. pagination: &PaginationResult{
  475. Total: 1,
  476. Page: 1,
  477. PageSize: 20,
  478. Pages: 1,
  479. },
  480. wantTotal: 1,
  481. wantPage: 1,
  482. wantPageSize: 20,
  483. wantPages: 1,
  484. },
  485. }
  486. for _, tt := range tests {
  487. t.Run(tt.name, func(t *testing.T) {
  488. w := httptest.NewRecorder()
  489. c, _ := gin.CreateTestContext(w)
  490. PaginatedWithResult(c, tt.items, tt.pagination)
  491. require.Equal(t, http.StatusOK, w.Code)
  492. resp, pd := parsePaginatedBody(t, w)
  493. require.Equal(t, 0, resp.Code)
  494. require.Equal(t, "success", resp.Message)
  495. require.Equal(t, tt.wantTotal, pd.Total)
  496. require.Equal(t, tt.wantPage, pd.Page)
  497. require.Equal(t, tt.wantPageSize, pd.PageSize)
  498. require.Equal(t, tt.wantPages, pd.Pages)
  499. })
  500. }
  501. }
  502. func TestParsePagination(t *testing.T) {
  503. gin.SetMode(gin.TestMode)
  504. tests := []struct {
  505. name string
  506. query string
  507. wantPage int
  508. wantPageSize int
  509. }{
  510. {
  511. name: "无参数_使用默认值",
  512. query: "",
  513. wantPage: 1,
  514. wantPageSize: 20,
  515. },
  516. {
  517. name: "仅指定page",
  518. query: "page=3",
  519. wantPage: 3,
  520. wantPageSize: 20,
  521. },
  522. {
  523. name: "仅指定page_size",
  524. query: "page_size=50",
  525. wantPage: 1,
  526. wantPageSize: 50,
  527. },
  528. {
  529. name: "同时指定page和page_size",
  530. query: "page=2&page_size=30",
  531. wantPage: 2,
  532. wantPageSize: 30,
  533. },
  534. {
  535. name: "使用limit代替page_size",
  536. query: "limit=15",
  537. wantPage: 1,
  538. wantPageSize: 15,
  539. },
  540. {
  541. name: "page_size优先于limit",
  542. query: "page_size=25&limit=50",
  543. wantPage: 1,
  544. wantPageSize: 25,
  545. },
  546. {
  547. name: "page为0_使用默认值",
  548. query: "page=0",
  549. wantPage: 1,
  550. wantPageSize: 20,
  551. },
  552. {
  553. name: "page_size超过1000_使用默认值",
  554. query: "page_size=1001",
  555. wantPage: 1,
  556. wantPageSize: 20,
  557. },
  558. {
  559. name: "page_size恰好1000_有效",
  560. query: "page_size=1000",
  561. wantPage: 1,
  562. wantPageSize: 1000,
  563. },
  564. {
  565. name: "page为非数字_使用默认值",
  566. query: "page=abc",
  567. wantPage: 1,
  568. wantPageSize: 20,
  569. },
  570. {
  571. name: "page_size为非数字_使用默认值",
  572. query: "page_size=xyz",
  573. wantPage: 1,
  574. wantPageSize: 20,
  575. },
  576. {
  577. name: "limit为非数字_使用默认值",
  578. query: "limit=abc",
  579. wantPage: 1,
  580. wantPageSize: 20,
  581. },
  582. {
  583. name: "page_size为0_使用默认值",
  584. query: "page_size=0",
  585. wantPage: 1,
  586. wantPageSize: 20,
  587. },
  588. {
  589. name: "limit为0_使用默认值",
  590. query: "limit=0",
  591. wantPage: 1,
  592. wantPageSize: 20,
  593. },
  594. {
  595. name: "大页码",
  596. query: "page=999&page_size=100",
  597. wantPage: 999,
  598. wantPageSize: 100,
  599. },
  600. {
  601. name: "page_size为1_最小有效值",
  602. query: "page_size=1",
  603. wantPage: 1,
  604. wantPageSize: 1,
  605. },
  606. {
  607. name: "混合数字和字母的page",
  608. query: "page=12a",
  609. wantPage: 1,
  610. wantPageSize: 20,
  611. },
  612. {
  613. name: "limit超过1000_使用默认值",
  614. query: "limit=2000",
  615. wantPage: 1,
  616. wantPageSize: 20,
  617. },
  618. }
  619. for _, tt := range tests {
  620. t.Run(tt.name, func(t *testing.T) {
  621. _, c := newContextWithQuery(tt.query)
  622. page, pageSize := ParsePagination(c)
  623. require.Equal(t, tt.wantPage, page, "page 不符合预期")
  624. require.Equal(t, tt.wantPageSize, pageSize, "pageSize 不符合预期")
  625. })
  626. }
  627. }
  628. func Test_parseInt(t *testing.T) {
  629. tests := []struct {
  630. name string
  631. input string
  632. wantVal int
  633. wantErr bool
  634. }{
  635. {
  636. name: "正常数字",
  637. input: "123",
  638. wantVal: 123,
  639. wantErr: false,
  640. },
  641. {
  642. name: "零",
  643. input: "0",
  644. wantVal: 0,
  645. wantErr: false,
  646. },
  647. {
  648. name: "单个数字",
  649. input: "5",
  650. wantVal: 5,
  651. wantErr: false,
  652. },
  653. {
  654. name: "大数字",
  655. input: "99999",
  656. wantVal: 99999,
  657. wantErr: false,
  658. },
  659. {
  660. name: "包含字母_返回0",
  661. input: "abc",
  662. wantVal: 0,
  663. wantErr: false,
  664. },
  665. {
  666. name: "数字开头接字母_返回0",
  667. input: "12a",
  668. wantVal: 0,
  669. wantErr: false,
  670. },
  671. {
  672. name: "包含负号_返回0",
  673. input: "-1",
  674. wantVal: 0,
  675. wantErr: false,
  676. },
  677. {
  678. name: "包含小数点_返回0",
  679. input: "1.5",
  680. wantVal: 0,
  681. wantErr: false,
  682. },
  683. {
  684. name: "包含空格_返回0",
  685. input: "1 2",
  686. wantVal: 0,
  687. wantErr: false,
  688. },
  689. {
  690. name: "空字符串",
  691. input: "",
  692. wantVal: 0,
  693. wantErr: false,
  694. },
  695. }
  696. for _, tt := range tests {
  697. t.Run(tt.name, func(t *testing.T) {
  698. val, err := parseInt(tt.input)
  699. if tt.wantErr {
  700. require.Error(t, err)
  701. } else {
  702. require.NoError(t, err)
  703. }
  704. require.Equal(t, tt.wantVal, val)
  705. })
  706. }
  707. }