[{"data":1,"prerenderedAt":2049},["ShallowReactive",2],{"navigation":3,"docs-path-index":340,"-docs-modules-testing-extensions":446,"-docs-modules-testing-extensions-surround":2044},[4],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":64},"Docs","i-lucide-book-marked","\u002Fdocs","docs",[10,30,65,293,318],{"title":11,"path":12,"stem":13,"children":14,"icon":29},"Getting Started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started\u002F1.index",[15,17,21,25],{"title":16,"path":12,"stem":13},"Introduction",{"title":18,"path":19,"stem":20},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F1.getting-started\u002F2.installation",{"title":22,"path":23,"stem":24},"Examples","\u002Fdocs\u002Fgetting-started\u002Fexamples","docs\u002F1.getting-started\u002F3.examples",{"title":26,"path":27,"stem":28},"Project Generator","\u002Fdocs\u002Fgetting-started\u002Fproject-generator","docs\u002F1.getting-started\u002F4.project-generator","i-lucide-rocket",{"title":31,"icon":32,"path":33,"stem":34,"children":35,"page":64},"Core features","i-lucide-square-function","\u002Fdocs\u002Fcore-features","docs\u002F2.core-features",[36,40,44,48,52,56,60],{"title":37,"path":38,"stem":39},"Configuration","\u002Fdocs\u002Fcore-features\u002Fconfiguration","docs\u002F2.core-features\u002F0.configuration",{"title":41,"path":42,"stem":43},"Dependency Injection","\u002Fdocs\u002Fcore-features\u002Fcontainer","docs\u002F2.core-features\u002F1.container",{"title":45,"path":46,"stem":47},"Controllers","\u002Fdocs\u002Fcore-features\u002Fcontrollers","docs\u002F2.core-features\u002F2.controllers",{"title":49,"path":50,"stem":51},"Middlewares","\u002Fdocs\u002Fcore-features\u002Fmiddlewares","docs\u002F2.core-features\u002F3.middlewares",{"title":53,"path":54,"stem":55},"Validation","\u002Fdocs\u002Fcore-features\u002Fvalidation","docs\u002F2.core-features\u002F4.validation",{"title":57,"path":58,"stem":59},"Introspection","\u002Fdocs\u002Fcore-features\u002Fintrospection","docs\u002F2.core-features\u002F5.introspection",{"title":61,"path":62,"stem":63},"Health Checks","\u002Fdocs\u002Fcore-features\u002Fhealth","docs\u002F2.core-features\u002F6.health",false,{"title":66,"icon":67,"defaultOpen":68,"path":69,"stem":70,"children":71,"page":64},"Modules","i-lucide-boxes",true,"\u002Fdocs\u002Fmodules","docs\u002F3.modules",[72,89,100,116,135,154,177,191,206,214,229,240,255,274,282],{"title":73,"icon":64,"defaultOpen":64,"path":74,"stem":75,"children":76,"page":64},"Auth","\u002Fdocs\u002Fmodules\u002Fauth","docs\u002F3.modules\u002F1.auth",[77,81,85],{"title":78,"path":79,"stem":80},"Overview","\u002Fdocs\u002Fmodules\u002Fauth\u002Foverview","docs\u002F3.modules\u002F1.auth\u002F0.overview",{"title":82,"path":83,"stem":84},"Decorators","\u002Fdocs\u002Fmodules\u002Fauth\u002Fdecorators","docs\u002F3.modules\u002F1.auth\u002F1.decorators",{"title":86,"path":87,"stem":88},"API Reference","\u002Fdocs\u002Fmodules\u002Fauth\u002Fapi","docs\u002F3.modules\u002F1.auth\u002F2.api",{"title":90,"icon":64,"defaultOpen":64,"path":91,"stem":92,"children":93,"page":64},"Devtools","\u002Fdocs\u002Fmodules\u002Fdevtools","docs\u002F3.modules\u002F10.devtools",[94,97],{"title":78,"path":95,"stem":96},"\u002Fdocs\u002Fmodules\u002Fdevtools\u002Foverview","docs\u002F3.modules\u002F10.devtools\u002F0.overview",{"title":37,"path":98,"stem":99},"\u002Fdocs\u002Fmodules\u002Fdevtools\u002Fconfiguration","docs\u002F3.modules\u002F10.devtools\u002F1.configuration",{"title":101,"icon":64,"defaultOpen":64,"path":102,"stem":103,"children":104,"page":64},"Telemetry","\u002Fdocs\u002Fmodules\u002Ftelemetry","docs\u002F3.modules\u002F11.telemetry",[105,108,112],{"title":78,"path":106,"stem":107},"\u002Fdocs\u002Fmodules\u002Ftelemetry\u002Foverview","docs\u002F3.modules\u002F11.telemetry\u002F0.overview",{"title":109,"path":110,"stem":111},"API","\u002Fdocs\u002Fmodules\u002Ftelemetry\u002Fapi","docs\u002F3.modules\u002F11.telemetry\u002F1.api",{"title":113,"path":114,"stem":115},"Instrumentation","\u002Fdocs\u002Fmodules\u002Ftelemetry\u002Finstrumentation","docs\u002F3.modules\u002F11.telemetry\u002F2.instrumentation",{"title":117,"icon":64,"defaultOpen":64,"path":118,"stem":119,"children":120,"page":64},"Queue","\u002Fdocs\u002Fmodules\u002Fqueue","docs\u002F3.modules\u002F12.queue",[121,124,128,132],{"title":78,"path":122,"stem":123},"\u002Fdocs\u002Fmodules\u002Fqueue\u002Foverview","docs\u002F3.modules\u002F12.queue\u002F0.overview",{"title":125,"path":126,"stem":127},"Consumers","\u002Fdocs\u002Fmodules\u002Fqueue\u002Fconsumers","docs\u002F3.modules\u002F12.queue\u002F1.consumers",{"title":129,"path":130,"stem":131},"Strategies","\u002Fdocs\u002Fmodules\u002Fqueue\u002Fstrategies","docs\u002F3.modules\u002F12.queue\u002F2.strategies",{"title":109,"path":133,"stem":134},"\u002Fdocs\u002Fmodules\u002Fqueue\u002Fapi","docs\u002F3.modules\u002F12.queue\u002F3.api",{"title":136,"icon":64,"defaultOpen":64,"path":137,"stem":138,"children":139,"page":64},"Scheduler","\u002Fdocs\u002Fmodules\u002Fscheduler","docs\u002F3.modules\u002F13.scheduler",[140,143,147,151],{"title":78,"path":141,"stem":142},"\u002Fdocs\u002Fmodules\u002Fscheduler\u002Foverview","docs\u002F3.modules\u002F13.scheduler\u002F0.overview",{"title":144,"path":145,"stem":146},"Jobs","\u002Fdocs\u002Fmodules\u002Fscheduler\u002Fjobs","docs\u002F3.modules\u002F13.scheduler\u002F1.jobs",{"title":148,"path":149,"stem":150},"Clustering","\u002Fdocs\u002Fmodules\u002Fscheduler\u002Fclustering","docs\u002F3.modules\u002F13.scheduler\u002F2.clustering",{"title":109,"path":152,"stem":153},"\u002Fdocs\u002Fmodules\u002Fscheduler\u002Fapi","docs\u002F3.modules\u002F13.scheduler\u002F3.api",{"title":155,"icon":64,"defaultOpen":64,"path":156,"stem":157,"children":158,"page":64},"Testing","\u002Fdocs\u002Fmodules\u002Ftesting","docs\u002F3.modules\u002F14.testing",[159,162,166,170,174],{"title":78,"path":160,"stem":161},"\u002Fdocs\u002Fmodules\u002Ftesting\u002Foverview","docs\u002F3.modules\u002F14.testing\u002F0.overview",{"title":163,"path":164,"stem":165},"Requests","\u002Fdocs\u002Fmodules\u002Ftesting\u002Frequests","docs\u002F3.modules\u002F14.testing\u002F1.requests",{"title":167,"path":168,"stem":169},"Dependencies","\u002Fdocs\u002Fmodules\u002Ftesting\u002Fdependencies","docs\u002F3.modules\u002F14.testing\u002F2.dependencies",{"title":171,"path":172,"stem":173},"Extensions","\u002Fdocs\u002Fmodules\u002Ftesting\u002Fextensions","docs\u002F3.modules\u002F14.testing\u002F3.extensions",{"title":109,"path":175,"stem":176},"\u002Fdocs\u002Fmodules\u002Ftesting\u002Fapi","docs\u002F3.modules\u002F14.testing\u002F4.api",{"title":178,"icon":64,"defaultOpen":64,"path":179,"stem":180,"children":181,"page":64},"Client","\u002Fdocs\u002Fmodules\u002Fclient","docs\u002F3.modules\u002F15.client",[182,185,188],{"title":78,"path":183,"stem":184},"\u002Fdocs\u002Fmodules\u002Fclient\u002Foverview","docs\u002F3.modules\u002F15.client\u002F0.overview",{"title":163,"path":186,"stem":187},"\u002Fdocs\u002Fmodules\u002Fclient\u002Frequests","docs\u002F3.modules\u002F15.client\u002F1.requests",{"title":109,"path":189,"stem":190},"\u002Fdocs\u002Fmodules\u002Fclient\u002Fapi","docs\u002F3.modules\u002F15.client\u002F2.api",{"title":192,"icon":64,"defaultOpen":64,"path":193,"stem":194,"children":195,"page":64},"Logger","\u002Fdocs\u002Fmodules\u002Flogger","docs\u002F3.modules\u002F2.logger",[196,199,203],{"title":78,"path":197,"stem":198},"\u002Fdocs\u002Fmodules\u002Flogger\u002Foverview","docs\u002F3.modules\u002F2.logger\u002F0.overview",{"title":200,"path":201,"stem":202},"Drains & Adapters","\u002Fdocs\u002Fmodules\u002Flogger\u002Fdrivers","docs\u002F3.modules\u002F2.logger\u002F1.drivers",{"title":109,"path":204,"stem":205},"\u002Fdocs\u002Fmodules\u002Flogger\u002Fapi","docs\u002F3.modules\u002F2.logger\u002F2.api",{"title":207,"icon":64,"defaultOpen":64,"path":208,"stem":209,"children":210,"page":64},"Serverless","\u002Fdocs\u002Fmodules\u002Fserverless","docs\u002F3.modules\u002F3.serverless",[211],{"title":78,"path":212,"stem":213},"\u002Fdocs\u002Fmodules\u002Fserverless\u002Foverview","docs\u002F3.modules\u002F3.serverless\u002F0.overview",{"title":215,"icon":64,"defaultOpen":64,"path":216,"stem":217,"children":218,"page":64},"Storage","\u002Fdocs\u002Fmodules\u002Fstorage","docs\u002F3.modules\u002F4.storage",[219,222,226],{"title":78,"path":220,"stem":221},"\u002Fdocs\u002Fmodules\u002Fstorage\u002Foverview","docs\u002F3.modules\u002F4.storage\u002F0.overview",{"title":223,"path":224,"stem":225},"Drivers","\u002Fdocs\u002Fmodules\u002Fstorage\u002Fdrivers","docs\u002F3.modules\u002F4.storage\u002F1.drivers",{"title":109,"path":227,"stem":228},"\u002Fdocs\u002Fmodules\u002Fstorage\u002Fapi","docs\u002F3.modules\u002F4.storage\u002F2.api",{"title":230,"icon":64,"defaultOpen":64,"path":231,"stem":232,"children":233,"page":64},"Web Sockets","\u002Fdocs\u002Fmodules\u002Fweb-sockets","docs\u002F3.modules\u002F5.web-sockets",[234,237],{"title":78,"path":235,"stem":236},"\u002Fdocs\u002Fmodules\u002Fweb-sockets\u002Foverview","docs\u002F3.modules\u002F5.web-sockets\u002F0.overview",{"title":109,"path":238,"stem":239},"\u002Fdocs\u002Fmodules\u002Fweb-sockets\u002Fapi","docs\u002F3.modules\u002F5.web-sockets\u002F1.api",{"title":241,"icon":64,"defaultOpen":64,"path":242,"stem":243,"children":244,"page":64},"Schema","\u002Fdocs\u002Fmodules\u002Fschema","docs\u002F3.modules\u002F6.schema",[245,248,251],{"title":78,"path":246,"stem":247},"\u002Fdocs\u002Fmodules\u002Fschema\u002Foverview","docs\u002F3.modules\u002F6.schema\u002F0.overview",{"title":82,"path":249,"stem":250},"\u002Fdocs\u002Fmodules\u002Fschema\u002Fdecorators","docs\u002F3.modules\u002F6.schema\u002F1.decorators",{"title":252,"path":253,"stem":254},"Scalar","\u002Fdocs\u002Fmodules\u002Fschema\u002Fscalar","docs\u002F3.modules\u002F6.schema\u002F2.scalar",{"title":256,"icon":64,"defaultOpen":64,"path":257,"stem":258,"children":259,"page":64},"Nitro","\u002Fdocs\u002Fmodules\u002Fnitro","docs\u002F3.modules\u002F7.nitro",[260,263,266,270],{"title":78,"path":261,"stem":262},"\u002Fdocs\u002Fmodules\u002Fnitro\u002Foverview","docs\u002F3.modules\u002F7.nitro\u002F0.overview",{"title":215,"path":264,"stem":265},"\u002Fdocs\u002Fmodules\u002Fnitro\u002Fstorage","docs\u002F3.modules\u002F7.nitro\u002F1.storage",{"title":267,"path":268,"stem":269},"File-Based Routes","\u002Fdocs\u002Fmodules\u002Fnitro\u002Ffile-based-routes","docs\u002F3.modules\u002F7.nitro\u002F2.file-based-routes",{"title":271,"path":272,"stem":273},"Middleware","\u002Fdocs\u002Fmodules\u002Fnitro\u002Fmiddleware","docs\u002F3.modules\u002F7.nitro\u002F3.middleware",{"title":275,"icon":64,"defaultOpen":64,"path":276,"stem":277,"children":278,"page":64},"Vite","\u002Fdocs\u002Fmodules\u002Fvite","docs\u002F3.modules\u002F8.vite",[279],{"title":78,"path":280,"stem":281},"\u002Fdocs\u002Fmodules\u002Fvite\u002Foverview","docs\u002F3.modules\u002F8.vite\u002F0.overview",{"title":283,"icon":64,"defaultOpen":64,"path":284,"stem":285,"children":286,"page":64},"Cache","\u002Fdocs\u002Fmodules\u002Fcache","docs\u002F3.modules\u002F9.cache",[287,290],{"title":78,"path":288,"stem":289},"\u002Fdocs\u002Fmodules\u002Fcache\u002Foverview","docs\u002F3.modules\u002F9.cache\u002F0.overview",{"title":109,"path":291,"stem":292},"\u002Fdocs\u002Fmodules\u002Fcache\u002Fapi","docs\u002F3.modules\u002F9.cache\u002F1.api",{"title":294,"icon":295,"path":296,"stem":297,"children":298,"page":64},"Deployment","i-lucide-cloud-upload","\u002Fdocs\u002Fdeployment","docs\u002F4.deployment",[299,302,306,310,314],{"title":78,"path":300,"stem":301},"\u002Fdocs\u002Fdeployment\u002Foverview","docs\u002F4.deployment\u002F0.overview",{"title":303,"path":304,"stem":305},"Vercel","\u002Fdocs\u002Fdeployment\u002Fvercel","docs\u002F4.deployment\u002F1.vercel",{"title":307,"path":308,"stem":309},"AWS Lambda","\u002Fdocs\u002Fdeployment\u002Faws-lambda","docs\u002F4.deployment\u002F2.aws-lambda",{"title":311,"path":312,"stem":313},"Azure Functions","\u002Fdocs\u002Fdeployment\u002Fazure-functions","docs\u002F4.deployment\u002F3.azure-functions",{"title":315,"path":316,"stem":317},"Node.js Server","\u002Fdocs\u002Fdeployment\u002Fnode-server","docs\u002F4.deployment\u002F4.node-server",{"title":319,"icon":320,"path":321,"stem":322,"children":323,"page":64},"Advanced","i-lucide-brain","\u002Fdocs\u002Fadvanced","docs\u002F99.advanced",[324,328,332,336],{"title":325,"path":326,"stem":327},"Custom Decorator","\u002Fdocs\u002Fadvanced\u002Fcustom-decorator","docs\u002F99.advanced\u002F1.custom-decorator",{"title":329,"path":330,"stem":331},"Plugins","\u002Fdocs\u002Fadvanced\u002Fcustom-plugin","docs\u002F99.advanced\u002F2.custom-plugin",{"title":333,"path":334,"stem":335},"Custom CLI Command","\u002Fdocs\u002Fadvanced\u002Fcustom-cli-command","docs\u002F99.advanced\u002F3.custom-cli-command",{"title":337,"path":338,"stem":339},"Writing a Recipe","\u002Fdocs\u002Fadvanced\u002Fwriting-a-recipe","docs\u002F99.advanced\u002F4.writing-a-recipe",[341,343,345,346,347,348,349,351,352,353,354,355,356,357,358,360,362,363,364,365,367,368,369,371,372,373,374,376,377,378,379,380,382,383,384,385,386,388,389,390,391,392,393,395,396,397,398,400,401,402,403,405,406,408,409,410,411,413,414,415,417,418,419,420,422,423,424,425,426,428,429,431,432,433,435,436,437,438,439,440,442,443,444,445],{"path":342},"\u002Fdocs\u002F.navigation",{"path":344},"\u002Fdocs\u002Fgetting-started\u002F.navigation",{"path":12},{"path":19},{"path":23},{"path":27},{"path":350},"\u002Fdocs\u002Fcore-features\u002F.navigation",{"path":38},{"path":42},{"path":46},{"path":50},{"path":54},{"path":58},{"path":62},{"path":359},"\u002Fdocs\u002Fmodules\u002F.navigation",{"path":361},"\u002Fdocs\u002Fmodules\u002Fauth\u002F.navigation",{"path":79},{"path":83},{"path":87},{"path":366},"\u002Fdocs\u002Fmodules\u002Fdevtools\u002F.navigation",{"path":95},{"path":98},{"path":370},"\u002Fdocs\u002Fmodules\u002Ftelemetry\u002F.navigation",{"path":106},{"path":110},{"path":114},{"path":375},"\u002Fdocs\u002Fmodules\u002Fqueue\u002F.navigation",{"path":122},{"path":126},{"path":130},{"path":133},{"path":381},"\u002Fdocs\u002Fmodules\u002Fscheduler\u002F.navigation",{"path":141},{"path":145},{"path":149},{"path":152},{"path":387},"\u002Fdocs\u002Fmodules\u002Ftesting\u002F.navigation",{"path":160},{"path":164},{"path":168},{"path":172},{"path":175},{"path":394},"\u002Fdocs\u002Fmodules\u002Fclient\u002F.navigation",{"path":183},{"path":186},{"path":189},{"path":399},"\u002Fdocs\u002Fmodules\u002Flogger\u002F.navigation",{"path":197},{"path":201},{"path":204},{"path":404},"\u002Fdocs\u002Fmodules\u002Fserverless\u002F.navigation",{"path":212},{"path":407},"\u002Fdocs\u002Fmodules\u002Fstorage\u002F.navigation",{"path":220},{"path":224},{"path":227},{"path":412},"\u002Fdocs\u002Fmodules\u002Fweb-sockets\u002F.navigation",{"path":235},{"path":238},{"path":416},"\u002Fdocs\u002Fmodules\u002Fschema\u002F.navigation",{"path":246},{"path":249},{"path":253},{"path":421},"\u002Fdocs\u002Fmodules\u002Fnitro\u002F.navigation",{"path":261},{"path":264},{"path":268},{"path":272},{"path":427},"\u002Fdocs\u002Fmodules\u002Fvite\u002F.navigation",{"path":280},{"path":430},"\u002Fdocs\u002Fmodules\u002Fcache\u002F.navigation",{"path":288},{"path":291},{"path":434},"\u002Fdocs\u002Fdeployment\u002F.navigation",{"path":300},{"path":304},{"path":308},{"path":312},{"path":316},{"path":441},"\u002Fdocs\u002Fadvanced\u002F.navigation",{"path":326},{"path":330},{"path":334},{"path":338},{"id":447,"title":171,"body":448,"description":2038,"extension":2039,"links":2040,"meta":2041,"navigation":68,"path":172,"seo":2042,"stem":173,"__hash__":2043},"docs\u002Fdocs\u002F3.modules\u002F14.testing\u002F3.extensions.md",{"type":449,"value":450,"toc":2030},"minimark",[451,468,472,494,836,855,862,865,878,1079,1082,1085,1102,1294,1301,1304,1325,1589,1592,1599,1752,1756,1779,2014,2026],[452,453,454,455,459,460,463,464,467],"p",{},"Extensions plug other modules into ",[456,457,458],"code",{},"createTestApp()",". Each module ships its own from a ",[456,461,462],{},"\u002Ftesting"," entry, so ",[456,465,466],{},"@vercube\u002Ftesting"," never depends on them.",[469,470,73],"h2",{"id":471},"auth",[452,473,474,477,478,481,482,485,486,489,490,493],{},[456,475,476],{},"testAuth()"," from ",[456,479,480],{},"@vercube\u002Ftesting\u002Fauth"," replaces the ",[456,483,484],{},"AuthProvider",". A request sent with ",[456,487,488],{},".as(user)"," is authenticated as that user, and ",[456,491,492],{},"@User()"," receives it.",[495,496,501],"pre",{"className":497,"code":498,"language":499,"meta":500,"style":500},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { testAuth } from '@vercube\u002Ftesting\u002Fauth';\n\nconst app = await createTestApp({\n  controllers: [AdminController],\n  extensions: [testAuth()],\n});\n\nawait app.get('\u002Fadmin').expect(401);\nawait app\n  .get('\u002Fadmin')\n  .as({ id: '1', roles: ['admin'] })\n  .expect(200);\nawait app\n  .get('\u002Fadmin')\n  .as({ id: '2', roles: ['user'] })\n  .expect(401);\n","ts","",[456,502,503,538,544,570,586,605,616,621,662,670,689,738,754,761,778,821],{"__ignoreMap":500},[504,505,508,512,516,520,523,526,529,532,535],"span",{"class":506,"line":507},"line",1,[504,509,511],{"class":510},"s7zQu","import",[504,513,515],{"class":514},"sMK4o"," {",[504,517,519],{"class":518},"sTEyZ"," testAuth",[504,521,522],{"class":514}," }",[504,524,525],{"class":510}," from",[504,527,528],{"class":514}," '",[504,530,480],{"class":531},"sfazB",[504,533,534],{"class":514},"'",[504,536,537],{"class":514},";\n",[504,539,541],{"class":506,"line":540},2,[504,542,543],{"emptyLinePlaceholder":68},"\n",[504,545,547,551,554,557,560,564,567],{"class":506,"line":546},3,[504,548,550],{"class":549},"spNyl","const",[504,552,553],{"class":518}," app ",[504,555,556],{"class":514},"=",[504,558,559],{"class":510}," await",[504,561,563],{"class":562},"s2Zo4"," createTestApp",[504,565,566],{"class":518},"(",[504,568,569],{"class":514},"{\n",[504,571,573,577,580,583],{"class":506,"line":572},4,[504,574,576],{"class":575},"swJcz","  controllers",[504,578,579],{"class":514},":",[504,581,582],{"class":518}," [AdminController]",[504,584,585],{"class":514},",\n",[504,587,589,592,594,597,600,603],{"class":506,"line":588},5,[504,590,591],{"class":575},"  extensions",[504,593,579],{"class":514},[504,595,596],{"class":518}," [",[504,598,599],{"class":562},"testAuth",[504,601,602],{"class":518},"()]",[504,604,585],{"class":514},[504,606,608,611,614],{"class":506,"line":607},6,[504,609,610],{"class":514},"}",[504,612,613],{"class":518},")",[504,615,537],{"class":514},[504,617,619],{"class":506,"line":618},7,[504,620,543],{"emptyLinePlaceholder":68},[504,622,624,627,630,633,636,638,640,643,645,647,649,652,654,658,660],{"class":506,"line":623},8,[504,625,626],{"class":510},"await",[504,628,629],{"class":518}," app",[504,631,632],{"class":514},".",[504,634,635],{"class":562},"get",[504,637,566],{"class":518},[504,639,534],{"class":514},[504,641,642],{"class":531},"\u002Fadmin",[504,644,534],{"class":514},[504,646,613],{"class":518},[504,648,632],{"class":514},[504,650,651],{"class":562},"expect",[504,653,566],{"class":518},[504,655,657],{"class":656},"sbssI","401",[504,659,613],{"class":518},[504,661,537],{"class":514},[504,663,665,667],{"class":506,"line":664},9,[504,666,626],{"class":510},[504,668,669],{"class":518}," app\n",[504,671,673,676,678,680,682,684,686],{"class":506,"line":672},10,[504,674,675],{"class":514},"  .",[504,677,635],{"class":562},[504,679,566],{"class":518},[504,681,534],{"class":514},[504,683,642],{"class":531},[504,685,534],{"class":514},[504,687,688],{"class":518},")\n",[504,690,692,694,697,699,702,705,707,709,712,714,717,720,722,724,726,729,731,734,736],{"class":506,"line":691},11,[504,693,675],{"class":514},[504,695,696],{"class":562},"as",[504,698,566],{"class":518},[504,700,701],{"class":514},"{",[504,703,704],{"class":575}," id",[504,706,579],{"class":514},[504,708,528],{"class":514},[504,710,711],{"class":531},"1",[504,713,534],{"class":514},[504,715,716],{"class":514},",",[504,718,719],{"class":575}," roles",[504,721,579],{"class":514},[504,723,596],{"class":518},[504,725,534],{"class":514},[504,727,728],{"class":531},"admin",[504,730,534],{"class":514},[504,732,733],{"class":518},"] ",[504,735,610],{"class":514},[504,737,688],{"class":518},[504,739,741,743,745,747,750,752],{"class":506,"line":740},12,[504,742,675],{"class":514},[504,744,651],{"class":562},[504,746,566],{"class":518},[504,748,749],{"class":656},"200",[504,751,613],{"class":518},[504,753,537],{"class":514},[504,755,757,759],{"class":506,"line":756},13,[504,758,626],{"class":510},[504,760,669],{"class":518},[504,762,764,766,768,770,772,774,776],{"class":506,"line":763},14,[504,765,675],{"class":514},[504,767,635],{"class":562},[504,769,566],{"class":518},[504,771,534],{"class":514},[504,773,642],{"class":531},[504,775,534],{"class":514},[504,777,688],{"class":518},[504,779,781,783,785,787,789,791,793,795,798,800,802,804,806,808,810,813,815,817,819],{"class":506,"line":780},15,[504,782,675],{"class":514},[504,784,696],{"class":562},[504,786,566],{"class":518},[504,788,701],{"class":514},[504,790,704],{"class":575},[504,792,579],{"class":514},[504,794,528],{"class":514},[504,796,797],{"class":531},"2",[504,799,534],{"class":514},[504,801,716],{"class":514},[504,803,719],{"class":575},[504,805,579],{"class":514},[504,807,596],{"class":518},[504,809,534],{"class":514},[504,811,812],{"class":531},"user",[504,814,534],{"class":514},[504,816,733],{"class":518},[504,818,610],{"class":514},[504,820,688],{"class":518},[504,822,824,826,828,830,832,834],{"class":506,"line":823},16,[504,825,675],{"class":514},[504,827,651],{"class":562},[504,829,566],{"class":518},[504,831,657],{"class":656},[504,833,613],{"class":518},[504,835,537],{"class":514},[452,837,838,839,842,843,846,847,850,851,854],{},"Roles required by ",[456,840,841],{},"@Auth({ roles })"," are checked against ",[456,844,845],{},"user.roles",". Read them elsewhere with ",[456,848,849],{},"testAuth({ roles: (user) => user.groups })",". Both a missing user and a missing role answer 401, as ",[456,852,853],{},"@Auth()"," does.",[452,856,857,858,861],{},"The user travels with the ",[456,859,860],{},"Request"," object the builder creates, never in a header. A request built by hand with the same headers is not authenticated, so the provider authenticates nobody even if it were shipped by mistake.",[469,863,101],{"id":864},"telemetry",[452,866,867,477,870,873,874,877],{},[456,868,869],{},"testTelemetry()",[456,871,872],{},"@vercube\u002Ftesting\u002Ftelemetry"," registers in-memory OpenTelemetry providers, installs ",[456,875,876],{},"TelemetryPlugin",", and shuts the providers down when the app closes.",[495,879,881],{"className":497,"code":880,"language":499,"meta":500,"style":500},"import { testTelemetry } from '@vercube\u002Ftesting\u002Ftelemetry';\n\nconst telemetry = testTelemetry();\nconst app = await createTestApp({\n  controllers: [UserController],\n  extensions: [telemetry],\n});\n\nawait app.get('\u002Fusers\u002F1');\nawait telemetry.settle();\n\nexpect(\n  telemetry.span('GET \u002Fusers\u002F:id')?.attributes['http.response.status_code'],\n).toBe(200);\n",[456,882,883,904,908,924,940,951,962,970,974,997,1013,1017,1024,1062],{"__ignoreMap":500},[504,884,885,887,889,892,894,896,898,900,902],{"class":506,"line":507},[504,886,511],{"class":510},[504,888,515],{"class":514},[504,890,891],{"class":518}," testTelemetry",[504,893,522],{"class":514},[504,895,525],{"class":510},[504,897,528],{"class":514},[504,899,872],{"class":531},[504,901,534],{"class":514},[504,903,537],{"class":514},[504,905,906],{"class":506,"line":540},[504,907,543],{"emptyLinePlaceholder":68},[504,909,910,912,915,917,919,922],{"class":506,"line":546},[504,911,550],{"class":549},[504,913,914],{"class":518}," telemetry ",[504,916,556],{"class":514},[504,918,891],{"class":562},[504,920,921],{"class":518},"()",[504,923,537],{"class":514},[504,925,926,928,930,932,934,936,938],{"class":506,"line":572},[504,927,550],{"class":549},[504,929,553],{"class":518},[504,931,556],{"class":514},[504,933,559],{"class":510},[504,935,563],{"class":562},[504,937,566],{"class":518},[504,939,569],{"class":514},[504,941,942,944,946,949],{"class":506,"line":588},[504,943,576],{"class":575},[504,945,579],{"class":514},[504,947,948],{"class":518}," [UserController]",[504,950,585],{"class":514},[504,952,953,955,957,960],{"class":506,"line":607},[504,954,591],{"class":575},[504,956,579],{"class":514},[504,958,959],{"class":518}," [telemetry]",[504,961,585],{"class":514},[504,963,964,966,968],{"class":506,"line":618},[504,965,610],{"class":514},[504,967,613],{"class":518},[504,969,537],{"class":514},[504,971,972],{"class":506,"line":623},[504,973,543],{"emptyLinePlaceholder":68},[504,975,976,978,980,982,984,986,988,991,993,995],{"class":506,"line":664},[504,977,626],{"class":510},[504,979,629],{"class":518},[504,981,632],{"class":514},[504,983,635],{"class":562},[504,985,566],{"class":518},[504,987,534],{"class":514},[504,989,990],{"class":531},"\u002Fusers\u002F1",[504,992,534],{"class":514},[504,994,613],{"class":518},[504,996,537],{"class":514},[504,998,999,1001,1004,1006,1009,1011],{"class":506,"line":672},[504,1000,626],{"class":510},[504,1002,1003],{"class":518}," telemetry",[504,1005,632],{"class":514},[504,1007,1008],{"class":562},"settle",[504,1010,921],{"class":518},[504,1012,537],{"class":514},[504,1014,1015],{"class":506,"line":691},[504,1016,543],{"emptyLinePlaceholder":68},[504,1018,1019,1021],{"class":506,"line":740},[504,1020,651],{"class":562},[504,1022,1023],{"class":518},"(\n",[504,1025,1026,1029,1031,1033,1035,1037,1040,1042,1044,1047,1050,1052,1055,1057,1060],{"class":506,"line":756},[504,1027,1028],{"class":518},"  telemetry",[504,1030,632],{"class":514},[504,1032,504],{"class":562},[504,1034,566],{"class":518},[504,1036,534],{"class":514},[504,1038,1039],{"class":531},"GET \u002Fusers\u002F:id",[504,1041,534],{"class":514},[504,1043,613],{"class":518},[504,1045,1046],{"class":514},"?.",[504,1048,1049],{"class":518},"attributes[",[504,1051,534],{"class":514},[504,1053,1054],{"class":531},"http.response.status_code",[504,1056,534],{"class":514},[504,1058,1059],{"class":518},"]",[504,1061,585],{"class":514},[504,1063,1064,1066,1068,1071,1073,1075,1077],{"class":506,"line":763},[504,1065,613],{"class":518},[504,1067,632],{"class":514},[504,1069,1070],{"class":562},"toBe",[504,1072,566],{"class":518},[504,1074,749],{"class":656},[504,1076,613],{"class":518},[504,1078,537],{"class":514},[452,1080,1081],{},"The providers are global to the process, so do not run two apps with this extension at the same time in one test file.",[469,1083,136],{"id":1084},"scheduler",[452,1086,1087,477,1090,1093,1094,1097,1098,1101],{},[456,1088,1089],{},"withManualClock()",[456,1091,1092],{},"@vercube\u002Fscheduler\u002Ftesting"," runs the scheduler on a ",[456,1095,1096],{},"ManualClock",". Time only moves when the test moves it, and ",[456,1099,1100],{},"advance()"," waits for the runs it started.",[495,1103,1105],{"className":497,"code":1104,"language":499,"meta":500,"style":500},"import { withManualClock } from '@vercube\u002Fscheduler\u002Ftesting';\n\nconst time = withManualClock('2026-01-01T00:00:00Z');\nconst app = await createTestApp({ providers: [ReportJobs], extensions: [time] });\n\nawait time.clock.advance('1h');\nexpect(app.resolve(ReportJobs).generated).toBe(1);\n\nawait time.scheduler.trigger('reports.hourly');\n",[456,1106,1107,1128,1132,1156,1196,1200,1230,1262,1266],{"__ignoreMap":500},[504,1108,1109,1111,1113,1116,1118,1120,1122,1124,1126],{"class":506,"line":507},[504,1110,511],{"class":510},[504,1112,515],{"class":514},[504,1114,1115],{"class":518}," withManualClock",[504,1117,522],{"class":514},[504,1119,525],{"class":510},[504,1121,528],{"class":514},[504,1123,1092],{"class":531},[504,1125,534],{"class":514},[504,1127,537],{"class":514},[504,1129,1130],{"class":506,"line":540},[504,1131,543],{"emptyLinePlaceholder":68},[504,1133,1134,1136,1139,1141,1143,1145,1147,1150,1152,1154],{"class":506,"line":546},[504,1135,550],{"class":549},[504,1137,1138],{"class":518}," time ",[504,1140,556],{"class":514},[504,1142,1115],{"class":562},[504,1144,566],{"class":518},[504,1146,534],{"class":514},[504,1148,1149],{"class":531},"2026-01-01T00:00:00Z",[504,1151,534],{"class":514},[504,1153,613],{"class":518},[504,1155,537],{"class":514},[504,1157,1158,1160,1162,1164,1166,1168,1170,1172,1175,1177,1180,1182,1185,1187,1190,1192,1194],{"class":506,"line":572},[504,1159,550],{"class":549},[504,1161,553],{"class":518},[504,1163,556],{"class":514},[504,1165,559],{"class":510},[504,1167,563],{"class":562},[504,1169,566],{"class":518},[504,1171,701],{"class":514},[504,1173,1174],{"class":575}," providers",[504,1176,579],{"class":514},[504,1178,1179],{"class":518}," [ReportJobs]",[504,1181,716],{"class":514},[504,1183,1184],{"class":575}," extensions",[504,1186,579],{"class":514},[504,1188,1189],{"class":518}," [time] ",[504,1191,610],{"class":514},[504,1193,613],{"class":518},[504,1195,537],{"class":514},[504,1197,1198],{"class":506,"line":588},[504,1199,543],{"emptyLinePlaceholder":68},[504,1201,1202,1204,1207,1209,1212,1214,1217,1219,1221,1224,1226,1228],{"class":506,"line":607},[504,1203,626],{"class":510},[504,1205,1206],{"class":518}," time",[504,1208,632],{"class":514},[504,1210,1211],{"class":518},"clock",[504,1213,632],{"class":514},[504,1215,1216],{"class":562},"advance",[504,1218,566],{"class":518},[504,1220,534],{"class":514},[504,1222,1223],{"class":531},"1h",[504,1225,534],{"class":514},[504,1227,613],{"class":518},[504,1229,537],{"class":514},[504,1231,1232,1234,1237,1239,1242,1245,1247,1250,1252,1254,1256,1258,1260],{"class":506,"line":618},[504,1233,651],{"class":562},[504,1235,1236],{"class":518},"(app",[504,1238,632],{"class":514},[504,1240,1241],{"class":562},"resolve",[504,1243,1244],{"class":518},"(ReportJobs)",[504,1246,632],{"class":514},[504,1248,1249],{"class":518},"generated)",[504,1251,632],{"class":514},[504,1253,1070],{"class":562},[504,1255,566],{"class":518},[504,1257,711],{"class":656},[504,1259,613],{"class":518},[504,1261,537],{"class":514},[504,1263,1264],{"class":506,"line":623},[504,1265,543],{"emptyLinePlaceholder":68},[504,1267,1268,1270,1272,1274,1276,1278,1281,1283,1285,1288,1290,1292],{"class":506,"line":664},[504,1269,626],{"class":510},[504,1271,1206],{"class":518},[504,1273,632],{"class":514},[504,1275,1084],{"class":518},[504,1277,632],{"class":514},[504,1279,1280],{"class":562},"trigger",[504,1282,566],{"class":518},[504,1284,534],{"class":514},[504,1286,1287],{"class":531},"reports.hourly",[504,1289,534],{"class":514},[504,1291,613],{"class":518},[504,1293,537],{"class":514},[452,1295,1296,1297,1300],{},"Cluster jobs get a ",[456,1298,1299],{},"MemoryStore"," unless the second argument mounts another store.",[469,1302,117],{"id":1303},"queue",[452,1305,1306,477,1309,1312,1313,1316,1317,1320,1321,1324],{},[456,1307,1308],{},"withMemoryQueue()",[456,1310,1311],{},"@vercube\u002Fqueue\u002Ftesting"," mounts the in-memory strategy as ",[456,1314,1315],{},"default",". ",[456,1318,1319],{},"drain()"," waits until every published job has been processed, retries included, and ",[456,1322,1323],{},"events()"," lists the attempts.",[495,1326,1328],{"className":497,"code":1327,"language":499,"meta":500,"style":500},"import { withMemoryQueue } from '@vercube\u002Fqueue\u002Ftesting';\n\nconst queue = withMemoryQueue();\nconst app = await createTestApp({\n  controllers: [SignupController],\n  providers: [EmailConsumer],\n  extensions: [queue],\n});\n\nawait app.post('\u002Fsignup').json({ email: 'ada@example.com' }).expect(201);\nawait queue.drain();\n\nexpect(queue.events()).toMatchObject([\n  { queue: 'emails', job: 'welcome', status: 'completed' },\n]);\n",[456,1329,1330,1351,1355,1370,1386,1397,1409,1420,1428,1432,1492,1508,1512,1535,1582],{"__ignoreMap":500},[504,1331,1332,1334,1336,1339,1341,1343,1345,1347,1349],{"class":506,"line":507},[504,1333,511],{"class":510},[504,1335,515],{"class":514},[504,1337,1338],{"class":518}," withMemoryQueue",[504,1340,522],{"class":514},[504,1342,525],{"class":510},[504,1344,528],{"class":514},[504,1346,1311],{"class":531},[504,1348,534],{"class":514},[504,1350,537],{"class":514},[504,1352,1353],{"class":506,"line":540},[504,1354,543],{"emptyLinePlaceholder":68},[504,1356,1357,1359,1362,1364,1366,1368],{"class":506,"line":546},[504,1358,550],{"class":549},[504,1360,1361],{"class":518}," queue ",[504,1363,556],{"class":514},[504,1365,1338],{"class":562},[504,1367,921],{"class":518},[504,1369,537],{"class":514},[504,1371,1372,1374,1376,1378,1380,1382,1384],{"class":506,"line":572},[504,1373,550],{"class":549},[504,1375,553],{"class":518},[504,1377,556],{"class":514},[504,1379,559],{"class":510},[504,1381,563],{"class":562},[504,1383,566],{"class":518},[504,1385,569],{"class":514},[504,1387,1388,1390,1392,1395],{"class":506,"line":588},[504,1389,576],{"class":575},[504,1391,579],{"class":514},[504,1393,1394],{"class":518}," [SignupController]",[504,1396,585],{"class":514},[504,1398,1399,1402,1404,1407],{"class":506,"line":607},[504,1400,1401],{"class":575},"  providers",[504,1403,579],{"class":514},[504,1405,1406],{"class":518}," [EmailConsumer]",[504,1408,585],{"class":514},[504,1410,1411,1413,1415,1418],{"class":506,"line":618},[504,1412,591],{"class":575},[504,1414,579],{"class":514},[504,1416,1417],{"class":518}," [queue]",[504,1419,585],{"class":514},[504,1421,1422,1424,1426],{"class":506,"line":623},[504,1423,610],{"class":514},[504,1425,613],{"class":518},[504,1427,537],{"class":514},[504,1429,1430],{"class":506,"line":664},[504,1431,543],{"emptyLinePlaceholder":68},[504,1433,1434,1436,1438,1440,1443,1445,1447,1450,1452,1454,1456,1459,1461,1463,1466,1468,1470,1473,1475,1477,1479,1481,1483,1485,1488,1490],{"class":506,"line":672},[504,1435,626],{"class":510},[504,1437,629],{"class":518},[504,1439,632],{"class":514},[504,1441,1442],{"class":562},"post",[504,1444,566],{"class":518},[504,1446,534],{"class":514},[504,1448,1449],{"class":531},"\u002Fsignup",[504,1451,534],{"class":514},[504,1453,613],{"class":518},[504,1455,632],{"class":514},[504,1457,1458],{"class":562},"json",[504,1460,566],{"class":518},[504,1462,701],{"class":514},[504,1464,1465],{"class":575}," email",[504,1467,579],{"class":514},[504,1469,528],{"class":514},[504,1471,1472],{"class":531},"ada@example.com",[504,1474,534],{"class":514},[504,1476,522],{"class":514},[504,1478,613],{"class":518},[504,1480,632],{"class":514},[504,1482,651],{"class":562},[504,1484,566],{"class":518},[504,1486,1487],{"class":656},"201",[504,1489,613],{"class":518},[504,1491,537],{"class":514},[504,1493,1494,1496,1499,1501,1504,1506],{"class":506,"line":691},[504,1495,626],{"class":510},[504,1497,1498],{"class":518}," queue",[504,1500,632],{"class":514},[504,1502,1503],{"class":562},"drain",[504,1505,921],{"class":518},[504,1507,537],{"class":514},[504,1509,1510],{"class":506,"line":740},[504,1511,543],{"emptyLinePlaceholder":68},[504,1513,1514,1516,1519,1521,1524,1527,1529,1532],{"class":506,"line":756},[504,1515,651],{"class":562},[504,1517,1518],{"class":518},"(queue",[504,1520,632],{"class":514},[504,1522,1523],{"class":562},"events",[504,1525,1526],{"class":518},"())",[504,1528,632],{"class":514},[504,1530,1531],{"class":562},"toMatchObject",[504,1533,1534],{"class":518},"([\n",[504,1536,1537,1540,1542,1544,1546,1549,1551,1553,1556,1558,1560,1563,1565,1567,1570,1572,1574,1577,1579],{"class":506,"line":763},[504,1538,1539],{"class":514},"  {",[504,1541,1498],{"class":575},[504,1543,579],{"class":514},[504,1545,528],{"class":514},[504,1547,1548],{"class":531},"emails",[504,1550,534],{"class":514},[504,1552,716],{"class":514},[504,1554,1555],{"class":575}," job",[504,1557,579],{"class":514},[504,1559,528],{"class":514},[504,1561,1562],{"class":531},"welcome",[504,1564,534],{"class":514},[504,1566,716],{"class":514},[504,1568,1569],{"class":575}," status",[504,1571,579],{"class":514},[504,1573,528],{"class":514},[504,1575,1576],{"class":531},"completed",[504,1578,534],{"class":514},[504,1580,1581],{"class":514}," },\n",[504,1583,1584,1587],{"class":506,"line":780},[504,1585,1586],{"class":518},"])",[504,1588,537],{"class":514},[469,1590,215],{"id":1591},"storage",[452,1593,1594,1595,1598],{},"Storage needs no extension: mount ",[456,1596,1597],{},"MemoryStorage"," once the app has booted.",[495,1600,1602],{"className":497,"code":1601,"language":499,"meta":500,"style":500},"import { StorageManager } from '@vercube\u002Fstorage';\nimport { MemoryStorage } from '@vercube\u002Fstorage\u002Fdrivers\u002FMemoryStorage';\n\nconst app = await createTestApp({\n  controllers: [UploadController],\n  providers: [StorageManager],\n});\nawait app\n  .resolve(StorageManager)\n  .mount({ name: 'uploads', storage: MemoryStorage });\n",[456,1603,1604,1626,1648,1652,1668,1679,1690,1698,1704,1713],{"__ignoreMap":500},[504,1605,1606,1608,1610,1613,1615,1617,1619,1622,1624],{"class":506,"line":507},[504,1607,511],{"class":510},[504,1609,515],{"class":514},[504,1611,1612],{"class":518}," StorageManager",[504,1614,522],{"class":514},[504,1616,525],{"class":510},[504,1618,528],{"class":514},[504,1620,1621],{"class":531},"@vercube\u002Fstorage",[504,1623,534],{"class":514},[504,1625,537],{"class":514},[504,1627,1628,1630,1632,1635,1637,1639,1641,1644,1646],{"class":506,"line":540},[504,1629,511],{"class":510},[504,1631,515],{"class":514},[504,1633,1634],{"class":518}," MemoryStorage",[504,1636,522],{"class":514},[504,1638,525],{"class":510},[504,1640,528],{"class":514},[504,1642,1643],{"class":531},"@vercube\u002Fstorage\u002Fdrivers\u002FMemoryStorage",[504,1645,534],{"class":514},[504,1647,537],{"class":514},[504,1649,1650],{"class":506,"line":546},[504,1651,543],{"emptyLinePlaceholder":68},[504,1653,1654,1656,1658,1660,1662,1664,1666],{"class":506,"line":572},[504,1655,550],{"class":549},[504,1657,553],{"class":518},[504,1659,556],{"class":514},[504,1661,559],{"class":510},[504,1663,563],{"class":562},[504,1665,566],{"class":518},[504,1667,569],{"class":514},[504,1669,1670,1672,1674,1677],{"class":506,"line":588},[504,1671,576],{"class":575},[504,1673,579],{"class":514},[504,1675,1676],{"class":518}," [UploadController]",[504,1678,585],{"class":514},[504,1680,1681,1683,1685,1688],{"class":506,"line":607},[504,1682,1401],{"class":575},[504,1684,579],{"class":514},[504,1686,1687],{"class":518}," [StorageManager]",[504,1689,585],{"class":514},[504,1691,1692,1694,1696],{"class":506,"line":618},[504,1693,610],{"class":514},[504,1695,613],{"class":518},[504,1697,537],{"class":514},[504,1699,1700,1702],{"class":506,"line":623},[504,1701,626],{"class":510},[504,1703,669],{"class":518},[504,1705,1706,1708,1710],{"class":506,"line":664},[504,1707,675],{"class":514},[504,1709,1241],{"class":562},[504,1711,1712],{"class":518},"(StorageManager)\n",[504,1714,1715,1717,1720,1722,1724,1727,1729,1731,1734,1736,1738,1741,1743,1746,1748,1750],{"class":506,"line":672},[504,1716,675],{"class":514},[504,1718,1719],{"class":562},"mount",[504,1721,566],{"class":518},[504,1723,701],{"class":514},[504,1725,1726],{"class":575}," name",[504,1728,579],{"class":514},[504,1730,528],{"class":514},[504,1732,1733],{"class":531},"uploads",[504,1735,534],{"class":514},[504,1737,716],{"class":514},[504,1739,1740],{"class":575}," storage",[504,1742,579],{"class":514},[504,1744,1745],{"class":518}," MemoryStorage ",[504,1747,610],{"class":514},[504,1749,613],{"class":518},[504,1751,537],{"class":514},[469,1753,1755],{"id":1754},"writing-an-extension","Writing an extension",[452,1757,1758,1759,1762,1763,1766,1767,1770,1771,1774,1775,1778],{},"An extension is an object with a ",[456,1760,1761],{},"name"," and any of four hooks: ",[456,1764,1765],{},"setup(app)"," before plugins are applied, ",[456,1768,1769],{},"beforeInitialize(app)"," once they have been, ",[456,1772,1773],{},"ready(testApp)"," once the app has booted, and ",[456,1776,1777],{},"teardown()"," when it closes. Teardown runs last extension first.",[495,1780,1782],{"className":497,"code":1781,"language":499,"meta":500,"style":500},"import type { TestExtension } from '@vercube\u002Ftesting';\n\nexport function withFakeMailer(): TestExtension & { sent: Mail[] } {\n  const sent: Mail[] = [];\n\n  return {\n    name: 'withFakeMailer',\n    sent,\n    beforeInitialize: (app) => {\n      app.container.bindMock(Mailer, {\n        send: async (mail: Mail) => void sent.push(mail),\n      });\n    },\n  };\n}\n",[456,1783,1784,1808,1812,1850,1870,1874,1881,1897,1904,1925,1949,1990,1999,2004,2009],{"__ignoreMap":500},[504,1785,1786,1788,1791,1793,1796,1798,1800,1802,1804,1806],{"class":506,"line":507},[504,1787,511],{"class":510},[504,1789,1790],{"class":510}," type",[504,1792,515],{"class":514},[504,1794,1795],{"class":518}," TestExtension",[504,1797,522],{"class":514},[504,1799,525],{"class":510},[504,1801,528],{"class":514},[504,1803,466],{"class":531},[504,1805,534],{"class":514},[504,1807,537],{"class":514},[504,1809,1810],{"class":506,"line":540},[504,1811,543],{"emptyLinePlaceholder":68},[504,1813,1814,1817,1820,1823,1826,1829,1832,1834,1837,1839,1842,1845,1847],{"class":506,"line":546},[504,1815,1816],{"class":510},"export",[504,1818,1819],{"class":549}," function",[504,1821,1822],{"class":562}," withFakeMailer",[504,1824,1825],{"class":514},"():",[504,1827,1795],{"class":1828},"sBMFI",[504,1830,1831],{"class":514}," &",[504,1833,515],{"class":514},[504,1835,1836],{"class":575}," sent",[504,1838,579],{"class":514},[504,1840,1841],{"class":1828}," Mail",[504,1843,1844],{"class":518},"[] ",[504,1846,610],{"class":514},[504,1848,1849],{"class":514}," {\n",[504,1851,1852,1855,1857,1859,1861,1863,1865,1868],{"class":506,"line":572},[504,1853,1854],{"class":549},"  const",[504,1856,1836],{"class":518},[504,1858,579],{"class":514},[504,1860,1841],{"class":1828},[504,1862,1844],{"class":575},[504,1864,556],{"class":514},[504,1866,1867],{"class":575}," []",[504,1869,537],{"class":514},[504,1871,1872],{"class":506,"line":588},[504,1873,543],{"emptyLinePlaceholder":68},[504,1875,1876,1879],{"class":506,"line":607},[504,1877,1878],{"class":510},"  return",[504,1880,1849],{"class":514},[504,1882,1883,1886,1888,1890,1893,1895],{"class":506,"line":618},[504,1884,1885],{"class":575},"    name",[504,1887,579],{"class":514},[504,1889,528],{"class":514},[504,1891,1892],{"class":531},"withFakeMailer",[504,1894,534],{"class":514},[504,1896,585],{"class":514},[504,1898,1899,1902],{"class":506,"line":623},[504,1900,1901],{"class":518},"    sent",[504,1903,585],{"class":514},[504,1905,1906,1909,1911,1914,1918,1920,1923],{"class":506,"line":664},[504,1907,1908],{"class":562},"    beforeInitialize",[504,1910,579],{"class":514},[504,1912,1913],{"class":514}," (",[504,1915,1917],{"class":1916},"sHdIc","app",[504,1919,613],{"class":514},[504,1921,1922],{"class":549}," =>",[504,1924,1849],{"class":514},[504,1926,1927,1930,1932,1935,1937,1940,1942,1945,1947],{"class":506,"line":672},[504,1928,1929],{"class":518},"      app",[504,1931,632],{"class":514},[504,1933,1934],{"class":518},"container",[504,1936,632],{"class":514},[504,1938,1939],{"class":562},"bindMock",[504,1941,566],{"class":575},[504,1943,1944],{"class":518},"Mailer",[504,1946,716],{"class":514},[504,1948,1849],{"class":514},[504,1950,1951,1954,1956,1959,1961,1964,1966,1968,1970,1972,1975,1977,1979,1982,1984,1986,1988],{"class":506,"line":691},[504,1952,1953],{"class":562},"        send",[504,1955,579],{"class":514},[504,1957,1958],{"class":549}," async",[504,1960,1913],{"class":514},[504,1962,1963],{"class":1916},"mail",[504,1965,579],{"class":514},[504,1967,1841],{"class":1828},[504,1969,613],{"class":514},[504,1971,1922],{"class":549},[504,1973,1974],{"class":514}," void",[504,1976,1836],{"class":518},[504,1978,632],{"class":514},[504,1980,1981],{"class":562},"push",[504,1983,566],{"class":575},[504,1985,1963],{"class":518},[504,1987,613],{"class":575},[504,1989,585],{"class":514},[504,1991,1992,1995,1997],{"class":506,"line":740},[504,1993,1994],{"class":514},"      }",[504,1996,613],{"class":575},[504,1998,537],{"class":514},[504,2000,2001],{"class":506,"line":756},[504,2002,2003],{"class":514},"    },\n",[504,2005,2006],{"class":506,"line":763},[504,2007,2008],{"class":514},"  };\n",[504,2010,2011],{"class":506,"line":780},[504,2012,2013],{"class":514},"}\n",[452,2015,2016,2017,2019,2020,477,2023,632],{},"An extension can support ",[456,2018,488],{}," too: call ",[456,2021,2022],{},"testApp.useImpersonator((request, user) => ...)",[456,2024,2025],{},"ready()",[2027,2028,2029],"style",{},"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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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}",{"title":500,"searchDepth":507,"depth":540,"links":2031},[2032,2033,2034,2035,2036,2037],{"id":471,"depth":540,"text":73},{"id":864,"depth":540,"text":101},{"id":1084,"depth":540,"text":136},{"id":1303,"depth":540,"text":117},{"id":1591,"depth":540,"text":215},{"id":1754,"depth":540,"text":1755},"Authenticated requests, recorded spans, a clock the test controls, an in-memory queue and storage","md",null,{},{"title":171,"description":2038},"ZcBQhRVMk2yfMvs9okAY05cAYqNyCSxVi7KAVBJLSxA",[2045,2047],{"title":167,"path":168,"stem":169,"description":2046,"children":-1},"Replace services with fakes, stub only what a test needs, test services without HTTP, and assert on logs",{"title":109,"path":175,"stem":176,"description":2048,"children":-1},"Reference for createTestApp, TestApp, the request builder, responses, createTestContainer, stub and TestLogger",1794396677540]