[{"data":1,"prerenderedAt":646},["ShallowReactive",2],{"navigation":3,"\u002Freference\u002Ftesting":189,"\u002Freference\u002Ftesting-surround":641},[4,34,60,116,137,153],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":33},"Getting Started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,18,23,28],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-house",{"title":5,"path":16,"stem":17,"icon":6},"\u002Fgetting-started\u002Fgetting-started","1.getting-started\u002F2.getting-started",{"title":19,"path":20,"stem":21,"icon":22},"Choosing a Preset","\u002Fgetting-started\u002Fchoosing-a-preset","1.getting-started\u002F3.choosing-a-preset","i-lucide-git-branch",{"title":24,"path":25,"stem":26,"icon":27},"Deployment","\u002Fgetting-started\u002Fdeployment","1.getting-started\u002F4.deployment","i-lucide-cloud",{"title":29,"path":30,"stem":31,"icon":32},"Background Maintenance","\u002Fgetting-started\u002Fbackground-maintenance","1.getting-started\u002F5.background-maintenance","i-lucide-timer-reset",false,{"title":35,"icon":36,"path":37,"stem":38,"children":39,"page":33},"Build","i-lucide-wrench","\u002Fbuild","2.build",[40,45,50,55],{"title":41,"path":42,"stem":43,"icon":44},"Routers & Prefixes","\u002Fbuild\u002Frouters-and-prefixes","2.build\u002F1.routers-and-prefixes","i-lucide-route",{"title":46,"path":47,"stem":48,"icon":49},"Configuration","\u002Fbuild\u002Fconfiguration","2.build\u002F2.configuration","i-lucide-settings",{"title":51,"path":52,"stem":53,"icon":54},"Authorization Dependencies","\u002Fbuild\u002Fauthorization-dependencies","2.build\u002F3.authorization-dependencies","i-lucide-shield-check",{"title":56,"path":57,"stem":58,"icon":59},"Command Line","\u002Fbuild\u002Fcli","2.build\u002F4.cli","i-lucide-terminal",{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":33},"Auth","i-lucide-lock","\u002Fauth","3.auth",[66,71,76,81,86,91,96,101,106,111],{"title":67,"path":68,"stem":69,"icon":70},"OAuth & Social Login","\u002Fauth\u002Foauth-and-social-login","3.auth\u002F1.oauth-and-social-login","i-lucide-log-in",{"title":72,"path":73,"stem":74,"icon":75},"Multi-Frontend Support","\u002Fauth\u002Fmulti-frontend","3.auth\u002F10.multi-frontend","i-lucide-layout-grid",{"title":77,"path":78,"stem":79,"icon":80},"Sessions & Audit","\u002Fauth\u002Fsessions-and-audit","3.auth\u002F2.sessions-and-audit","i-lucide-monitor-smartphone",{"title":82,"path":83,"stem":84,"icon":85},"Passwordless & Messaging","\u002Fauth\u002Fpasswordless-and-messaging","3.auth\u002F3.passwordless-and-messaging","i-lucide-mail",{"title":87,"path":88,"stem":89,"icon":90},"JWT Tokens","\u002Fauth\u002Fjwt-tokens","3.auth\u002F4.jwt-tokens","i-lucide-key-round",{"title":92,"path":93,"stem":94,"icon":95},"User Management API","\u002Fauth\u002Fuser-management-api","3.auth\u002F5.user-management-api","i-lucide-users",{"title":97,"path":98,"stem":99,"icon":100},"User Invitations","\u002Fauth\u002Fuser-invitations","3.auth\u002F6.user-invitations","i-lucide-send",{"title":102,"path":103,"stem":104,"icon":105},"Roles & Permissions","\u002Fauth\u002Froles-and-permissions","3.auth\u002F7.roles-and-permissions","i-lucide-shield",{"title":107,"path":108,"stem":109,"icon":110},"ABAC","\u002Fauth\u002Fabac","3.auth\u002F8.abac","i-lucide-filter",{"title":112,"path":113,"stem":114,"icon":115},"User Status","\u002Fauth\u002Fuser-status","3.auth\u002F9.user-status","i-lucide-user-cog",{"title":117,"icon":118,"path":119,"stem":120,"children":121,"page":33},"Enterprise","i-lucide-building-2","\u002Fenterprise","4.enterprise",[122,127,132],{"title":123,"path":124,"stem":125,"icon":126},"Core Authorization Concepts","\u002Fenterprise\u002Fcore-authorization-concepts","4.enterprise\u002F1.core-authorization-concepts","i-lucide-network",{"title":128,"path":129,"stem":130,"icon":131},"Entities","\u002Fenterprise\u002Fentities","4.enterprise\u002F2.entities","i-lucide-folder-tree",{"title":133,"path":134,"stem":135,"icon":136},"Entity Memberships","\u002Fenterprise\u002Fentity-memberships","4.enterprise\u002F3.entity-memberships","i-lucide-user-plus",{"title":138,"icon":139,"path":140,"stem":141,"children":142,"page":33},"Integrations","i-lucide-plug","\u002Fintegrations","5.integrations",[143,148],{"title":144,"path":145,"stem":146,"icon":147},"API Keys","\u002Fintegrations\u002Fapi-keys","5.integrations\u002F1.api-keys","i-lucide-key",{"title":149,"path":150,"stem":151,"icon":152},"OutlabsAuth UI","\u002Fintegrations\u002Foutlabsauth-ui","5.integrations\u002F2.outlabsauth-ui","i-lucide-layout-dashboard",{"title":154,"icon":155,"path":156,"stem":157,"children":158,"page":33},"Reference","i-lucide-book-marked","\u002Freference","6.reference",[159,164,169,174,179,184],{"title":160,"path":161,"stem":162,"icon":163},"Data Models","\u002Freference\u002Fdata-models","6.reference\u002F1.data-models","i-lucide-database",{"title":165,"path":166,"stem":167,"icon":168},"Activity Tracking","\u002Freference\u002Factivity-tracking","6.reference\u002F2.activity-tracking","i-lucide-activity",{"title":170,"path":171,"stem":172,"icon":173},"Testing","\u002Freference\u002Ftesting","6.reference\u002F3.testing","i-lucide-flask-conical",{"title":175,"path":176,"stem":177,"icon":178},"Observability","\u002Freference\u002Fobservability","6.reference\u002F4.observability","i-lucide-eye",{"title":180,"path":181,"stem":182,"icon":183},"Metrics Reference","\u002Freference\u002Fmetrics-reference","6.reference\u002F5.metrics-reference","i-lucide-chart-bar",{"title":185,"path":186,"stem":187,"icon":188},"Log Events Reference","\u002Freference\u002Flog-events-reference","6.reference\u002F6.log-events-reference","i-lucide-scroll-text",{"id":190,"title":170,"body":191,"description":634,"extension":635,"links":636,"meta":637,"navigation":638,"path":171,"seo":639,"stem":172,"__hash__":640},"docs\u002F6.reference\u002F3.testing.md",{"type":192,"value":193,"toc":626},"minimark",[194,212,215,220,357,368,370,374,382,398,400,404,407,437,444,452,454,458,525,531,533,537,590,592,596,622],[195,196,197],"note",{},[198,199,200,201,211],"p",{},"— run and extend tests for the library and host apps. Deeper fixture\u002FCI notes: ",[202,203,207],"a",{"href":204,"rel":205},"https:\u002F\u002Fgithub.com\u002Foutlabsio\u002FoutlabsAuth\u002Fblob\u002Fmain\u002Ftests\u002FREADME.md",[206],"nofollow",[208,209,210],"code",{},"tests\u002FREADME.md",".",[213,214],"hr",{},[216,217,219],"h2",{"id":218},"quick-start","Quick start",[221,222,228],"pre",{"className":223,"code":224,"filename":225,"language":226,"meta":227,"style":227},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Full local suite (Postgres required; Redis is optional in this mode)\nuv run pytest\n\n# Mandatory real-Redis contract: counter, quota, cache and invalidation tests\nTEST_REDIS_URL=redis:\u002F\u002Flocalhost:56379\u002F15 \\\nTEST_REDIS_REQUIRED=1 \\\nuv run pytest -m redis\n\n# Focused\nuv run pytest tests\u002Funit\u002F\nuv run pytest tests\u002Fintegration\u002F\nuv run pytest tests\u002Funit\u002Fservices\u002Ftest_permission_scope.py -v\n","Terminal","bash","",[208,229,230,239,253,260,266,283,289,307,312,318,330,342],{"__ignoreMap":227},[231,232,235],"span",{"class":233,"line":234},"line",1,[231,236,238],{"class":237},"sHwdD","# Full local suite (Postgres required; Redis is optional in this mode)\n",[231,240,242,246,250],{"class":233,"line":241},2,[231,243,245],{"class":244},"sBMFI","uv",[231,247,249],{"class":248},"sfazB"," run",[231,251,252],{"class":248}," pytest\n",[231,254,256],{"class":233,"line":255},3,[231,257,259],{"emptyLinePlaceholder":258},true,"\n",[231,261,263],{"class":233,"line":262},4,[231,264,265],{"class":237},"# Mandatory real-Redis contract: counter, quota, cache and invalidation tests\n",[231,267,269,273,277,280],{"class":233,"line":268},5,[231,270,272],{"class":271},"sTEyZ","TEST_REDIS_URL",[231,274,276],{"class":275},"sMK4o","=",[231,278,279],{"class":248},"redis:\u002F\u002Flocalhost:56379\u002F15",[231,281,282],{"class":244}," \\\n",[231,284,286],{"class":233,"line":285},6,[231,287,288],{"class":271},"TEST_REDIS_REQUIRED=1 \\\n",[231,290,292,295,298,301,304],{"class":233,"line":291},7,[231,293,294],{"class":271},"uv ",[231,296,297],{"class":248},"run",[231,299,300],{"class":248}," pytest",[231,302,303],{"class":248}," -m",[231,305,306],{"class":248}," redis\n",[231,308,310],{"class":233,"line":309},8,[231,311,259],{"emptyLinePlaceholder":258},[231,313,315],{"class":233,"line":314},9,[231,316,317],{"class":237},"# Focused\n",[231,319,321,323,325,327],{"class":233,"line":320},10,[231,322,245],{"class":244},[231,324,249],{"class":248},[231,326,300],{"class":248},[231,328,329],{"class":248}," tests\u002Funit\u002F\n",[231,331,333,335,337,339],{"class":233,"line":332},11,[231,334,245],{"class":244},[231,336,249],{"class":248},[231,338,300],{"class":248},[231,340,341],{"class":248}," tests\u002Fintegration\u002F\n",[231,343,345,347,349,351,354],{"class":233,"line":344},12,[231,346,245],{"class":244},[231,348,249],{"class":248},[231,350,300],{"class":248},[231,352,353],{"class":248}," tests\u002Funit\u002Fservices\u002Ftest_permission_scope.py",[231,355,356],{"class":248}," -v\n",[198,358,359,360,363,364,367],{},"Prefer ",[208,361,362],{},"uv run pytest"," so the interpreter matches ",[208,365,366],{},"pyproject.toml"," (Python 3.12+).",[213,369],{},[216,371,373],{"id":372},"layout","Layout",[221,375,380],{"className":376,"code":378,"language":379},[377],"language-text","tests\u002F\n├── README.md          # Detailed fixtures, markers, troubleshooting\n├── conftest.py        # Shared engine \u002F session \u002F auth fixtures\n├── unit\u002F              # Narrow scope (often still hits Postgres)\n└── integration\u002F       # Router + multi-service flows against a real DB\n","text",[208,381,378],{"__ignoreMap":227},[198,383,384,385,389,390,393,394,397],{},"“Unit” here means ",[386,387,388],"strong",{},"narrow",", not necessarily mock-everything. Tests using the\nreal-Redis fixtures are automatically marked ",[208,391,392],{},"redis",". They may skip in an ordinary\nlocal run, but ",[208,395,396],{},"TEST_REDIS_REQUIRED=1"," converts an unavailable Redis into a failure.\nRelease CI runs that fail-closed selection independently and again as part of the\ncomplete PostgreSQL + Redis suite.",[213,399],{},[216,401,403],{"id":402},"what-hosts-should-test","What hosts should test",[198,405,406],{},"When embedding OutlabsAuth, prefer:",[408,409,410,421,427],"ol",{},[411,412,413,416,417,420],"li",{},[386,414,415],{},"Smoke"," — migrate\u002Fbootstrap, login, one ",[208,418,419],{},"require_permission"," route",[411,422,423,426],{},[386,424,425],{},"Your mounts"," — prefixes and feature flags you actually enable",[411,428,429,432,433,436],{},[386,430,431],{},"Example parity"," — run or copy patterns from\n",[208,434,435],{},"examples\u002F*\u002Fapi_integration_check.py"," \u002F smoke scripts when relevant",[198,438,439,440,443],{},"Do ",[386,441,442],{},"not"," re-test the whole library suite inside every host CI — depend on\npublished versions and keep host tests on product routes.",[198,445,446,447,211],{},"Library release gates also run an Enterprise API integration suite; see\n",[202,448,449],{"href":171},[208,450,451],{},"docs\u002FPRIVATE_RELEASE.md",[213,453],{},[216,455,457],{"id":456},"writing-library-tests-contributors","Writing library tests (contributors)",[459,460,461,474],"table",{},[462,463,464],"thead",{},[465,466,467,471],"tr",{},[468,469,470],"th",{},"Prefer",[468,472,473],{},"Avoid",[475,476,477,493,501,517],"tbody",{},[465,478,479,490],{},[480,481,482,483,486,487],"td",{},"Existing fixtures in ",[208,484,485],{},"conftest.py"," \u002F ",[208,488,489],{},"tests\u002Ffixtures\u002F",[480,491,492],{},"One-off engines per file",[465,494,495,498],{},[480,496,497],{},"Asserting public router\u002Fservice contracts",[480,499,500],{},"Scraping private helpers unless testing that helper",[465,502,503,514],{},[480,504,505,506,509,510,513],{},"Markers (",[208,507,508],{},"@pytest.mark.unit",", ",[208,511,512],{},".integration",")",[480,515,516],{},"Undocumented skips",[465,518,519,522],{},[480,520,521],{},"Real Redis for counter expiry, concurrency and recovery",[480,523,524],{},"Redis-shaped mocks for server semantics",[198,526,527,528,211],{},"Examples of focused suites: OAuth callbacks, session inventory, API key\nlifecycle, query-count budgets under ",[208,529,530],{},"tests\u002Fintegration\u002F",[213,532],{},[216,534,536],{"id":535},"common-issues","Common issues",[459,538,539,549],{},[462,540,541],{},[465,542,543,546],{},[468,544,545],{},"Symptom",[468,547,548],{},"Likely fix",[475,550,551,566,582],{},[465,552,553,556],{},[480,554,555],{},"Connection errors to Postgres",[480,557,558,559,562,563],{},"Start local Postgres; check ",[208,560,561],{},"DATABASE_URL"," \u002F test defaults in ",[208,564,565],{},"conftest",[465,567,568,571],{},[480,569,570],{},"Cache \u002F rate-limit tests skipped",[480,572,573,574,509,576,578,579],{},"Set ",[208,575,272],{},[208,577,396],{},", then run ",[208,580,581],{},"pytest -m redis",[465,583,584,587],{},[480,585,586],{},"Flaky timing around activity sync",[480,588,589],{},"Prefer unit tests that mock Redis for ActivityTracker",[213,591],{},[216,593,595],{"id":594},"related","Related",[597,598,599,606,610,614],"ul",{},[411,600,601],{},[202,602,604],{"href":204,"rel":603},[206],[208,605,210],{},[411,607,608],{},[202,609,46],{"href":47},[411,611,612],{},[202,613,175],{"href":176},[411,615,616,617],{},"Maintainer testing strategy: ",[202,618,619],{"href":171},[208,620,621],{},"docs\u002FTESTING_GUIDE.md",[623,624,625],"style",{},"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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}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);}",{"title":227,"searchDepth":234,"depth":241,"links":627},[628,629,630,631,632,633],{"id":218,"depth":241,"text":219},{"id":372,"depth":241,"text":373},{"id":402,"depth":241,"text":403},{"id":456,"depth":241,"text":457},{"id":535,"depth":241,"text":536},{"id":594,"depth":241,"text":595},"Run and extend library and host tests.","md",null,{},{"icon":173},{"title":170,"description":634},"3vQrHkigGJEk4oJ_LBEKsSAVKnvhYnGwwW0PevqgJw0",[642,644],{"title":165,"path":166,"stem":167,"description":643,"icon":168,"children":-1},"DAU \u002F MAU style engagement counters.",{"title":175,"path":176,"stem":177,"description":645,"icon":178,"children":-1},"Metrics and logs without taking over your FastAPI app.",1787472745731]