[{"data":1,"prerenderedAt":4447},["ShallowReactive",2],{"navigation_docs":3,"-guides-testing":172,"-guides-testing-surround":4442},[4,26,67,98,124,140,161],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":25},"Getting Started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,20],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Quick Start","\u002Fgetting-started\u002Fquick-start","1.getting-started\u002F3.quick-start","i-lucide-play",false,{"title":27,"icon":28,"path":29,"stem":30,"children":31,"page":25},"Core","i-lucide-box","\u002Fcore","2.core",[32,37,42,47,52,57,62],{"title":33,"path":34,"stem":35,"icon":36},"Routing","\u002Fcore\u002Frouting","2.core\u002F1.routing","i-lucide-route",{"title":38,"path":39,"stem":40,"icon":41},"Handlers","\u002Fcore\u002Fhandlers","2.core\u002F2.handlers","i-lucide-braces",{"title":43,"path":44,"stem":45,"icon":46},"Request Binding","\u002Fcore\u002Frequest-binding","2.core\u002F3.request-binding","i-lucide-tags",{"title":48,"path":49,"stem":50,"icon":51},"Validation","\u002Fcore\u002Fvalidation","2.core\u002F4.validation","i-lucide-check-check",{"title":53,"path":54,"stem":55,"icon":56},"Context","\u002Fcore\u002Fcontext","2.core\u002F5.context","i-lucide-file-input",{"title":58,"path":59,"stem":60,"icon":61},"Errors","\u002Fcore\u002Ferrors","2.core\u002F6.errors","i-lucide-triangle-alert",{"title":63,"path":64,"stem":65,"icon":66},"Middleware","\u002Fcore\u002Fmiddleware","2.core\u002F7.middleware","i-lucide-layers",{"title":68,"icon":69,"path":70,"stem":71,"children":72,"page":25},"Auth","i-lucide-shield-check","\u002Fauth","3.auth",[73,78,83,88,93],{"title":74,"path":75,"stem":76,"icon":77},"Overview","\u002Fauth\u002Foverview","3.auth\u002F1.overview","i-lucide-shield",{"title":79,"path":80,"stem":81,"icon":82},"Auth Rules","\u002Fauth\u002Fauth-rules","3.auth\u002F2.auth-rules","i-lucide-lock",{"title":84,"path":85,"stem":86,"icon":87},"JWT Providers","\u002Fauth\u002Fjwt-providers","3.auth\u002F3.jwt-providers","i-lucide-key",{"title":89,"path":90,"stem":91,"icon":92},"Roles & Permissions","\u002Fauth\u002Froles-and-permissions","3.auth\u002F4.roles-and-permissions","i-lucide-users",{"title":94,"path":95,"stem":96,"icon":97},"Audit Logging","\u002Fauth\u002Faudit-logging","3.auth\u002F5.audit-logging","i-lucide-scroll-text",{"title":99,"icon":100,"path":101,"stem":102,"children":103,"page":25},"GoTrue","i-lucide-key-round","\u002Fgotrue","4.gotrue",[104,109,114,119],{"title":105,"path":106,"stem":107,"icon":108},"Proxy","\u002Fgotrue\u002Fproxy","4.gotrue\u002F1.proxy","i-lucide-shuffle",{"title":110,"path":111,"stem":112,"icon":113},"Endpoints","\u002Fgotrue\u002Fendpoints","4.gotrue\u002F2.endpoints","i-lucide-list",{"title":115,"path":116,"stem":117,"icon":118},"Client IP & Rate Limits","\u002Fgotrue\u002Fclient-ip-and-rate-limits","4.gotrue\u002F3.client-ip-and-rate-limits","i-lucide-network",{"title":120,"path":121,"stem":122,"icon":123},"Deployment","\u002Fgotrue\u002Fdeployment","4.gotrue\u002F4.deployment","i-lucide-server",{"title":125,"icon":126,"path":127,"stem":128,"children":129,"page":25},"API Docs","i-lucide-file-text","\u002Fapi-docs","5.api-docs",[130,135],{"title":131,"path":132,"stem":133,"icon":134},"OpenAPI","\u002Fapi-docs\u002Fopenapi","5.api-docs\u002F1.openapi","i-lucide-file-json",{"title":136,"path":137,"stem":138,"icon":139},"Scalar UI","\u002Fapi-docs\u002Fscalar","5.api-docs\u002F2.scalar","i-lucide-book-open-text",{"title":141,"icon":142,"path":143,"stem":144,"children":145,"page":25},"Guides","i-lucide-compass","\u002Fguides","6.guides",[146,151,156],{"title":147,"path":148,"stem":149,"icon":150},"Testing","\u002Fguides\u002Ftesting","6.guides\u002F1.testing","i-lucide-flask-conical",{"title":152,"path":153,"stem":154,"icon":155},"Performance","\u002Fguides\u002Fperformance","6.guides\u002F2.performance","i-lucide-gauge",{"title":157,"path":158,"stem":159,"icon":160},"Releasing","\u002Fguides\u002Freleasing","6.guides\u002F3.releasing","i-lucide-tag",{"title":162,"icon":163,"path":164,"stem":165,"children":166,"page":25},"Reference","i-lucide-code","\u002Freference","7.reference",[167],{"title":168,"path":169,"stem":170,"icon":171},"API Reference","\u002Freference\u002Fapi-reference","7.reference\u002F1.api-reference","i-lucide-list-tree",{"id":173,"title":147,"body":174,"description":4435,"extension":4436,"links":4437,"meta":4438,"navigation":4439,"path":148,"seo":4440,"stem":149,"__hash__":4441},"docs\u002F6.guides\u002F1.testing.md",{"type":175,"value":176,"toc":4424},"minimark",[177,194,199,202,685,688,692,698,950,1030,1054,1257,1260,1397,1407,1411,1414,1683,1710,1713,1717,2084,2088,2091,2343,2573,2576,3023,3027,3030,3253,3256,3717,3721,3735,4317,4321,4385,4400,4404,4420],[178,179,180,181,185,186,189,190,193],"p",{},"An ",[182,183,184],"code",{},"*ezz.App"," is an ",[182,187,188],{},"http.Handler",", so everything in ",[182,191,192],{},"net\u002Fhttp\u002Fhttptest"," works without adapters. ezz adds a few helpers for the repetitive parts.",[195,196,198],"h2",{"id":197},"test-a-route-with-httptest","Test a route with httptest",[178,200,201],{},"This is the approach with the fewest surprises: build the real app, send a real request, assert on the recorder.",[203,204,209],"pre",{"className":205,"code":206,"language":207,"meta":208,"style":208},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","func TestGetUser(t *testing.T) {\n    app := buildApp(testStore(t))     \u002F\u002F the same constructor main() uses\n\n    req := httptest.NewRequest(\"GET\", \"\u002Fapi\u002Fv1\u002Fusers\u002F42\", nil)\n    req.Header.Set(\"Authorization\", \"Bearer \"+testToken(t, \"member\"))\n\n    rec := httptest.NewRecorder()\n    app.ServeHTTP(rec, req)\n\n    if rec.Code != http.StatusOK {\n        t.Fatalf(\"got %d, body: %s\", rec.Code, rec.Body.String())\n    }\n\n    var user User\n    if err := json.Unmarshal(rec.Body.Bytes(), &user); err != nil {\n        t.Fatal(err)\n    }\n    if user.ID != \"42\" {\n        t.Fatalf(\"got %q\", user.ID)\n    }\n}\n","go","",[182,210,211,250,279,286,329,385,390,408,432,437,466,522,528,533,545,597,614,619,643,674,679],{"__ignoreMap":208},[212,213,216,220,224,227,231,234,238,241,244,247],"span",{"class":214,"line":215},"line",1,[212,217,219],{"class":218},"sMK4o","func",[212,221,223],{"class":222},"s2Zo4"," TestGetUser",[212,225,226],{"class":218},"(",[212,228,230],{"class":229},"sHdIc","t",[212,232,233],{"class":218}," *",[212,235,237],{"class":236},"sBMFI","testing",[212,239,240],{"class":218},".",[212,242,243],{"class":236},"T",[212,245,246],{"class":218},")",[212,248,249],{"class":218}," {\n",[212,251,253,257,260,263,265,268,270,272,275],{"class":214,"line":252},2,[212,254,256],{"class":255},"sTEyZ","    app ",[212,258,259],{"class":218},":=",[212,261,262],{"class":222}," buildApp",[212,264,226],{"class":218},[212,266,267],{"class":222},"testStore",[212,269,226],{"class":218},[212,271,230],{"class":255},[212,273,274],{"class":218},"))",[212,276,278],{"class":277},"sHwdD","     \u002F\u002F the same constructor main() uses\n",[212,280,282],{"class":214,"line":281},3,[212,283,285],{"emptyLinePlaceholder":284},true,"\n",[212,287,289,292,294,297,299,302,304,307,311,313,316,319,322,324,326],{"class":214,"line":288},4,[212,290,291],{"class":255},"    req ",[212,293,259],{"class":218},[212,295,296],{"class":255}," httptest",[212,298,240],{"class":218},[212,300,301],{"class":222},"NewRequest",[212,303,226],{"class":218},[212,305,306],{"class":218},"\"",[212,308,310],{"class":309},"sfazB","GET",[212,312,306],{"class":218},[212,314,315],{"class":218},",",[212,317,318],{"class":218}," \"",[212,320,321],{"class":309},"\u002Fapi\u002Fv1\u002Fusers\u002F42",[212,323,306],{"class":218},[212,325,315],{"class":218},[212,327,328],{"class":218}," nil)\n",[212,330,332,335,337,340,342,345,347,349,352,354,356,358,361,363,366,369,371,373,375,377,380,382],{"class":214,"line":331},5,[212,333,334],{"class":255},"    req",[212,336,240],{"class":218},[212,338,339],{"class":255},"Header",[212,341,240],{"class":218},[212,343,344],{"class":222},"Set",[212,346,226],{"class":218},[212,348,306],{"class":218},[212,350,351],{"class":309},"Authorization",[212,353,306],{"class":218},[212,355,315],{"class":218},[212,357,318],{"class":218},[212,359,360],{"class":309},"Bearer ",[212,362,306],{"class":218},[212,364,365],{"class":218},"+",[212,367,368],{"class":222},"testToken",[212,370,226],{"class":218},[212,372,230],{"class":255},[212,374,315],{"class":218},[212,376,318],{"class":218},[212,378,379],{"class":309},"member",[212,381,306],{"class":218},[212,383,384],{"class":218},"))\n",[212,386,388],{"class":214,"line":387},6,[212,389,285],{"emptyLinePlaceholder":284},[212,391,393,396,398,400,402,405],{"class":214,"line":392},7,[212,394,395],{"class":255},"    rec ",[212,397,259],{"class":218},[212,399,296],{"class":255},[212,401,240],{"class":218},[212,403,404],{"class":222},"NewRecorder",[212,406,407],{"class":218},"()\n",[212,409,411,414,416,419,421,424,426,429],{"class":214,"line":410},8,[212,412,413],{"class":255},"    app",[212,415,240],{"class":218},[212,417,418],{"class":222},"ServeHTTP",[212,420,226],{"class":218},[212,422,423],{"class":255},"rec",[212,425,315],{"class":218},[212,427,428],{"class":255}," req",[212,430,431],{"class":218},")\n",[212,433,435],{"class":214,"line":434},9,[212,436,285],{"emptyLinePlaceholder":284},[212,438,440,444,447,449,452,455,458,460,463],{"class":214,"line":439},10,[212,441,443],{"class":442},"s7zQu","    if",[212,445,446],{"class":255}," rec",[212,448,240],{"class":218},[212,450,451],{"class":255},"Code ",[212,453,454],{"class":218},"!=",[212,456,457],{"class":255}," http",[212,459,240],{"class":218},[212,461,462],{"class":255},"StatusOK ",[212,464,465],{"class":218},"{\n",[212,467,469,472,474,477,479,481,484,488,491,494,496,498,500,502,505,507,509,511,514,516,519],{"class":214,"line":468},11,[212,470,471],{"class":255},"        t",[212,473,240],{"class":218},[212,475,476],{"class":222},"Fatalf",[212,478,226],{"class":218},[212,480,306],{"class":218},[212,482,483],{"class":309},"got ",[212,485,487],{"class":486},"swJcz","%d",[212,489,490],{"class":309},", body: ",[212,492,493],{"class":486},"%s",[212,495,306],{"class":218},[212,497,315],{"class":218},[212,499,446],{"class":255},[212,501,240],{"class":218},[212,503,504],{"class":255},"Code",[212,506,315],{"class":218},[212,508,446],{"class":255},[212,510,240],{"class":218},[212,512,513],{"class":255},"Body",[212,515,240],{"class":218},[212,517,518],{"class":222},"String",[212,520,521],{"class":218},"())\n",[212,523,525],{"class":214,"line":524},12,[212,526,527],{"class":218},"    }\n",[212,529,531],{"class":214,"line":530},13,[212,532,285],{"emptyLinePlaceholder":284},[212,534,536,539,542],{"class":214,"line":535},14,[212,537,538],{"class":218},"    var",[212,540,541],{"class":255}," user ",[212,543,544],{"class":236},"User\n",[212,546,548,550,553,555,558,560,563,565,567,569,571,573,576,579,582,585,588,590,592,595],{"class":214,"line":547},15,[212,549,443],{"class":442},[212,551,552],{"class":255}," err ",[212,554,259],{"class":218},[212,556,557],{"class":255}," json",[212,559,240],{"class":218},[212,561,562],{"class":222},"Unmarshal",[212,564,226],{"class":218},[212,566,423],{"class":255},[212,568,240],{"class":218},[212,570,513],{"class":255},[212,572,240],{"class":218},[212,574,575],{"class":222},"Bytes",[212,577,578],{"class":218},"(),",[212,580,581],{"class":218}," &",[212,583,584],{"class":255},"user",[212,586,587],{"class":218},");",[212,589,552],{"class":255},[212,591,454],{"class":218},[212,593,594],{"class":218}," nil",[212,596,249],{"class":218},[212,598,600,602,604,607,609,612],{"class":214,"line":599},16,[212,601,471],{"class":255},[212,603,240],{"class":218},[212,605,606],{"class":222},"Fatal",[212,608,226],{"class":218},[212,610,611],{"class":255},"err",[212,613,431],{"class":218},[212,615,617],{"class":214,"line":616},17,[212,618,527],{"class":218},[212,620,622,624,627,629,632,634,636,639,641],{"class":214,"line":621},18,[212,623,443],{"class":442},[212,625,626],{"class":255}," user",[212,628,240],{"class":218},[212,630,631],{"class":255},"ID ",[212,633,454],{"class":218},[212,635,318],{"class":218},[212,637,638],{"class":309},"42",[212,640,306],{"class":218},[212,642,249],{"class":218},[212,644,646,648,650,652,654,656,658,661,663,665,667,669,672],{"class":214,"line":645},19,[212,647,471],{"class":255},[212,649,240],{"class":218},[212,651,476],{"class":222},[212,653,226],{"class":218},[212,655,306],{"class":218},[212,657,483],{"class":309},[212,659,660],{"class":486},"%q",[212,662,306],{"class":218},[212,664,315],{"class":218},[212,666,626],{"class":255},[212,668,240],{"class":218},[212,670,671],{"class":255},"ID",[212,673,431],{"class":218},[212,675,677],{"class":214,"line":676},20,[212,678,527],{"class":218},[212,680,682],{"class":214,"line":681},21,[212,683,684],{"class":218},"}\n",[178,686,687],{},"Build the app through a shared function so tests exercise the real middleware, auth rules, and route table rather than a simplified copy.",[195,689,691],{"id":690},"use-the-test-helpers","Use the test helpers",[178,693,694,697],{},[182,695,696],{},"ezz.NewTestApp()"," wraps an app with request helpers and fluent assertions.",[203,699,701],{"className":205,"code":700,"language":207,"meta":208,"style":208},"func TestHealth(t *testing.T) {\n    ta := ezz.NewTestApp()\n\n    ta.App().GET(\"\u002Fhealth\", ezz.HFunc(func(ctx *ezz.Context) (map[string]string, error) {\n        return map[string]string{\"status\": \"ok\"}, nil\n    })).Public()\n\n    ta.GET(\"\u002Fhealth\").\n        AssertStatus(t, 200).\n        AssertJSON(t, map[string]string{\"status\": \"ok\"})\n}\n",[182,702,703,726,743,747,818,858,868,872,891,908,946],{"__ignoreMap":208},[212,704,705,707,710,712,714,716,718,720,722,724],{"class":214,"line":215},[212,706,219],{"class":218},[212,708,709],{"class":222}," TestHealth",[212,711,226],{"class":218},[212,713,230],{"class":229},[212,715,233],{"class":218},[212,717,237],{"class":236},[212,719,240],{"class":218},[212,721,243],{"class":236},[212,723,246],{"class":218},[212,725,249],{"class":218},[212,727,728,731,733,736,738,741],{"class":214,"line":252},[212,729,730],{"class":255},"    ta ",[212,732,259],{"class":218},[212,734,735],{"class":255}," ezz",[212,737,240],{"class":218},[212,739,740],{"class":222},"NewTestApp",[212,742,407],{"class":218},[212,744,745],{"class":214,"line":281},[212,746,285],{"emptyLinePlaceholder":284},[212,748,749,752,754,757,760,762,764,766,769,771,773,775,777,780,783,786,788,791,793,795,797,800,804,807,809,811,814,816],{"class":214,"line":288},[212,750,751],{"class":255},"    ta",[212,753,240],{"class":218},[212,755,756],{"class":222},"App",[212,758,759],{"class":218},"().",[212,761,310],{"class":222},[212,763,226],{"class":218},[212,765,306],{"class":218},[212,767,768],{"class":309},"\u002Fhealth",[212,770,306],{"class":218},[212,772,315],{"class":218},[212,774,735],{"class":255},[212,776,240],{"class":218},[212,778,779],{"class":222},"HFunc",[212,781,782],{"class":218},"(func(",[212,784,785],{"class":229},"ctx",[212,787,233],{"class":218},[212,789,790],{"class":236},"ezz",[212,792,240],{"class":218},[212,794,53],{"class":236},[212,796,246],{"class":218},[212,798,799],{"class":218}," (map[",[212,801,803],{"class":802},"spNyl","string",[212,805,806],{"class":218},"]",[212,808,803],{"class":802},[212,810,315],{"class":218},[212,812,813],{"class":802}," error",[212,815,246],{"class":218},[212,817,249],{"class":218},[212,819,820,823,826,828,830,832,835,837,840,842,845,847,850,852,855],{"class":214,"line":331},[212,821,822],{"class":442},"        return",[212,824,825],{"class":218}," map[",[212,827,803],{"class":802},[212,829,806],{"class":218},[212,831,803],{"class":802},[212,833,834],{"class":218},"{",[212,836,306],{"class":218},[212,838,839],{"class":309},"status",[212,841,306],{"class":218},[212,843,844],{"class":218},":",[212,846,318],{"class":218},[212,848,849],{"class":309},"ok",[212,851,306],{"class":218},[212,853,854],{"class":218},"},",[212,856,857],{"class":218}," nil\n",[212,859,860,863,866],{"class":214,"line":387},[212,861,862],{"class":218},"    })).",[212,864,865],{"class":222},"Public",[212,867,407],{"class":218},[212,869,870],{"class":214,"line":392},[212,871,285],{"emptyLinePlaceholder":284},[212,873,874,876,878,880,882,884,886,888],{"class":214,"line":410},[212,875,751],{"class":255},[212,877,240],{"class":218},[212,879,310],{"class":222},[212,881,226],{"class":218},[212,883,306],{"class":218},[212,885,768],{"class":309},[212,887,306],{"class":218},[212,889,890],{"class":218},").\n",[212,892,893,896,898,900,902,906],{"class":214,"line":434},[212,894,895],{"class":222},"        AssertStatus",[212,897,226],{"class":218},[212,899,230],{"class":255},[212,901,315],{"class":218},[212,903,905],{"class":904},"sbssI"," 200",[212,907,890],{"class":218},[212,909,910,913,915,917,919,921,923,925,927,929,931,933,935,937,939,941,943],{"class":214,"line":439},[212,911,912],{"class":222},"        AssertJSON",[212,914,226],{"class":218},[212,916,230],{"class":255},[212,918,315],{"class":218},[212,920,825],{"class":218},[212,922,803],{"class":802},[212,924,806],{"class":218},[212,926,803],{"class":802},[212,928,834],{"class":218},[212,930,306],{"class":218},[212,932,839],{"class":309},[212,934,306],{"class":218},[212,936,844],{"class":218},[212,938,318],{"class":218},[212,940,849],{"class":309},[212,942,306],{"class":218},[212,944,945],{"class":218},"})\n",[212,947,948],{"class":214,"line":468},[212,949,684],{"class":218},[951,952,953,966],"table",{},[954,955,956],"thead",{},[957,958,959,963],"tr",{},[960,961,962],"th",{},"Helper",[960,964,965],{},"Use",[967,968,969,983,993,1003,1020],"tbody",{},[957,970,971,977],{},[972,973,974],"td",{},[182,975,976],{},"ta.App()",[972,978,979,980,982],{},"The underlying ",[182,981,184],{},", for registering routes and providers",[957,984,985,990],{},[972,986,987],{},[182,988,989],{},"ta.GET(path, headers...)",[972,991,992],{},"Send a GET",[957,994,995,1000],{},[972,996,997],{},[182,998,999],{},"ta.POST(path, body, headers...)",[972,1001,1002],{},"Send a POST; a struct body is marshalled as JSON",[957,1004,1005,1017],{},[972,1006,1007,1010,1011,1010,1014],{},[182,1008,1009],{},"ta.PUT",", ",[182,1012,1013],{},"ta.PATCH",[182,1015,1016],{},"ta.DELETE",[972,1018,1019],{},"The same for other methods",[957,1021,1022,1027],{},[972,1023,1024],{},[182,1025,1026],{},"ta.Request(method, path, body, headers...)",[972,1028,1029],{},"Anything else",[178,1031,1032,1033,1036,1037,1010,1040,1010,1043,1010,1046,1049,1050,1053],{},"A ",[182,1034,1035],{},"*TestResponse"," gives you ",[182,1038,1039],{},"StatusCode()",[182,1041,1042],{},"Header()",[182,1044,1045],{},"BodyString()",[182,1047,1048],{},"JSON(&v)",", and the raw ",[182,1051,1052],{},"Recorder",", plus assertions:",[203,1055,1057],{"className":205,"code":1056,"language":207,"meta":208,"style":208},"resp := ta.POST(\"\u002Fusers\", CreateUserRequest{Name: \"Ada\", Email: \"ada@example.com\"})\n\nresp.AssertStatus(t, 201).\n    AssertHeader(t, \"Location\", \"\u002Fusers\u002F1\").\n    AssertBodyContains(t, \"Ada\")\n\nvar created User\nif err := resp.JSON(&created); err != nil {\n    t.Fatal(err)\n}\n",[182,1058,1059,1118,1122,1143,1172,1191,1195,1205,1238,1253],{"__ignoreMap":208},[212,1060,1061,1064,1066,1069,1071,1074,1076,1078,1081,1083,1085,1088,1090,1093,1095,1097,1100,1102,1104,1107,1109,1111,1114,1116],{"class":214,"line":215},[212,1062,1063],{"class":255},"resp ",[212,1065,259],{"class":218},[212,1067,1068],{"class":255}," ta",[212,1070,240],{"class":218},[212,1072,1073],{"class":222},"POST",[212,1075,226],{"class":218},[212,1077,306],{"class":218},[212,1079,1080],{"class":309},"\u002Fusers",[212,1082,306],{"class":218},[212,1084,315],{"class":218},[212,1086,1087],{"class":236}," CreateUserRequest",[212,1089,834],{"class":218},[212,1091,1092],{"class":255},"Name",[212,1094,844],{"class":218},[212,1096,318],{"class":218},[212,1098,1099],{"class":309},"Ada",[212,1101,306],{"class":218},[212,1103,315],{"class":218},[212,1105,1106],{"class":255}," Email",[212,1108,844],{"class":218},[212,1110,318],{"class":218},[212,1112,1113],{"class":309},"ada@example.com",[212,1115,306],{"class":218},[212,1117,945],{"class":218},[212,1119,1120],{"class":214,"line":252},[212,1121,285],{"emptyLinePlaceholder":284},[212,1123,1124,1127,1129,1132,1134,1136,1138,1141],{"class":214,"line":281},[212,1125,1126],{"class":255},"resp",[212,1128,240],{"class":218},[212,1130,1131],{"class":222},"AssertStatus",[212,1133,226],{"class":218},[212,1135,230],{"class":255},[212,1137,315],{"class":218},[212,1139,1140],{"class":904}," 201",[212,1142,890],{"class":218},[212,1144,1145,1148,1150,1152,1154,1156,1159,1161,1163,1165,1168,1170],{"class":214,"line":288},[212,1146,1147],{"class":222},"    AssertHeader",[212,1149,226],{"class":218},[212,1151,230],{"class":255},[212,1153,315],{"class":218},[212,1155,318],{"class":218},[212,1157,1158],{"class":309},"Location",[212,1160,306],{"class":218},[212,1162,315],{"class":218},[212,1164,318],{"class":218},[212,1166,1167],{"class":309},"\u002Fusers\u002F1",[212,1169,306],{"class":218},[212,1171,890],{"class":218},[212,1173,1174,1177,1179,1181,1183,1185,1187,1189],{"class":214,"line":331},[212,1175,1176],{"class":222},"    AssertBodyContains",[212,1178,226],{"class":218},[212,1180,230],{"class":255},[212,1182,315],{"class":218},[212,1184,318],{"class":218},[212,1186,1099],{"class":309},[212,1188,306],{"class":218},[212,1190,431],{"class":218},[212,1192,1193],{"class":214,"line":387},[212,1194,285],{"emptyLinePlaceholder":284},[212,1196,1197,1200,1203],{"class":214,"line":392},[212,1198,1199],{"class":218},"var",[212,1201,1202],{"class":255}," created ",[212,1204,544],{"class":236},[212,1206,1207,1210,1212,1214,1217,1219,1222,1225,1228,1230,1232,1234,1236],{"class":214,"line":410},[212,1208,1209],{"class":442},"if",[212,1211,552],{"class":255},[212,1213,259],{"class":218},[212,1215,1216],{"class":255}," resp",[212,1218,240],{"class":218},[212,1220,1221],{"class":222},"JSON",[212,1223,1224],{"class":218},"(&",[212,1226,1227],{"class":255},"created",[212,1229,587],{"class":218},[212,1231,552],{"class":255},[212,1233,454],{"class":218},[212,1235,594],{"class":218},[212,1237,249],{"class":218},[212,1239,1240,1243,1245,1247,1249,1251],{"class":214,"line":434},[212,1241,1242],{"class":255},"    t",[212,1244,240],{"class":218},[212,1246,606],{"class":222},[212,1248,226],{"class":218},[212,1250,611],{"class":255},[212,1252,431],{"class":218},[212,1254,1255],{"class":214,"line":439},[212,1256,684],{"class":218},[178,1258,1259],{},"Header helpers keep call sites short:",[203,1261,1263],{"className":205,"code":1262,"language":207,"meta":208,"style":208},"ta.GET(\"\u002Fme\", ezz.WithJWT(token))\nta.POST(\"\u002Fupload\", body, ezz.WithContentType(\"application\u002Foctet-stream\"))\nta.GET(\"\u002Fitems\", ezz.WithHeaders(map[string]string{\"X-Tenant\": \"acme\"}))\n",[182,1264,1265,1299,1341],{"__ignoreMap":208},[212,1266,1267,1270,1272,1274,1276,1278,1281,1283,1285,1287,1289,1292,1294,1297],{"class":214,"line":215},[212,1268,1269],{"class":255},"ta",[212,1271,240],{"class":218},[212,1273,310],{"class":222},[212,1275,226],{"class":218},[212,1277,306],{"class":218},[212,1279,1280],{"class":309},"\u002Fme",[212,1282,306],{"class":218},[212,1284,315],{"class":218},[212,1286,735],{"class":255},[212,1288,240],{"class":218},[212,1290,1291],{"class":222},"WithJWT",[212,1293,226],{"class":218},[212,1295,1296],{"class":255},"token",[212,1298,384],{"class":218},[212,1300,1301,1303,1305,1307,1309,1311,1314,1316,1318,1321,1323,1325,1327,1330,1332,1334,1337,1339],{"class":214,"line":252},[212,1302,1269],{"class":255},[212,1304,240],{"class":218},[212,1306,1073],{"class":222},[212,1308,226],{"class":218},[212,1310,306],{"class":218},[212,1312,1313],{"class":309},"\u002Fupload",[212,1315,306],{"class":218},[212,1317,315],{"class":218},[212,1319,1320],{"class":255}," body",[212,1322,315],{"class":218},[212,1324,735],{"class":255},[212,1326,240],{"class":218},[212,1328,1329],{"class":222},"WithContentType",[212,1331,226],{"class":218},[212,1333,306],{"class":218},[212,1335,1336],{"class":309},"application\u002Foctet-stream",[212,1338,306],{"class":218},[212,1340,384],{"class":218},[212,1342,1343,1345,1347,1349,1351,1353,1356,1358,1360,1362,1364,1367,1370,1372,1374,1376,1378,1380,1383,1385,1387,1389,1392,1394],{"class":214,"line":281},[212,1344,1269],{"class":255},[212,1346,240],{"class":218},[212,1348,310],{"class":222},[212,1350,226],{"class":218},[212,1352,306],{"class":218},[212,1354,1355],{"class":309},"\u002Fitems",[212,1357,306],{"class":218},[212,1359,315],{"class":218},[212,1361,735],{"class":255},[212,1363,240],{"class":218},[212,1365,1366],{"class":222},"WithHeaders",[212,1368,1369],{"class":218},"(map[",[212,1371,803],{"class":802},[212,1373,806],{"class":218},[212,1375,803],{"class":802},[212,1377,834],{"class":218},[212,1379,306],{"class":218},[212,1381,1382],{"class":309},"X-Tenant",[212,1384,306],{"class":218},[212,1386,844],{"class":218},[212,1388,318],{"class":218},[212,1390,1391],{"class":309},"acme",[212,1393,306],{"class":218},[212,1395,1396],{"class":218},"}))\n",[1398,1399,1400,1402,1403,1406],"note",{},[182,1401,740],{}," builds a plain app with no auth provider, so route rules are not enforced until you call ",[182,1404,1405],{},"ta.App().Auth(...)",". Register middleware and the provider before registering routes, since chains are compiled at registration.",[195,1408,1410],{"id":1409},"test-a-handler-function-directly","Test a handler function directly",[178,1412,1413],{},"Handlers are ordinary functions, so the fastest test skips HTTP entirely:",[203,1415,1417],{"className":205,"code":1416,"language":207,"meta":208,"style":208},"func TestGetUserNotFound(t *testing.T) {\n    h := getUser(emptyStore{})\n\n    w := httptest.NewRecorder()\n    r := httptest.NewRequest(\"GET\", \"\u002Fusers\u002F404\", nil)\n    ctx := ezz.NewTestContext(w, r)\n\n    _, err := h(ctx, GetUserRequest{ID: \"404\"})\n\n    var httpErr *ezz.HTTPError\n    if !errors.As(err, &httpErr) || httpErr.StatusCode != 404 {\n        t.Fatalf(\"expected a 404 HTTPError, got %v\", err)\n    }\n}\n",[182,1418,1419,1442,1460,1464,1479,1513,1539,1543,1581,1585,1602,1648,1675,1679],{"__ignoreMap":208},[212,1420,1421,1423,1426,1428,1430,1432,1434,1436,1438,1440],{"class":214,"line":215},[212,1422,219],{"class":218},[212,1424,1425],{"class":222}," TestGetUserNotFound",[212,1427,226],{"class":218},[212,1429,230],{"class":229},[212,1431,233],{"class":218},[212,1433,237],{"class":236},[212,1435,240],{"class":218},[212,1437,243],{"class":236},[212,1439,246],{"class":218},[212,1441,249],{"class":218},[212,1443,1444,1447,1449,1452,1454,1457],{"class":214,"line":252},[212,1445,1446],{"class":255},"    h ",[212,1448,259],{"class":218},[212,1450,1451],{"class":222}," getUser",[212,1453,226],{"class":218},[212,1455,1456],{"class":236},"emptyStore",[212,1458,1459],{"class":218},"{})\n",[212,1461,1462],{"class":214,"line":281},[212,1463,285],{"emptyLinePlaceholder":284},[212,1465,1466,1469,1471,1473,1475,1477],{"class":214,"line":288},[212,1467,1468],{"class":255},"    w ",[212,1470,259],{"class":218},[212,1472,296],{"class":255},[212,1474,240],{"class":218},[212,1476,404],{"class":222},[212,1478,407],{"class":218},[212,1480,1481,1484,1486,1488,1490,1492,1494,1496,1498,1500,1502,1504,1507,1509,1511],{"class":214,"line":331},[212,1482,1483],{"class":255},"    r ",[212,1485,259],{"class":218},[212,1487,296],{"class":255},[212,1489,240],{"class":218},[212,1491,301],{"class":222},[212,1493,226],{"class":218},[212,1495,306],{"class":218},[212,1497,310],{"class":309},[212,1499,306],{"class":218},[212,1501,315],{"class":218},[212,1503,318],{"class":218},[212,1505,1506],{"class":309},"\u002Fusers\u002F404",[212,1508,306],{"class":218},[212,1510,315],{"class":218},[212,1512,328],{"class":218},[212,1514,1515,1518,1520,1522,1524,1527,1529,1532,1534,1537],{"class":214,"line":387},[212,1516,1517],{"class":255},"    ctx ",[212,1519,259],{"class":218},[212,1521,735],{"class":255},[212,1523,240],{"class":218},[212,1525,1526],{"class":222},"NewTestContext",[212,1528,226],{"class":218},[212,1530,1531],{"class":255},"w",[212,1533,315],{"class":218},[212,1535,1536],{"class":255}," r",[212,1538,431],{"class":218},[212,1540,1541],{"class":214,"line":392},[212,1542,285],{"emptyLinePlaceholder":284},[212,1544,1545,1548,1550,1552,1554,1557,1559,1561,1563,1566,1568,1570,1572,1574,1577,1579],{"class":214,"line":410},[212,1546,1547],{"class":255},"    _",[212,1549,315],{"class":218},[212,1551,552],{"class":255},[212,1553,259],{"class":218},[212,1555,1556],{"class":222}," h",[212,1558,226],{"class":218},[212,1560,785],{"class":255},[212,1562,315],{"class":218},[212,1564,1565],{"class":236}," GetUserRequest",[212,1567,834],{"class":218},[212,1569,671],{"class":255},[212,1571,844],{"class":218},[212,1573,318],{"class":218},[212,1575,1576],{"class":309},"404",[212,1578,306],{"class":218},[212,1580,945],{"class":218},[212,1582,1583],{"class":214,"line":434},[212,1584,285],{"emptyLinePlaceholder":284},[212,1586,1587,1589,1592,1595,1597,1599],{"class":214,"line":439},[212,1588,538],{"class":218},[212,1590,1591],{"class":255}," httpErr ",[212,1593,1594],{"class":218},"*",[212,1596,790],{"class":236},[212,1598,240],{"class":218},[212,1600,1601],{"class":236},"HTTPError\n",[212,1603,1604,1606,1609,1612,1614,1617,1619,1621,1623,1625,1628,1630,1633,1636,1638,1641,1643,1646],{"class":214,"line":468},[212,1605,443],{"class":442},[212,1607,1608],{"class":218}," !",[212,1610,1611],{"class":255},"errors",[212,1613,240],{"class":218},[212,1615,1616],{"class":222},"As",[212,1618,226],{"class":218},[212,1620,611],{"class":255},[212,1622,315],{"class":218},[212,1624,581],{"class":218},[212,1626,1627],{"class":255},"httpErr",[212,1629,246],{"class":218},[212,1631,1632],{"class":218}," ||",[212,1634,1635],{"class":255}," httpErr",[212,1637,240],{"class":218},[212,1639,1640],{"class":255},"StatusCode ",[212,1642,454],{"class":218},[212,1644,1645],{"class":904}," 404",[212,1647,249],{"class":218},[212,1649,1650,1652,1654,1656,1658,1660,1663,1666,1668,1670,1673],{"class":214,"line":524},[212,1651,471],{"class":255},[212,1653,240],{"class":218},[212,1655,476],{"class":222},[212,1657,226],{"class":218},[212,1659,306],{"class":218},[212,1661,1662],{"class":309},"expected a 404 HTTPError, got ",[212,1664,1665],{"class":486},"%v",[212,1667,306],{"class":218},[212,1669,315],{"class":218},[212,1671,1672],{"class":255}," err",[212,1674,431],{"class":218},[212,1676,1677],{"class":214,"line":530},[212,1678,527],{"class":218},[212,1680,1681],{"class":214,"line":535},[212,1682,684],{"class":218},[178,1684,1685,1688,1689,1010,1692,1010,1695,1698,1699,1702,1703,1706,1707,240],{},[182,1686,1687],{},"ezz.NewTestContext(w, r)"," builds a real context around a recorder, so ",[182,1690,1691],{},"ctx.SetHeader",[182,1693,1694],{},"ctx.JSON",[182,1696,1697],{},"ctx.Param",", and ",[182,1700,1701],{},"ctx.User()"," behave as they do in production. Set a user with ",[182,1704,1705],{},"ctx.SetUser(...)"," and path values with ",[182,1708,1709],{},"r.SetPathValue(...)",[178,1711,1712],{},"This form skips binding and validation, since you pass the request struct yourself. Cover those with a request-level test.",[195,1714,1716],{"id":1715},"test-binding-and-validation","Test binding and validation",[203,1718,1720],{"className":205,"code":1719,"language":207,"meta":208,"style":208},"func TestListUsersDefaults(t *testing.T) {\n    var got ListUsersRequest\n\n    ta := ezz.NewTestApp()\n    ta.App().GET(\"\u002Fusers\", ezz.H(func(ctx *ezz.Context, req ListUsersRequest) (any, error) {\n        got = req\n        return map[string]bool{\"ok\": true}, nil\n    })).Public()\n\n    ta.GET(\"\u002Fusers\").AssertStatus(t, 200)\n\n    if got.Page != 1 || got.Limit != 25 {\n        t.Fatalf(\"defaults not applied: %+v\", got)\n    }\n}\n\nfunc TestListUsersRejectsLimit(t *testing.T) {\n    \u002F\u002F ... same setup\n    ta.GET(\"\u002Fusers?limit=500\").\n        AssertStatus(t, 400).\n        AssertBodyContains(t, \"Limit\")\n}\n",[182,1721,1722,1745,1755,1759,1773,1837,1848,1879,1887,1891,1922,1926,1959,1985,1989,1993,1997,2020,2025,2044,2059,2079],{"__ignoreMap":208},[212,1723,1724,1726,1729,1731,1733,1735,1737,1739,1741,1743],{"class":214,"line":215},[212,1725,219],{"class":218},[212,1727,1728],{"class":222}," TestListUsersDefaults",[212,1730,226],{"class":218},[212,1732,230],{"class":229},[212,1734,233],{"class":218},[212,1736,237],{"class":236},[212,1738,240],{"class":218},[212,1740,243],{"class":236},[212,1742,246],{"class":218},[212,1744,249],{"class":218},[212,1746,1747,1749,1752],{"class":214,"line":252},[212,1748,538],{"class":218},[212,1750,1751],{"class":255}," got ",[212,1753,1754],{"class":236},"ListUsersRequest\n",[212,1756,1757],{"class":214,"line":281},[212,1758,285],{"emptyLinePlaceholder":284},[212,1760,1761,1763,1765,1767,1769,1771],{"class":214,"line":288},[212,1762,730],{"class":255},[212,1764,259],{"class":218},[212,1766,735],{"class":255},[212,1768,240],{"class":218},[212,1770,740],{"class":222},[212,1772,407],{"class":218},[212,1774,1775,1777,1779,1781,1783,1785,1787,1789,1791,1793,1795,1797,1799,1802,1804,1806,1808,1810,1812,1814,1816,1818,1821,1823,1826,1829,1831,1833,1835],{"class":214,"line":331},[212,1776,751],{"class":255},[212,1778,240],{"class":218},[212,1780,756],{"class":222},[212,1782,759],{"class":218},[212,1784,310],{"class":222},[212,1786,226],{"class":218},[212,1788,306],{"class":218},[212,1790,1080],{"class":309},[212,1792,306],{"class":218},[212,1794,315],{"class":218},[212,1796,735],{"class":255},[212,1798,240],{"class":218},[212,1800,1801],{"class":222},"H",[212,1803,782],{"class":218},[212,1805,785],{"class":229},[212,1807,233],{"class":218},[212,1809,790],{"class":236},[212,1811,240],{"class":218},[212,1813,53],{"class":236},[212,1815,315],{"class":218},[212,1817,428],{"class":229},[212,1819,1820],{"class":236}," ListUsersRequest",[212,1822,246],{"class":218},[212,1824,1825],{"class":218}," (",[212,1827,1828],{"class":236},"any",[212,1830,315],{"class":218},[212,1832,813],{"class":802},[212,1834,246],{"class":218},[212,1836,249],{"class":218},[212,1838,1839,1842,1845],{"class":214,"line":387},[212,1840,1841],{"class":255},"        got ",[212,1843,1844],{"class":218},"=",[212,1846,1847],{"class":255}," req\n",[212,1849,1850,1852,1854,1856,1858,1861,1863,1865,1867,1869,1871,1875,1877],{"class":214,"line":392},[212,1851,822],{"class":442},[212,1853,825],{"class":218},[212,1855,803],{"class":802},[212,1857,806],{"class":218},[212,1859,1860],{"class":802},"bool",[212,1862,834],{"class":218},[212,1864,306],{"class":218},[212,1866,849],{"class":309},[212,1868,306],{"class":218},[212,1870,844],{"class":218},[212,1872,1874],{"class":1873},"sfNiH"," true",[212,1876,854],{"class":218},[212,1878,857],{"class":218},[212,1880,1881,1883,1885],{"class":214,"line":410},[212,1882,862],{"class":218},[212,1884,865],{"class":222},[212,1886,407],{"class":218},[212,1888,1889],{"class":214,"line":434},[212,1890,285],{"emptyLinePlaceholder":284},[212,1892,1893,1895,1897,1899,1901,1903,1905,1907,1910,1912,1914,1916,1918,1920],{"class":214,"line":439},[212,1894,751],{"class":255},[212,1896,240],{"class":218},[212,1898,310],{"class":222},[212,1900,226],{"class":218},[212,1902,306],{"class":218},[212,1904,1080],{"class":309},[212,1906,306],{"class":218},[212,1908,1909],{"class":218},").",[212,1911,1131],{"class":222},[212,1913,226],{"class":218},[212,1915,230],{"class":255},[212,1917,315],{"class":218},[212,1919,905],{"class":904},[212,1921,431],{"class":218},[212,1923,1924],{"class":214,"line":468},[212,1925,285],{"emptyLinePlaceholder":284},[212,1927,1928,1930,1933,1935,1938,1940,1943,1945,1947,1949,1952,1954,1957],{"class":214,"line":524},[212,1929,443],{"class":442},[212,1931,1932],{"class":255}," got",[212,1934,240],{"class":218},[212,1936,1937],{"class":255},"Page ",[212,1939,454],{"class":218},[212,1941,1942],{"class":904}," 1",[212,1944,1632],{"class":218},[212,1946,1932],{"class":255},[212,1948,240],{"class":218},[212,1950,1951],{"class":255},"Limit ",[212,1953,454],{"class":218},[212,1955,1956],{"class":904}," 25",[212,1958,249],{"class":218},[212,1960,1961,1963,1965,1967,1969,1971,1974,1977,1979,1981,1983],{"class":214,"line":530},[212,1962,471],{"class":255},[212,1964,240],{"class":218},[212,1966,476],{"class":222},[212,1968,226],{"class":218},[212,1970,306],{"class":218},[212,1972,1973],{"class":309},"defaults not applied: ",[212,1975,1976],{"class":486},"%+v",[212,1978,306],{"class":218},[212,1980,315],{"class":218},[212,1982,1932],{"class":255},[212,1984,431],{"class":218},[212,1986,1987],{"class":214,"line":535},[212,1988,527],{"class":218},[212,1990,1991],{"class":214,"line":547},[212,1992,684],{"class":218},[212,1994,1995],{"class":214,"line":599},[212,1996,285],{"emptyLinePlaceholder":284},[212,1998,1999,2001,2004,2006,2008,2010,2012,2014,2016,2018],{"class":214,"line":616},[212,2000,219],{"class":218},[212,2002,2003],{"class":222}," TestListUsersRejectsLimit",[212,2005,226],{"class":218},[212,2007,230],{"class":229},[212,2009,233],{"class":218},[212,2011,237],{"class":236},[212,2013,240],{"class":218},[212,2015,243],{"class":236},[212,2017,246],{"class":218},[212,2019,249],{"class":218},[212,2021,2022],{"class":214,"line":621},[212,2023,2024],{"class":277},"    \u002F\u002F ... same setup\n",[212,2026,2027,2029,2031,2033,2035,2037,2040,2042],{"class":214,"line":645},[212,2028,751],{"class":255},[212,2030,240],{"class":218},[212,2032,310],{"class":222},[212,2034,226],{"class":218},[212,2036,306],{"class":218},[212,2038,2039],{"class":309},"\u002Fusers?limit=500",[212,2041,306],{"class":218},[212,2043,890],{"class":218},[212,2045,2046,2048,2050,2052,2054,2057],{"class":214,"line":676},[212,2047,895],{"class":222},[212,2049,226],{"class":218},[212,2051,230],{"class":255},[212,2053,315],{"class":218},[212,2055,2056],{"class":904}," 400",[212,2058,890],{"class":218},[212,2060,2061,2064,2066,2068,2070,2072,2075,2077],{"class":214,"line":681},[212,2062,2063],{"class":222},"        AssertBodyContains",[212,2065,226],{"class":218},[212,2067,230],{"class":255},[212,2069,315],{"class":218},[212,2071,318],{"class":218},[212,2073,2074],{"class":309},"Limit",[212,2076,306],{"class":218},[212,2078,431],{"class":218},[212,2080,2082],{"class":214,"line":2081},22,[212,2083,684],{"class":218},[195,2085,2087],{"id":2086},"test-authenticated-routes","Test authenticated routes",[178,2089,2090],{},"Mint a token with the same provider the app uses:",[203,2092,2094],{"className":205,"code":2093,"language":207,"meta":208,"style":208},"func testToken(t *testing.T, roles ...string) string {\n    t.Helper()\n    jwt := auth.NewHMACProvider(\"test-secret\", \"test\")\n    token, err := jwt.GenerateToken(&auth.Claims{\n        Subject:   \"u-1\",\n        ExpiresAt: time.Now().Add(time.Hour).Unix(),\n        Custom:    map[string]any{\"roles\": roles},\n    })\n    if err != nil {\n        t.Fatal(err)\n    }\n    return token\n}\n",[182,2095,2096,2132,2142,2177,2208,2226,2264,2296,2301,2313,2327,2331,2339],{"__ignoreMap":208},[212,2097,2098,2100,2103,2105,2107,2109,2111,2113,2115,2117,2120,2123,2125,2127,2130],{"class":214,"line":215},[212,2099,219],{"class":218},[212,2101,2102],{"class":222}," testToken",[212,2104,226],{"class":218},[212,2106,230],{"class":229},[212,2108,233],{"class":218},[212,2110,237],{"class":236},[212,2112,240],{"class":218},[212,2114,243],{"class":236},[212,2116,315],{"class":218},[212,2118,2119],{"class":229}," roles",[212,2121,2122],{"class":218}," ...",[212,2124,803],{"class":802},[212,2126,246],{"class":218},[212,2128,2129],{"class":802}," string",[212,2131,249],{"class":218},[212,2133,2134,2136,2138,2140],{"class":214,"line":252},[212,2135,1242],{"class":255},[212,2137,240],{"class":218},[212,2139,962],{"class":222},[212,2141,407],{"class":218},[212,2143,2144,2147,2149,2152,2154,2157,2159,2161,2164,2166,2168,2170,2173,2175],{"class":214,"line":281},[212,2145,2146],{"class":255},"    jwt ",[212,2148,259],{"class":218},[212,2150,2151],{"class":255}," auth",[212,2153,240],{"class":218},[212,2155,2156],{"class":222},"NewHMACProvider",[212,2158,226],{"class":218},[212,2160,306],{"class":218},[212,2162,2163],{"class":309},"test-secret",[212,2165,306],{"class":218},[212,2167,315],{"class":218},[212,2169,318],{"class":218},[212,2171,2172],{"class":309},"test",[212,2174,306],{"class":218},[212,2176,431],{"class":218},[212,2178,2179,2182,2184,2186,2188,2191,2193,2196,2198,2201,2203,2206],{"class":214,"line":288},[212,2180,2181],{"class":255},"    token",[212,2183,315],{"class":218},[212,2185,552],{"class":255},[212,2187,259],{"class":218},[212,2189,2190],{"class":255}," jwt",[212,2192,240],{"class":218},[212,2194,2195],{"class":222},"GenerateToken",[212,2197,1224],{"class":218},[212,2199,2200],{"class":236},"auth",[212,2202,240],{"class":218},[212,2204,2205],{"class":236},"Claims",[212,2207,465],{"class":218},[212,2209,2210,2213,2215,2218,2221,2223],{"class":214,"line":331},[212,2211,2212],{"class":255},"        Subject",[212,2214,844],{"class":218},[212,2216,2217],{"class":218},"   \"",[212,2219,2220],{"class":309},"u-1",[212,2222,306],{"class":218},[212,2224,2225],{"class":218},",\n",[212,2227,2228,2231,2233,2236,2238,2241,2243,2246,2248,2251,2253,2256,2258,2261],{"class":214,"line":387},[212,2229,2230],{"class":255},"        ExpiresAt",[212,2232,844],{"class":218},[212,2234,2235],{"class":255}," time",[212,2237,240],{"class":218},[212,2239,2240],{"class":222},"Now",[212,2242,759],{"class":218},[212,2244,2245],{"class":222},"Add",[212,2247,226],{"class":218},[212,2249,2250],{"class":255},"time",[212,2252,240],{"class":218},[212,2254,2255],{"class":255},"Hour",[212,2257,1909],{"class":218},[212,2259,2260],{"class":222},"Unix",[212,2262,2263],{"class":218},"(),\n",[212,2265,2266,2269,2271,2274,2276,2278,2280,2282,2284,2287,2289,2291,2293],{"class":214,"line":392},[212,2267,2268],{"class":255},"        Custom",[212,2270,844],{"class":218},[212,2272,2273],{"class":218},"    map[",[212,2275,803],{"class":802},[212,2277,806],{"class":218},[212,2279,1828],{"class":236},[212,2281,834],{"class":218},[212,2283,306],{"class":218},[212,2285,2286],{"class":309},"roles",[212,2288,306],{"class":218},[212,2290,844],{"class":218},[212,2292,2119],{"class":255},[212,2294,2295],{"class":218},"},\n",[212,2297,2298],{"class":214,"line":410},[212,2299,2300],{"class":218},"    })\n",[212,2302,2303,2305,2307,2309,2311],{"class":214,"line":434},[212,2304,443],{"class":442},[212,2306,552],{"class":255},[212,2308,454],{"class":218},[212,2310,594],{"class":218},[212,2312,249],{"class":218},[212,2314,2315,2317,2319,2321,2323,2325],{"class":214,"line":439},[212,2316,471],{"class":255},[212,2318,240],{"class":218},[212,2320,606],{"class":222},[212,2322,226],{"class":218},[212,2324,611],{"class":255},[212,2326,431],{"class":218},[212,2328,2329],{"class":214,"line":468},[212,2330,527],{"class":218},[212,2332,2333,2336],{"class":214,"line":524},[212,2334,2335],{"class":442},"    return",[212,2337,2338],{"class":255}," token\n",[212,2340,2341],{"class":214,"line":530},[212,2342,684],{"class":218},[203,2344,2346],{"className":205,"code":2345,"language":207,"meta":208,"style":208},"func TestAdminRouteForbidsMembers(t *testing.T) {\n    app := buildApp(testStore(t))\n\n    req := httptest.NewRequest(\"DELETE\", \"\u002Fapi\u002Fv1\u002Fadmin\u002Fusers\u002F42\", nil)\n    req.Header.Set(\"Authorization\", \"Bearer \"+testToken(t, \"member\"))\n\n    rec := httptest.NewRecorder()\n    app.ServeHTTP(rec, req)\n\n    if rec.Code != http.StatusForbidden {\n        t.Fatalf(\"got %d, want 403\", rec.Code)\n    }\n}\n",[182,2347,2348,2371,2389,2393,2427,2473,2477,2491,2509,2513,2534,2565,2569],{"__ignoreMap":208},[212,2349,2350,2352,2355,2357,2359,2361,2363,2365,2367,2369],{"class":214,"line":215},[212,2351,219],{"class":218},[212,2353,2354],{"class":222}," TestAdminRouteForbidsMembers",[212,2356,226],{"class":218},[212,2358,230],{"class":229},[212,2360,233],{"class":218},[212,2362,237],{"class":236},[212,2364,240],{"class":218},[212,2366,243],{"class":236},[212,2368,246],{"class":218},[212,2370,249],{"class":218},[212,2372,2373,2375,2377,2379,2381,2383,2385,2387],{"class":214,"line":252},[212,2374,256],{"class":255},[212,2376,259],{"class":218},[212,2378,262],{"class":222},[212,2380,226],{"class":218},[212,2382,267],{"class":222},[212,2384,226],{"class":218},[212,2386,230],{"class":255},[212,2388,384],{"class":218},[212,2390,2391],{"class":214,"line":281},[212,2392,285],{"emptyLinePlaceholder":284},[212,2394,2395,2397,2399,2401,2403,2405,2407,2409,2412,2414,2416,2418,2421,2423,2425],{"class":214,"line":288},[212,2396,291],{"class":255},[212,2398,259],{"class":218},[212,2400,296],{"class":255},[212,2402,240],{"class":218},[212,2404,301],{"class":222},[212,2406,226],{"class":218},[212,2408,306],{"class":218},[212,2410,2411],{"class":309},"DELETE",[212,2413,306],{"class":218},[212,2415,315],{"class":218},[212,2417,318],{"class":218},[212,2419,2420],{"class":309},"\u002Fapi\u002Fv1\u002Fadmin\u002Fusers\u002F42",[212,2422,306],{"class":218},[212,2424,315],{"class":218},[212,2426,328],{"class":218},[212,2428,2429,2431,2433,2435,2437,2439,2441,2443,2445,2447,2449,2451,2453,2455,2457,2459,2461,2463,2465,2467,2469,2471],{"class":214,"line":331},[212,2430,334],{"class":255},[212,2432,240],{"class":218},[212,2434,339],{"class":255},[212,2436,240],{"class":218},[212,2438,344],{"class":222},[212,2440,226],{"class":218},[212,2442,306],{"class":218},[212,2444,351],{"class":309},[212,2446,306],{"class":218},[212,2448,315],{"class":218},[212,2450,318],{"class":218},[212,2452,360],{"class":309},[212,2454,306],{"class":218},[212,2456,365],{"class":218},[212,2458,368],{"class":222},[212,2460,226],{"class":218},[212,2462,230],{"class":255},[212,2464,315],{"class":218},[212,2466,318],{"class":218},[212,2468,379],{"class":309},[212,2470,306],{"class":218},[212,2472,384],{"class":218},[212,2474,2475],{"class":214,"line":387},[212,2476,285],{"emptyLinePlaceholder":284},[212,2478,2479,2481,2483,2485,2487,2489],{"class":214,"line":392},[212,2480,395],{"class":255},[212,2482,259],{"class":218},[212,2484,296],{"class":255},[212,2486,240],{"class":218},[212,2488,404],{"class":222},[212,2490,407],{"class":218},[212,2492,2493,2495,2497,2499,2501,2503,2505,2507],{"class":214,"line":410},[212,2494,413],{"class":255},[212,2496,240],{"class":218},[212,2498,418],{"class":222},[212,2500,226],{"class":218},[212,2502,423],{"class":255},[212,2504,315],{"class":218},[212,2506,428],{"class":255},[212,2508,431],{"class":218},[212,2510,2511],{"class":214,"line":434},[212,2512,285],{"emptyLinePlaceholder":284},[212,2514,2515,2517,2519,2521,2523,2525,2527,2529,2532],{"class":214,"line":439},[212,2516,443],{"class":442},[212,2518,446],{"class":255},[212,2520,240],{"class":218},[212,2522,451],{"class":255},[212,2524,454],{"class":218},[212,2526,457],{"class":255},[212,2528,240],{"class":218},[212,2530,2531],{"class":255},"StatusForbidden ",[212,2533,465],{"class":218},[212,2535,2536,2538,2540,2542,2544,2546,2548,2550,2553,2555,2557,2559,2561,2563],{"class":214,"line":468},[212,2537,471],{"class":255},[212,2539,240],{"class":218},[212,2541,476],{"class":222},[212,2543,226],{"class":218},[212,2545,306],{"class":218},[212,2547,483],{"class":309},[212,2549,487],{"class":486},[212,2551,2552],{"class":309},", want 403",[212,2554,306],{"class":218},[212,2556,315],{"class":218},[212,2558,446],{"class":255},[212,2560,240],{"class":218},[212,2562,504],{"class":255},[212,2564,431],{"class":218},[212,2566,2567],{"class":214,"line":524},[212,2568,527],{"class":218},[212,2570,2571],{"class":214,"line":530},[212,2572,684],{"class":218},[178,2574,2575],{},"For a case where the token itself is irrelevant, a stub provider is simpler:",[203,2577,2579],{"className":205,"code":2578,"language":207,"meta":208,"style":208},"type stubProvider struct{ user any }\n\nfunc (s stubProvider) Authenticate(*http.Request) (any, error) {\n    if s.user == nil {\n        return nil, errors.New(\"no user\")\n    }\n    return s.user, nil\n}\n\ntype stubUser struct{ roles []string }\n\nfunc (u stubUser) ID() string                        { return \"u-1\" }\nfunc (u stubUser) GetClaims() *auth.Claims           { return &auth.Claims{Subject: \"u-1\"} }\nfunc (u stubUser) GetAttribute(string) (any, bool)   { return nil, false }\nfunc (u stubUser) HasRole(role string) bool          { return slices.Contains(u.roles, role) }\nfunc (u stubUser) HasPermission(perm string) bool    { return false }\n\napp := ezz.New().Auth(stubProvider{user: stubUser{roles: []string{\"admin\"}}})\n",[182,2580,2581,2599,2603,2645,2664,2690,2694,2708,2712,2716,2735,2739,2775,2831,2875,2932,2967,2971],{"__ignoreMap":208},[212,2582,2583,2586,2589,2592,2594,2596],{"class":214,"line":215},[212,2584,2585],{"class":218},"type",[212,2587,2588],{"class":236}," stubProvider",[212,2590,2591],{"class":218}," struct{",[212,2593,541],{"class":255},[212,2595,1828],{"class":236},[212,2597,2598],{"class":218}," }\n",[212,2600,2601],{"class":214,"line":252},[212,2602,285],{"emptyLinePlaceholder":284},[212,2604,2605,2607,2609,2612,2615,2617,2620,2623,2626,2628,2631,2633,2635,2637,2639,2641,2643],{"class":214,"line":281},[212,2606,219],{"class":218},[212,2608,1825],{"class":218},[212,2610,2611],{"class":229},"s ",[212,2613,2614],{"class":236},"stubProvider",[212,2616,246],{"class":218},[212,2618,2619],{"class":222}," Authenticate",[212,2621,2622],{"class":218},"(*",[212,2624,2625],{"class":236},"http",[212,2627,240],{"class":218},[212,2629,2630],{"class":236},"Request",[212,2632,246],{"class":218},[212,2634,1825],{"class":218},[212,2636,1828],{"class":236},[212,2638,315],{"class":218},[212,2640,813],{"class":802},[212,2642,246],{"class":218},[212,2644,249],{"class":218},[212,2646,2647,2649,2652,2654,2657,2660,2662],{"class":214,"line":288},[212,2648,443],{"class":442},[212,2650,2651],{"class":255}," s",[212,2653,240],{"class":218},[212,2655,2656],{"class":255},"user ",[212,2658,2659],{"class":218},"==",[212,2661,594],{"class":218},[212,2663,249],{"class":218},[212,2665,2666,2668,2671,2674,2676,2679,2681,2683,2686,2688],{"class":214,"line":331},[212,2667,822],{"class":442},[212,2669,2670],{"class":218}," nil,",[212,2672,2673],{"class":255}," errors",[212,2675,240],{"class":218},[212,2677,2678],{"class":222},"New",[212,2680,226],{"class":218},[212,2682,306],{"class":218},[212,2684,2685],{"class":309},"no user",[212,2687,306],{"class":218},[212,2689,431],{"class":218},[212,2691,2692],{"class":214,"line":387},[212,2693,527],{"class":218},[212,2695,2696,2698,2700,2702,2704,2706],{"class":214,"line":392},[212,2697,2335],{"class":442},[212,2699,2651],{"class":255},[212,2701,240],{"class":218},[212,2703,584],{"class":255},[212,2705,315],{"class":218},[212,2707,857],{"class":218},[212,2709,2710],{"class":214,"line":410},[212,2711,684],{"class":218},[212,2713,2714],{"class":214,"line":434},[212,2715,285],{"emptyLinePlaceholder":284},[212,2717,2718,2720,2723,2725,2728,2731,2733],{"class":214,"line":439},[212,2719,2585],{"class":218},[212,2721,2722],{"class":236}," stubUser",[212,2724,2591],{"class":218},[212,2726,2727],{"class":255}," roles ",[212,2729,2730],{"class":218},"[]",[212,2732,803],{"class":802},[212,2734,2598],{"class":218},[212,2736,2737],{"class":214,"line":468},[212,2738,285],{"emptyLinePlaceholder":284},[212,2740,2741,2743,2745,2748,2751,2753,2756,2759,2761,2764,2767,2769,2771,2773],{"class":214,"line":524},[212,2742,219],{"class":218},[212,2744,1825],{"class":218},[212,2746,2747],{"class":229},"u ",[212,2749,2750],{"class":236},"stubUser",[212,2752,246],{"class":218},[212,2754,2755],{"class":222}," ID",[212,2757,2758],{"class":218},"()",[212,2760,2129],{"class":802},[212,2762,2763],{"class":218},"                        {",[212,2765,2766],{"class":442}," return",[212,2768,318],{"class":218},[212,2770,2220],{"class":309},[212,2772,306],{"class":218},[212,2774,2598],{"class":218},[212,2776,2777,2779,2781,2783,2785,2787,2790,2792,2794,2796,2798,2800,2803,2805,2807,2809,2811,2813,2815,2818,2820,2822,2824,2826,2829],{"class":214,"line":530},[212,2778,219],{"class":218},[212,2780,1825],{"class":218},[212,2782,2747],{"class":229},[212,2784,2750],{"class":236},[212,2786,246],{"class":218},[212,2788,2789],{"class":222}," GetClaims",[212,2791,2758],{"class":218},[212,2793,233],{"class":218},[212,2795,2200],{"class":236},[212,2797,240],{"class":218},[212,2799,2205],{"class":236},[212,2801,2802],{"class":218},"           {",[212,2804,2766],{"class":442},[212,2806,581],{"class":218},[212,2808,2200],{"class":236},[212,2810,240],{"class":218},[212,2812,2205],{"class":236},[212,2814,834],{"class":218},[212,2816,2817],{"class":255},"Subject",[212,2819,844],{"class":218},[212,2821,318],{"class":218},[212,2823,2220],{"class":309},[212,2825,306],{"class":218},[212,2827,2828],{"class":218},"}",[212,2830,2598],{"class":218},[212,2832,2833,2835,2837,2839,2841,2843,2846,2848,2850,2852,2854,2856,2858,2861,2863,2866,2868,2870,2873],{"class":214,"line":535},[212,2834,219],{"class":218},[212,2836,1825],{"class":218},[212,2838,2747],{"class":229},[212,2840,2750],{"class":236},[212,2842,246],{"class":218},[212,2844,2845],{"class":222}," GetAttribute",[212,2847,226],{"class":218},[212,2849,803],{"class":802},[212,2851,246],{"class":218},[212,2853,1825],{"class":218},[212,2855,1828],{"class":236},[212,2857,315],{"class":218},[212,2859,2860],{"class":802}," bool",[212,2862,246],{"class":218},[212,2864,2865],{"class":218},"   {",[212,2867,2766],{"class":442},[212,2869,2670],{"class":218},[212,2871,2872],{"class":1873}," false",[212,2874,2598],{"class":218},[212,2876,2877,2879,2881,2883,2885,2887,2890,2892,2895,2897,2899,2901,2904,2906,2909,2911,2914,2916,2919,2921,2923,2925,2928,2930],{"class":214,"line":547},[212,2878,219],{"class":218},[212,2880,1825],{"class":218},[212,2882,2747],{"class":229},[212,2884,2750],{"class":236},[212,2886,246],{"class":218},[212,2888,2889],{"class":222}," HasRole",[212,2891,226],{"class":218},[212,2893,2894],{"class":229},"role",[212,2896,2129],{"class":802},[212,2898,246],{"class":218},[212,2900,2860],{"class":802},[212,2902,2903],{"class":218},"          {",[212,2905,2766],{"class":442},[212,2907,2908],{"class":255}," slices",[212,2910,240],{"class":218},[212,2912,2913],{"class":222},"Contains",[212,2915,226],{"class":218},[212,2917,2918],{"class":255},"u",[212,2920,240],{"class":218},[212,2922,2286],{"class":255},[212,2924,315],{"class":218},[212,2926,2927],{"class":255}," role",[212,2929,246],{"class":218},[212,2931,2598],{"class":218},[212,2933,2934,2936,2938,2940,2942,2944,2947,2949,2952,2954,2956,2958,2961,2963,2965],{"class":214,"line":599},[212,2935,219],{"class":218},[212,2937,1825],{"class":218},[212,2939,2747],{"class":229},[212,2941,2750],{"class":236},[212,2943,246],{"class":218},[212,2945,2946],{"class":222}," HasPermission",[212,2948,226],{"class":218},[212,2950,2951],{"class":229},"perm",[212,2953,2129],{"class":802},[212,2955,246],{"class":218},[212,2957,2860],{"class":802},[212,2959,2960],{"class":218},"    {",[212,2962,2766],{"class":442},[212,2964,2872],{"class":1873},[212,2966,2598],{"class":218},[212,2968,2969],{"class":214,"line":616},[212,2970,285],{"emptyLinePlaceholder":284},[212,2972,2973,2976,2978,2980,2982,2984,2986,2988,2990,2992,2994,2996,2998,3000,3002,3004,3006,3009,3011,3013,3015,3018,3020],{"class":214,"line":621},[212,2974,2975],{"class":255},"app ",[212,2977,259],{"class":218},[212,2979,735],{"class":255},[212,2981,240],{"class":218},[212,2983,2678],{"class":222},[212,2985,759],{"class":218},[212,2987,68],{"class":222},[212,2989,226],{"class":218},[212,2991,2614],{"class":236},[212,2993,834],{"class":218},[212,2995,584],{"class":255},[212,2997,844],{"class":218},[212,2999,2722],{"class":236},[212,3001,834],{"class":218},[212,3003,2286],{"class":255},[212,3005,844],{"class":218},[212,3007,3008],{"class":218}," []",[212,3010,803],{"class":802},[212,3012,834],{"class":218},[212,3014,306],{"class":218},[212,3016,3017],{"class":309},"admin",[212,3019,306],{"class":218},[212,3021,3022],{"class":218},"}}})\n",[195,3024,3026],{"id":3025},"test-a-resolver","Test a resolver",[178,3028,3029],{},"Resolvers do not need HTTP at all:",[203,3031,3033],{"className":205,"code":3032,"language":207,"meta":208,"style":208},"func TestResolverExpandsHierarchy(t *testing.T) {\n    r := resolver{db: testDB(t)}       \u002F\u002F user u-1 has super_admin\n\n    authz, err := r.Resolve(context.Background(), auth.NewBasicUser(&auth.Claims{Subject: \"u-1\"}))\n    if err != nil {\n        t.Fatal(err)\n    }\n    if !slices.Contains(authz.Roles, \"admin\") {\n        t.Fatalf(\"super_admin should imply admin, got %v\", authz.Roles)\n    }\n}\n",[182,3034,3035,3058,3087,3091,3150,3162,3176,3180,3215,3245,3249],{"__ignoreMap":208},[212,3036,3037,3039,3042,3044,3046,3048,3050,3052,3054,3056],{"class":214,"line":215},[212,3038,219],{"class":218},[212,3040,3041],{"class":222}," TestResolverExpandsHierarchy",[212,3043,226],{"class":218},[212,3045,230],{"class":229},[212,3047,233],{"class":218},[212,3049,237],{"class":236},[212,3051,240],{"class":218},[212,3053,243],{"class":236},[212,3055,246],{"class":218},[212,3057,249],{"class":218},[212,3059,3060,3062,3064,3067,3069,3072,3074,3077,3079,3081,3084],{"class":214,"line":252},[212,3061,1483],{"class":255},[212,3063,259],{"class":218},[212,3065,3066],{"class":236}," resolver",[212,3068,834],{"class":218},[212,3070,3071],{"class":255},"db",[212,3073,844],{"class":218},[212,3075,3076],{"class":222}," testDB",[212,3078,226],{"class":218},[212,3080,230],{"class":255},[212,3082,3083],{"class":218},")}",[212,3085,3086],{"class":277},"       \u002F\u002F user u-1 has super_admin\n",[212,3088,3089],{"class":214,"line":281},[212,3090,285],{"emptyLinePlaceholder":284},[212,3092,3093,3096,3098,3100,3102,3104,3106,3109,3111,3114,3116,3119,3121,3123,3125,3128,3130,3132,3134,3136,3138,3140,3142,3144,3146,3148],{"class":214,"line":288},[212,3094,3095],{"class":255},"    authz",[212,3097,315],{"class":218},[212,3099,552],{"class":255},[212,3101,259],{"class":218},[212,3103,1536],{"class":255},[212,3105,240],{"class":218},[212,3107,3108],{"class":222},"Resolve",[212,3110,226],{"class":218},[212,3112,3113],{"class":255},"context",[212,3115,240],{"class":218},[212,3117,3118],{"class":222},"Background",[212,3120,578],{"class":218},[212,3122,2151],{"class":255},[212,3124,240],{"class":218},[212,3126,3127],{"class":222},"NewBasicUser",[212,3129,1224],{"class":218},[212,3131,2200],{"class":236},[212,3133,240],{"class":218},[212,3135,2205],{"class":236},[212,3137,834],{"class":218},[212,3139,2817],{"class":255},[212,3141,844],{"class":218},[212,3143,318],{"class":218},[212,3145,2220],{"class":309},[212,3147,306],{"class":218},[212,3149,1396],{"class":218},[212,3151,3152,3154,3156,3158,3160],{"class":214,"line":331},[212,3153,443],{"class":442},[212,3155,552],{"class":255},[212,3157,454],{"class":218},[212,3159,594],{"class":218},[212,3161,249],{"class":218},[212,3163,3164,3166,3168,3170,3172,3174],{"class":214,"line":387},[212,3165,471],{"class":255},[212,3167,240],{"class":218},[212,3169,606],{"class":222},[212,3171,226],{"class":218},[212,3173,611],{"class":255},[212,3175,431],{"class":218},[212,3177,3178],{"class":214,"line":392},[212,3179,527],{"class":218},[212,3181,3182,3184,3186,3189,3191,3193,3195,3198,3200,3203,3205,3207,3209,3211,3213],{"class":214,"line":410},[212,3183,443],{"class":442},[212,3185,1608],{"class":218},[212,3187,3188],{"class":255},"slices",[212,3190,240],{"class":218},[212,3192,2913],{"class":222},[212,3194,226],{"class":218},[212,3196,3197],{"class":255},"authz",[212,3199,240],{"class":218},[212,3201,3202],{"class":255},"Roles",[212,3204,315],{"class":218},[212,3206,318],{"class":218},[212,3208,3017],{"class":309},[212,3210,306],{"class":218},[212,3212,246],{"class":218},[212,3214,249],{"class":218},[212,3216,3217,3219,3221,3223,3225,3227,3230,3232,3234,3236,3239,3241,3243],{"class":214,"line":434},[212,3218,471],{"class":255},[212,3220,240],{"class":218},[212,3222,476],{"class":222},[212,3224,226],{"class":218},[212,3226,306],{"class":218},[212,3228,3229],{"class":309},"super_admin should imply admin, got ",[212,3231,1665],{"class":486},[212,3233,306],{"class":218},[212,3235,315],{"class":218},[212,3237,3238],{"class":255}," authz",[212,3240,240],{"class":218},[212,3242,3202],{"class":255},[212,3244,431],{"class":218},[212,3246,3247],{"class":214,"line":439},[212,3248,527],{"class":218},[212,3250,3251],{"class":214,"line":468},[212,3252,684],{"class":218},[178,3254,3255],{},"To check the caching wrapper, count calls to the inner resolver:",[203,3257,3259],{"className":205,"code":3258,"language":207,"meta":208,"style":208},"type countingResolver struct{ n int }\n\nfunc (c *countingResolver) Resolve(context.Context, auth.User) (auth.Authorization, error) {\n    c.n++\n    return auth.Authorization{Roles: []string{\"admin\"}}, nil\n}\n\nfunc TestCachedResolverCaches(t *testing.T) {\n    inner := &countingResolver{}\n    cached := auth.Cached(inner, time.Minute)\n    user := auth.NewBasicUser(&auth.Claims{Subject: \"u-1\"})\n\n    _, _ = cached.Resolve(context.Background(), user)\n    _, _ = cached.Resolve(context.Background(), user)\n\n    if inner.n != 1 {\n        t.Fatalf(\"inner resolver called %d times, want 1\", inner.n)\n    }\n\n    cached.Invalidate(\"u-1\")\n    _, _ = cached.Resolve(context.Background(), user)\n    if inner.n != 2 {\n        t.Fatalf(\"invalidation did not take effect\")\n    }\n}\n",[182,3260,3261,3278,3282,3336,3349,3382,3386,3390,3413,3427,3457,3492,3496,3528,3558,3562,3580,3612,3616,3620,3640,3670,3687,3707,3712],{"__ignoreMap":208},[212,3262,3263,3265,3268,3270,3273,3276],{"class":214,"line":215},[212,3264,2585],{"class":218},[212,3266,3267],{"class":236}," countingResolver",[212,3269,2591],{"class":218},[212,3271,3272],{"class":255}," n ",[212,3274,3275],{"class":802},"int",[212,3277,2598],{"class":218},[212,3279,3280],{"class":214,"line":252},[212,3281,285],{"emptyLinePlaceholder":284},[212,3283,3284,3286,3288,3291,3293,3296,3298,3301,3303,3305,3307,3309,3311,3313,3315,3318,3320,3322,3324,3326,3328,3330,3332,3334],{"class":214,"line":281},[212,3285,219],{"class":218},[212,3287,1825],{"class":218},[212,3289,3290],{"class":229},"c ",[212,3292,1594],{"class":218},[212,3294,3295],{"class":236},"countingResolver",[212,3297,246],{"class":218},[212,3299,3300],{"class":222}," Resolve",[212,3302,226],{"class":218},[212,3304,3113],{"class":236},[212,3306,240],{"class":218},[212,3308,53],{"class":236},[212,3310,315],{"class":218},[212,3312,2151],{"class":236},[212,3314,240],{"class":218},[212,3316,3317],{"class":236},"User",[212,3319,246],{"class":218},[212,3321,1825],{"class":218},[212,3323,2200],{"class":236},[212,3325,240],{"class":218},[212,3327,351],{"class":236},[212,3329,315],{"class":218},[212,3331,813],{"class":802},[212,3333,246],{"class":218},[212,3335,249],{"class":218},[212,3337,3338,3341,3343,3346],{"class":214,"line":288},[212,3339,3340],{"class":255},"    c",[212,3342,240],{"class":218},[212,3344,3345],{"class":255},"n",[212,3347,3348],{"class":218},"++\n",[212,3350,3351,3353,3355,3357,3359,3361,3363,3365,3367,3369,3371,3373,3375,3377,3380],{"class":214,"line":331},[212,3352,2335],{"class":442},[212,3354,2151],{"class":236},[212,3356,240],{"class":218},[212,3358,351],{"class":236},[212,3360,834],{"class":218},[212,3362,3202],{"class":255},[212,3364,844],{"class":218},[212,3366,3008],{"class":218},[212,3368,803],{"class":802},[212,3370,834],{"class":218},[212,3372,306],{"class":218},[212,3374,3017],{"class":309},[212,3376,306],{"class":218},[212,3378,3379],{"class":218},"}},",[212,3381,857],{"class":218},[212,3383,3384],{"class":214,"line":387},[212,3385,684],{"class":218},[212,3387,3388],{"class":214,"line":392},[212,3389,285],{"emptyLinePlaceholder":284},[212,3391,3392,3394,3397,3399,3401,3403,3405,3407,3409,3411],{"class":214,"line":410},[212,3393,219],{"class":218},[212,3395,3396],{"class":222}," TestCachedResolverCaches",[212,3398,226],{"class":218},[212,3400,230],{"class":229},[212,3402,233],{"class":218},[212,3404,237],{"class":236},[212,3406,240],{"class":218},[212,3408,243],{"class":236},[212,3410,246],{"class":218},[212,3412,249],{"class":218},[212,3414,3415,3418,3420,3422,3424],{"class":214,"line":434},[212,3416,3417],{"class":255},"    inner ",[212,3419,259],{"class":218},[212,3421,581],{"class":218},[212,3423,3295],{"class":236},[212,3425,3426],{"class":218},"{}\n",[212,3428,3429,3432,3434,3436,3438,3441,3443,3446,3448,3450,3452,3455],{"class":214,"line":439},[212,3430,3431],{"class":255},"    cached ",[212,3433,259],{"class":218},[212,3435,2151],{"class":255},[212,3437,240],{"class":218},[212,3439,3440],{"class":222},"Cached",[212,3442,226],{"class":218},[212,3444,3445],{"class":255},"inner",[212,3447,315],{"class":218},[212,3449,2235],{"class":255},[212,3451,240],{"class":218},[212,3453,3454],{"class":255},"Minute",[212,3456,431],{"class":218},[212,3458,3459,3462,3464,3466,3468,3470,3472,3474,3476,3478,3480,3482,3484,3486,3488,3490],{"class":214,"line":468},[212,3460,3461],{"class":255},"    user ",[212,3463,259],{"class":218},[212,3465,2151],{"class":255},[212,3467,240],{"class":218},[212,3469,3127],{"class":222},[212,3471,1224],{"class":218},[212,3473,2200],{"class":236},[212,3475,240],{"class":218},[212,3477,2205],{"class":236},[212,3479,834],{"class":218},[212,3481,2817],{"class":255},[212,3483,844],{"class":218},[212,3485,318],{"class":218},[212,3487,2220],{"class":309},[212,3489,306],{"class":218},[212,3491,945],{"class":218},[212,3493,3494],{"class":214,"line":524},[212,3495,285],{"emptyLinePlaceholder":284},[212,3497,3498,3500,3502,3505,3507,3510,3512,3514,3516,3518,3520,3522,3524,3526],{"class":214,"line":530},[212,3499,1547],{"class":255},[212,3501,315],{"class":218},[212,3503,3504],{"class":255}," _ ",[212,3506,1844],{"class":218},[212,3508,3509],{"class":255}," cached",[212,3511,240],{"class":218},[212,3513,3108],{"class":222},[212,3515,226],{"class":218},[212,3517,3113],{"class":255},[212,3519,240],{"class":218},[212,3521,3118],{"class":222},[212,3523,578],{"class":218},[212,3525,626],{"class":255},[212,3527,431],{"class":218},[212,3529,3530,3532,3534,3536,3538,3540,3542,3544,3546,3548,3550,3552,3554,3556],{"class":214,"line":535},[212,3531,1547],{"class":255},[212,3533,315],{"class":218},[212,3535,3504],{"class":255},[212,3537,1844],{"class":218},[212,3539,3509],{"class":255},[212,3541,240],{"class":218},[212,3543,3108],{"class":222},[212,3545,226],{"class":218},[212,3547,3113],{"class":255},[212,3549,240],{"class":218},[212,3551,3118],{"class":222},[212,3553,578],{"class":218},[212,3555,626],{"class":255},[212,3557,431],{"class":218},[212,3559,3560],{"class":214,"line":547},[212,3561,285],{"emptyLinePlaceholder":284},[212,3563,3564,3566,3569,3571,3574,3576,3578],{"class":214,"line":599},[212,3565,443],{"class":442},[212,3567,3568],{"class":255}," inner",[212,3570,240],{"class":218},[212,3572,3573],{"class":255},"n ",[212,3575,454],{"class":218},[212,3577,1942],{"class":904},[212,3579,249],{"class":218},[212,3581,3582,3584,3586,3588,3590,3592,3595,3597,3600,3602,3604,3606,3608,3610],{"class":214,"line":616},[212,3583,471],{"class":255},[212,3585,240],{"class":218},[212,3587,476],{"class":222},[212,3589,226],{"class":218},[212,3591,306],{"class":218},[212,3593,3594],{"class":309},"inner resolver called ",[212,3596,487],{"class":486},[212,3598,3599],{"class":309}," times, want 1",[212,3601,306],{"class":218},[212,3603,315],{"class":218},[212,3605,3568],{"class":255},[212,3607,240],{"class":218},[212,3609,3345],{"class":255},[212,3611,431],{"class":218},[212,3613,3614],{"class":214,"line":621},[212,3615,527],{"class":218},[212,3617,3618],{"class":214,"line":645},[212,3619,285],{"emptyLinePlaceholder":284},[212,3621,3622,3625,3627,3630,3632,3634,3636,3638],{"class":214,"line":676},[212,3623,3624],{"class":255},"    cached",[212,3626,240],{"class":218},[212,3628,3629],{"class":222},"Invalidate",[212,3631,226],{"class":218},[212,3633,306],{"class":218},[212,3635,2220],{"class":309},[212,3637,306],{"class":218},[212,3639,431],{"class":218},[212,3641,3642,3644,3646,3648,3650,3652,3654,3656,3658,3660,3662,3664,3666,3668],{"class":214,"line":681},[212,3643,1547],{"class":255},[212,3645,315],{"class":218},[212,3647,3504],{"class":255},[212,3649,1844],{"class":218},[212,3651,3509],{"class":255},[212,3653,240],{"class":218},[212,3655,3108],{"class":222},[212,3657,226],{"class":218},[212,3659,3113],{"class":255},[212,3661,240],{"class":218},[212,3663,3118],{"class":222},[212,3665,578],{"class":218},[212,3667,626],{"class":255},[212,3669,431],{"class":218},[212,3671,3672,3674,3676,3678,3680,3682,3685],{"class":214,"line":2081},[212,3673,443],{"class":442},[212,3675,3568],{"class":255},[212,3677,240],{"class":218},[212,3679,3573],{"class":255},[212,3681,454],{"class":218},[212,3683,3684],{"class":904}," 2",[212,3686,249],{"class":218},[212,3688,3690,3692,3694,3696,3698,3700,3703,3705],{"class":214,"line":3689},23,[212,3691,471],{"class":255},[212,3693,240],{"class":218},[212,3695,476],{"class":222},[212,3697,226],{"class":218},[212,3699,306],{"class":218},[212,3701,3702],{"class":309},"invalidation did not take effect",[212,3704,306],{"class":218},[212,3706,431],{"class":218},[212,3708,3710],{"class":214,"line":3709},24,[212,3711,527],{"class":218},[212,3713,3715],{"class":214,"line":3714},25,[212,3716,684],{"class":218},[195,3718,3720],{"id":3719},"test-the-gotrue-proxy-without-gotrue","Test the GoTrue proxy without GoTrue",[178,3722,3723,3724,3727,3728,3731,3732,844],{},"Point ",[182,3725,3726],{},"InternalURL"," at an ",[182,3729,3730],{},"httptest.Server"," that returns whatever you want, including the golden fixtures in ",[182,3733,3734],{},"auth\u002Ftestdata\u002Fgotrue",[203,3736,3738],{"className":205,"code":3737,"language":207,"meta":208,"style":208},"func TestSignupForwards(t *testing.T) {\n    var gotBody []byte\n    upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        gotBody, _ = io.ReadAll(r.Body)\n        w.Header().Set(\"Content-Type\", \"application\u002Fjson\")\n        _, _ = w.Write([]byte(`{\"access_token\":\"tok\",\"token_type\":\"bearer\"}`))\n    }))\n    defer upstream.Close()\n\n    gt := auth.NewGoTrue(auth.GoTrueConfig{\n        InternalURL: upstream.URL,\n        JWTSecret:   \"test-secret\",\n    })\n\n    app := ezz.New()\n    gt.Mount(app, \"\u002Fauth\")\n\n    rec := httptest.NewRecorder()\n    req := httptest.NewRequest(\"POST\", \"\u002Fauth\u002Fsignup\",\n        strings.NewReader(`{\"email\":\"ada@example.com\",\"password\":\"password123\"}`))\n    req.Header.Set(\"Content-Type\", \"application\u002Fjson\")\n    app.ServeHTTP(rec, req)\n\n    if rec.Code != http.StatusOK {\n        t.Fatalf(\"got %d: %s\", rec.Code, rec.Body.String())\n    }\n    if !strings.Contains(string(gotBody), \"ada@example.com\") {\n        t.Fatalf(\"body not forwarded: %s\", gotBody)\n    }\n}\n",[182,3739,3740,3763,3775,3825,3855,3888,3925,3930,3945,3949,3974,3990,4005,4009,4013,4027,4052,4056,4070,4101,4122,4152,4170,4174,4194,4239,4244,4280,4307,4312],{"__ignoreMap":208},[212,3741,3742,3744,3747,3749,3751,3753,3755,3757,3759,3761],{"class":214,"line":215},[212,3743,219],{"class":218},[212,3745,3746],{"class":222}," TestSignupForwards",[212,3748,226],{"class":218},[212,3750,230],{"class":229},[212,3752,233],{"class":218},[212,3754,237],{"class":236},[212,3756,240],{"class":218},[212,3758,243],{"class":236},[212,3760,246],{"class":218},[212,3762,249],{"class":218},[212,3764,3765,3767,3770,3772],{"class":214,"line":252},[212,3766,538],{"class":218},[212,3768,3769],{"class":255}," gotBody ",[212,3771,2730],{"class":218},[212,3773,3774],{"class":802},"byte\n",[212,3776,3777,3780,3782,3784,3786,3789,3791,3793,3795,3798,3800,3802,3804,3806,3809,3811,3813,3815,3817,3819,3821,3823],{"class":214,"line":281},[212,3778,3779],{"class":255},"    upstream ",[212,3781,259],{"class":218},[212,3783,296],{"class":255},[212,3785,240],{"class":218},[212,3787,3788],{"class":222},"NewServer",[212,3790,226],{"class":218},[212,3792,2625],{"class":255},[212,3794,240],{"class":218},[212,3796,3797],{"class":222},"HandlerFunc",[212,3799,782],{"class":218},[212,3801,1531],{"class":229},[212,3803,457],{"class":236},[212,3805,240],{"class":218},[212,3807,3808],{"class":236},"ResponseWriter",[212,3810,315],{"class":218},[212,3812,1536],{"class":229},[212,3814,233],{"class":218},[212,3816,2625],{"class":236},[212,3818,240],{"class":218},[212,3820,2630],{"class":236},[212,3822,246],{"class":218},[212,3824,249],{"class":218},[212,3826,3827,3830,3832,3834,3836,3839,3841,3844,3846,3849,3851,3853],{"class":214,"line":288},[212,3828,3829],{"class":255},"        gotBody",[212,3831,315],{"class":218},[212,3833,3504],{"class":255},[212,3835,1844],{"class":218},[212,3837,3838],{"class":255}," io",[212,3840,240],{"class":218},[212,3842,3843],{"class":222},"ReadAll",[212,3845,226],{"class":218},[212,3847,3848],{"class":255},"r",[212,3850,240],{"class":218},[212,3852,513],{"class":255},[212,3854,431],{"class":218},[212,3856,3857,3860,3862,3864,3866,3868,3870,3872,3875,3877,3879,3881,3884,3886],{"class":214,"line":331},[212,3858,3859],{"class":255},"        w",[212,3861,240],{"class":218},[212,3863,339],{"class":222},[212,3865,759],{"class":218},[212,3867,344],{"class":222},[212,3869,226],{"class":218},[212,3871,306],{"class":218},[212,3873,3874],{"class":309},"Content-Type",[212,3876,306],{"class":218},[212,3878,315],{"class":218},[212,3880,318],{"class":218},[212,3882,3883],{"class":309},"application\u002Fjson",[212,3885,306],{"class":218},[212,3887,431],{"class":218},[212,3889,3890,3893,3895,3897,3899,3902,3904,3907,3910,3913,3915,3918,3921,3923],{"class":214,"line":387},[212,3891,3892],{"class":255},"        _",[212,3894,315],{"class":218},[212,3896,3504],{"class":255},[212,3898,1844],{"class":218},[212,3900,3901],{"class":255}," w",[212,3903,240],{"class":218},[212,3905,3906],{"class":222},"Write",[212,3908,3909],{"class":218},"([]",[212,3911,3912],{"class":802},"byte",[212,3914,226],{"class":218},[212,3916,3917],{"class":218},"`",[212,3919,3920],{"class":309},"{\"access_token\":\"tok\",\"token_type\":\"bearer\"}",[212,3922,3917],{"class":218},[212,3924,384],{"class":218},[212,3926,3927],{"class":214,"line":392},[212,3928,3929],{"class":218},"    }))\n",[212,3931,3932,3935,3938,3940,3943],{"class":214,"line":410},[212,3933,3934],{"class":442},"    defer",[212,3936,3937],{"class":255}," upstream",[212,3939,240],{"class":218},[212,3941,3942],{"class":222},"Close",[212,3944,407],{"class":218},[212,3946,3947],{"class":214,"line":434},[212,3948,285],{"emptyLinePlaceholder":284},[212,3950,3951,3954,3956,3958,3960,3963,3965,3967,3969,3972],{"class":214,"line":439},[212,3952,3953],{"class":255},"    gt ",[212,3955,259],{"class":218},[212,3957,2151],{"class":255},[212,3959,240],{"class":218},[212,3961,3962],{"class":222},"NewGoTrue",[212,3964,226],{"class":218},[212,3966,2200],{"class":236},[212,3968,240],{"class":218},[212,3970,3971],{"class":236},"GoTrueConfig",[212,3973,465],{"class":218},[212,3975,3976,3979,3981,3983,3985,3988],{"class":214,"line":468},[212,3977,3978],{"class":255},"        InternalURL",[212,3980,844],{"class":218},[212,3982,3937],{"class":255},[212,3984,240],{"class":218},[212,3986,3987],{"class":255},"URL",[212,3989,2225],{"class":218},[212,3991,3992,3995,3997,3999,4001,4003],{"class":214,"line":524},[212,3993,3994],{"class":255},"        JWTSecret",[212,3996,844],{"class":218},[212,3998,2217],{"class":218},[212,4000,2163],{"class":309},[212,4002,306],{"class":218},[212,4004,2225],{"class":218},[212,4006,4007],{"class":214,"line":530},[212,4008,2300],{"class":218},[212,4010,4011],{"class":214,"line":535},[212,4012,285],{"emptyLinePlaceholder":284},[212,4014,4015,4017,4019,4021,4023,4025],{"class":214,"line":547},[212,4016,256],{"class":255},[212,4018,259],{"class":218},[212,4020,735],{"class":255},[212,4022,240],{"class":218},[212,4024,2678],{"class":222},[212,4026,407],{"class":218},[212,4028,4029,4032,4034,4037,4039,4042,4044,4046,4048,4050],{"class":214,"line":599},[212,4030,4031],{"class":255},"    gt",[212,4033,240],{"class":218},[212,4035,4036],{"class":222},"Mount",[212,4038,226],{"class":218},[212,4040,4041],{"class":255},"app",[212,4043,315],{"class":218},[212,4045,318],{"class":218},[212,4047,70],{"class":309},[212,4049,306],{"class":218},[212,4051,431],{"class":218},[212,4053,4054],{"class":214,"line":616},[212,4055,285],{"emptyLinePlaceholder":284},[212,4057,4058,4060,4062,4064,4066,4068],{"class":214,"line":621},[212,4059,395],{"class":255},[212,4061,259],{"class":218},[212,4063,296],{"class":255},[212,4065,240],{"class":218},[212,4067,404],{"class":222},[212,4069,407],{"class":218},[212,4071,4072,4074,4076,4078,4080,4082,4084,4086,4088,4090,4092,4094,4097,4099],{"class":214,"line":645},[212,4073,291],{"class":255},[212,4075,259],{"class":218},[212,4077,296],{"class":255},[212,4079,240],{"class":218},[212,4081,301],{"class":222},[212,4083,226],{"class":218},[212,4085,306],{"class":218},[212,4087,1073],{"class":309},[212,4089,306],{"class":218},[212,4091,315],{"class":218},[212,4093,318],{"class":218},[212,4095,4096],{"class":309},"\u002Fauth\u002Fsignup",[212,4098,306],{"class":218},[212,4100,2225],{"class":218},[212,4102,4103,4106,4108,4111,4113,4115,4118,4120],{"class":214,"line":676},[212,4104,4105],{"class":255},"        strings",[212,4107,240],{"class":218},[212,4109,4110],{"class":222},"NewReader",[212,4112,226],{"class":218},[212,4114,3917],{"class":218},[212,4116,4117],{"class":309},"{\"email\":\"ada@example.com\",\"password\":\"password123\"}",[212,4119,3917],{"class":218},[212,4121,384],{"class":218},[212,4123,4124,4126,4128,4130,4132,4134,4136,4138,4140,4142,4144,4146,4148,4150],{"class":214,"line":681},[212,4125,334],{"class":255},[212,4127,240],{"class":218},[212,4129,339],{"class":255},[212,4131,240],{"class":218},[212,4133,344],{"class":222},[212,4135,226],{"class":218},[212,4137,306],{"class":218},[212,4139,3874],{"class":309},[212,4141,306],{"class":218},[212,4143,315],{"class":218},[212,4145,318],{"class":218},[212,4147,3883],{"class":309},[212,4149,306],{"class":218},[212,4151,431],{"class":218},[212,4153,4154,4156,4158,4160,4162,4164,4166,4168],{"class":214,"line":2081},[212,4155,413],{"class":255},[212,4157,240],{"class":218},[212,4159,418],{"class":222},[212,4161,226],{"class":218},[212,4163,423],{"class":255},[212,4165,315],{"class":218},[212,4167,428],{"class":255},[212,4169,431],{"class":218},[212,4171,4172],{"class":214,"line":3689},[212,4173,285],{"emptyLinePlaceholder":284},[212,4175,4176,4178,4180,4182,4184,4186,4188,4190,4192],{"class":214,"line":3709},[212,4177,443],{"class":442},[212,4179,446],{"class":255},[212,4181,240],{"class":218},[212,4183,451],{"class":255},[212,4185,454],{"class":218},[212,4187,457],{"class":255},[212,4189,240],{"class":218},[212,4191,462],{"class":255},[212,4193,465],{"class":218},[212,4195,4196,4198,4200,4202,4204,4206,4208,4210,4213,4215,4217,4219,4221,4223,4225,4227,4229,4231,4233,4235,4237],{"class":214,"line":3714},[212,4197,471],{"class":255},[212,4199,240],{"class":218},[212,4201,476],{"class":222},[212,4203,226],{"class":218},[212,4205,306],{"class":218},[212,4207,483],{"class":309},[212,4209,487],{"class":486},[212,4211,4212],{"class":309},": ",[212,4214,493],{"class":486},[212,4216,306],{"class":218},[212,4218,315],{"class":218},[212,4220,446],{"class":255},[212,4222,240],{"class":218},[212,4224,504],{"class":255},[212,4226,315],{"class":218},[212,4228,446],{"class":255},[212,4230,240],{"class":218},[212,4232,513],{"class":255},[212,4234,240],{"class":218},[212,4236,518],{"class":222},[212,4238,521],{"class":218},[212,4240,4242],{"class":214,"line":4241},26,[212,4243,527],{"class":218},[212,4245,4247,4249,4251,4254,4256,4258,4260,4262,4264,4267,4270,4272,4274,4276,4278],{"class":214,"line":4246},27,[212,4248,443],{"class":442},[212,4250,1608],{"class":218},[212,4252,4253],{"class":255},"strings",[212,4255,240],{"class":218},[212,4257,2913],{"class":222},[212,4259,226],{"class":218},[212,4261,803],{"class":802},[212,4263,226],{"class":218},[212,4265,4266],{"class":255},"gotBody",[212,4268,4269],{"class":218},"),",[212,4271,318],{"class":218},[212,4273,1113],{"class":309},[212,4275,306],{"class":218},[212,4277,246],{"class":218},[212,4279,249],{"class":218},[212,4281,4283,4285,4287,4289,4291,4293,4296,4298,4300,4302,4305],{"class":214,"line":4282},28,[212,4284,471],{"class":255},[212,4286,240],{"class":218},[212,4288,476],{"class":222},[212,4290,226],{"class":218},[212,4292,306],{"class":218},[212,4294,4295],{"class":309},"body not forwarded: ",[212,4297,493],{"class":486},[212,4299,306],{"class":218},[212,4301,315],{"class":218},[212,4303,4304],{"class":255}," gotBody",[212,4306,431],{"class":218},[212,4308,4310],{"class":214,"line":4309},29,[212,4311,527],{"class":218},[212,4313,4315],{"class":214,"line":4314},30,[212,4316,684],{"class":218},[195,4318,4320],{"id":4319},"run-the-suite","Run the suite",[203,4322,4327],{"className":4323,"code":4324,"filename":4325,"language":4326,"meta":208,"style":208},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","go test .\u002F...              # everything\ngo test -race .\u002F...        # with the race detector\ngo test -cover .\u002F...       # with coverage\ngo test -bench=. -benchmem .\u002F...\n","Terminal","bash",[182,4328,4329,4342,4356,4370],{"__ignoreMap":208},[212,4330,4331,4333,4336,4339],{"class":214,"line":215},[212,4332,207],{"class":236},[212,4334,4335],{"class":309}," test",[212,4337,4338],{"class":309}," .\u002F...",[212,4340,4341],{"class":277},"              # everything\n",[212,4343,4344,4346,4348,4351,4353],{"class":214,"line":252},[212,4345,207],{"class":236},[212,4347,4335],{"class":309},[212,4349,4350],{"class":309}," -race",[212,4352,4338],{"class":309},[212,4354,4355],{"class":277},"        # with the race detector\n",[212,4357,4358,4360,4362,4365,4367],{"class":214,"line":281},[212,4359,207],{"class":236},[212,4361,4335],{"class":309},[212,4363,4364],{"class":309}," -cover",[212,4366,4338],{"class":309},[212,4368,4369],{"class":277},"       # with coverage\n",[212,4371,4372,4374,4376,4379,4382],{"class":214,"line":288},[212,4373,207],{"class":236},[212,4375,4335],{"class":309},[212,4377,4378],{"class":309}," -bench=.",[212,4380,4381],{"class":309}," -benchmem",[212,4383,4384],{"class":309}," .\u002F...\n",[178,4386,4387,4388,1010,4391,1010,4394,1698,4397,240],{},"The repository's Makefile wraps these as ",[182,4389,4390],{},"make test",[182,4392,4393],{},"make test-race",[182,4395,4396],{},"make test-coverage",[182,4398,4399],{},"make bench",[195,4401,4403],{"id":4402},"next-steps","Next steps",[4405,4406,4407,4414],"ul",{},[4408,4409,4410,4413],"li",{},[4411,4412,152],"a",{"href":153}," for benchmarking and the pooling rules that affect tests.",[4408,4415,4416,4419],{},[4411,4417,4418],{"href":80},"Auth rules"," for the behaviour these tests assert.",[4421,4422,4423],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":208,"searchDepth":252,"depth":252,"links":4425},[4426,4427,4428,4429,4430,4431,4432,4433,4434],{"id":197,"depth":252,"text":198},{"id":690,"depth":252,"text":691},{"id":1409,"depth":252,"text":1410},{"id":1715,"depth":252,"text":1716},{"id":2086,"depth":252,"text":2087},{"id":3025,"depth":252,"text":3026},{"id":3719,"depth":252,"text":3720},{"id":4319,"depth":252,"text":4320},{"id":4402,"depth":252,"text":4403},"Test handlers, routes, and auth with httptest or the built-in test helpers.","md",null,{},{"icon":150},{"title":147,"description":4435},"vMdx3IEwc-6PnHsBWgDUk-GgXmaB4HeetZO1wITHRGk",[4443,4445],{"title":136,"path":137,"stem":138,"description":4444,"icon":139,"children":-1},"Serve an interactive API reference next to your spec, and style it.",{"title":152,"path":153,"stem":154,"description":4446,"icon":155,"children":-1},"What ezz precomputes and pools, the rules that keeps, and how to benchmark your own service.",1784970048839]